Full Code of pqina/filepond for AI

master 990dca310c45 cached
298 files
1.3 MB
297.5k tokens
148 symbols
1 requests
Download .txt
Showing preview only (1,369K chars total). Download the full file or copy to clipboard to get everything.
Repository: pqina/filepond
Branch: master
Commit: 990dca310c45
Files: 298
Total size: 1.3 MB

Directory structure:
gitextract_hbxyat0y/

├── .babelrc
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.yml
│       ├── config.yml
│       └── feature_request.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── banner-cli.js
├── banner.js
├── dist/
│   ├── filepond.css
│   ├── filepond.esm.js
│   └── filepond.js
├── index.html
├── jest.config.js
├── jest.stubs.js
├── locale/
│   ├── am-et.js
│   ├── ar-ar.js
│   ├── az-az.js
│   ├── ca-ca.js
│   ├── cs-cz.js
│   ├── cy-cy.js
│   ├── da-dk.js
│   ├── de-de.js
│   ├── el-el.js
│   ├── en-en.js
│   ├── es-es.js
│   ├── et-ee.js
│   ├── fa_ir.js
│   ├── fi-fi.js
│   ├── fr-fr.js
│   ├── he-he.js
│   ├── hr-hr.js
│   ├── hu-hu.js
│   ├── id-id.js
│   ├── it-it.js
│   ├── ja-ja.js
│   ├── km-km.js
│   ├── ko-kr.js
│   ├── ku-ckb.js
│   ├── kur-ckb.js
│   ├── lt-lt.js
│   ├── lus-lus.js
│   ├── lv-lv.js
│   ├── nl-nl.js
│   ├── no_nb.js
│   ├── pl-pl.js
│   ├── pt-br.js
│   ├── pt-pt.js
│   ├── ro-ro.js
│   ├── ru-ru.js
│   ├── sk-sk.js
│   ├── sl-si.js
│   ├── sr-rs.js
│   ├── sv_se.js
│   ├── tr-tr.js
│   ├── uk-ua.js
│   ├── ur-ur.js
│   ├── vi-vi.js
│   ├── zh-cn.js
│   ├── zh-hk.js
│   └── zh-tw.js
├── package.json
├── rollup.config.js
├── rollup.scripts.js
├── src/
│   ├── css/
│   │   ├── assistant.css
│   │   ├── browser.css
│   │   ├── data.css
│   │   ├── drip.css
│   │   ├── drop-label.css
│   │   ├── file-action-button.css
│   │   ├── file-info.css
│   │   ├── file-status.css
│   │   ├── file-wrapper.css
│   │   ├── file.css
│   │   ├── hopper.css
│   │   ├── item-order.css
│   │   ├── item.css
│   │   ├── list-scroller.css
│   │   ├── list.css
│   │   ├── modifiers.css
│   │   ├── panel-root.css
│   │   ├── panel.css
│   │   ├── progress-indicator.css
│   │   ├── root-order.css
│   │   ├── root.css
│   │   └── styles.css
│   └── js/
│       ├── __tests__/
│       │   ├── addFile.test.js
│       │   ├── callbacks.test.js
│       │   ├── contentDisposition.test.js
│       │   ├── createInstance.test.js
│       │   ├── removeFile.test.js
│       │   ├── revertUploadOnRemove.test.js
│       │   ├── server.test.js
│       │   ├── setFiles.test.js
│       │   └── windowMatchMedia.mock
│       ├── app/
│       │   ├── actions.js
│       │   ├── enum/
│       │   │   ├── ChunkStatus.js
│       │   │   ├── FileOrigin.js
│       │   │   ├── InteractionMethod.js
│       │   │   ├── ItemStatus.js
│       │   │   ├── Key.js
│       │   │   ├── Status.js
│       │   │   └── Type.js
│       │   ├── frame/
│       │   │   ├── createPainter.js
│       │   │   ├── createRoute.js
│       │   │   ├── createStore.js
│       │   │   ├── createView.js
│       │   │   ├── index.js
│       │   │   ├── mixins/
│       │   │   │   ├── animations.js
│       │   │   │   ├── apis.js
│       │   │   │   ├── index.js
│       │   │   │   ├── listeners.js
│       │   │   │   ├── styles.js
│       │   │   │   └── utils/
│       │   │   │       └── addGetSet.js
│       │   │   └── utils/
│       │   │       ├── AxisEnum.js
│       │   │       ├── addEvent.js
│       │   │       ├── animators/
│       │   │       │   ├── easing.js
│       │   │       │   ├── spring.js
│       │   │       │   └── tween.js
│       │   │       ├── appendChild.js
│       │   │       ├── appendChildView.js
│       │   │       ├── createAnimator.js
│       │   │       ├── createElement.js
│       │   │       ├── getChildCount.js
│       │   │       ├── getViewRect.js
│       │   │       ├── removeChildView.js
│       │   │       ├── removeEvent.js
│       │   │       └── updateRect.js
│       │   ├── index.js
│       │   ├── options.js
│       │   ├── queries.js
│       │   ├── utils/
│       │   │   ├── buildURL.js
│       │   │   ├── convertTo.js
│       │   │   ├── createDragHelper.js
│       │   │   ├── createFetchFunction.js
│       │   │   ├── createFileLoader.js
│       │   │   ├── createFileProcessor.js
│       │   │   ├── createFileProcessorFunction.js
│       │   │   ├── createFileStub.js
│       │   │   ├── createHopper.js
│       │   │   ├── createInitialState.js
│       │   │   ├── createItem.js
│       │   │   ├── createItemAPI.js
│       │   │   ├── createOption.js
│       │   │   ├── createOptionAPI.js
│       │   │   ├── createOptionActions.js
│       │   │   ├── createOptionQueries.js
│       │   │   ├── createOptions.js
│       │   │   ├── createPaster.js
│       │   │   ├── createPerceivedPerformanceUpdater.js
│       │   │   ├── createProcessorFunction.js
│       │   │   ├── createRevertFunction.js
│       │   │   ├── createServerAPI.js
│       │   │   ├── dnd.js
│       │   │   ├── dropAreaDimensions.js
│       │   │   ├── dynamicLabel.js
│       │   │   ├── fetchBlob.js
│       │   │   ├── getActiveItems.js
│       │   │   ├── getItemById.js
│       │   │   ├── getItemByQuery.js
│       │   │   ├── getItemIndexByPosition.js
│       │   │   ├── getItemIndexByQuery.js
│       │   │   ├── getItemsPerRow.js
│       │   │   ├── getType.js
│       │   │   ├── getValueByType.js
│       │   │   ├── hasRoomForItem.js
│       │   │   ├── insertItem.js
│       │   │   ├── isAPI.js
│       │   │   ├── mergeOptionObject.js
│       │   │   ├── on.js
│       │   │   ├── processFileChunked.js
│       │   │   ├── removeReleasedItems.js
│       │   │   ├── requestDataTransferItems.js
│       │   │   └── toServerAPI.js
│       │   └── view/
│       │       ├── assistant.js
│       │       ├── blob.js
│       │       ├── browser.js
│       │       ├── data.js
│       │       ├── drip.js
│       │       ├── dropLabel.js
│       │       ├── file.js
│       │       ├── fileActionButton.js
│       │       ├── fileInfo.js
│       │       ├── fileStatus.js
│       │       ├── fileWrapper.js
│       │       ├── item.js
│       │       ├── list.js
│       │       ├── listScroller.js
│       │       ├── panel.js
│       │       ├── progressIndicator.js
│       │       └── root.js
│       ├── createApp.js
│       ├── createAppAPI.js
│       ├── createAppAtElement.js
│       ├── createAppObject.js
│       ├── createAppPlugin.js
│       ├── filter.js
│       ├── index.js
│       └── utils/
│           ├── arrayInsert.js
│           ├── arrayRemove.js
│           ├── arrayReverse.js
│           ├── attr.js
│           ├── attrToggle.js
│           ├── canUpdateFileInput.js
│           ├── capitalizeFirstLetter.js
│           ├── composeObject.js
│           ├── copyFile.js
│           ├── copyObjectPropertiesToObject.js
│           ├── createBlob.js
│           ├── createDefaultResponse.js
│           ├── createElement.js
│           ├── createObject.js
│           ├── createResponse.js
│           ├── createWorker.js
│           ├── debounce.js
│           ├── deepCloneObject.js
│           ├── defineProperty.js
│           ├── describeArc.js
│           ├── forEachDelayed.js
│           ├── forin.js
│           ├── formatFilename.js
│           ├── fromCamels.js
│           ├── getAttributesAsObject.js
│           ├── getBase64DataFromBase64DataURI.js
│           ├── getBlobBuilder.js
│           ├── getBlobFromBase64DataURI.js
│           ├── getBlobFromByteStringWithMimeType.js
│           ├── getByteStringFromBase64DataURI.js
│           ├── getDateString.js
│           ├── getDecimalSeparator.js
│           ├── getDomainFromURL.js
│           ├── getExtensionFromFilename.js
│           ├── getFileFromBase64DataURI.js
│           ├── getFileFromBlob.js
│           ├── getFileInfoFromHeaders.js
│           ├── getFilenameFromURL.js
│           ├── getFilenameWithoutExtension.js
│           ├── getMimeTypeFromBase64DataURI.js
│           ├── getNonNumeric.js
│           ├── getNumericAspectRatioFromString.js
│           ├── getParameters.js
│           ├── getRandomNumber.js
│           ├── getRootNode.js
│           ├── getThousandsSeparator.js
│           ├── getUniqueId.js
│           ├── guesstimateExtension.js
│           ├── guesstimateMimeType.js
│           ├── hasQueryString.js
│           ├── insertAfter.js
│           ├── insertBefore.js
│           ├── isArray.js
│           ├── isBase64DataURI.js
│           ├── isBoolean.js
│           ├── isBrowser.js
│           ├── isDefined.js
│           ├── isEmpty.js
│           ├── isExternalURL.js
│           ├── isFile.js
│           ├── isFunction.js
│           ├── isIOS.js
│           ├── isInt.js
│           ├── isNode.js
│           ├── isNull.js
│           ├── isNumber.js
│           ├── isObject.js
│           ├── isString.js
│           ├── leftPad.js
│           ├── limit.js
│           ├── loadImage.js
│           ├── lowerCaseFirstLetter.js
│           ├── percentageArc.js
│           ├── polarToCartesian.js
│           ├── renameFile.js
│           ├── replaceInString.js
│           ├── resetFileInput.js
│           ├── sendRequest.js
│           ├── setInputFiles.js
│           ├── text.js
│           ├── toArray.js
│           ├── toBoolean.js
│           ├── toBytes.js
│           ├── toCamels.js
│           ├── toFloat.js
│           ├── toFunctionReference.js
│           ├── toInt.js
│           ├── toNaturalFileSize.js
│           ├── toNumber.js
│           ├── toPercentage.js
│           ├── toString.js
│           └── trim.js
└── types/
    ├── index.d.ts
    └── tsconfig.json

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

================================================
FILE: .babelrc
================================================
{
    "retainLines": true,
    "presets": [
        ["@babel/preset-env", {
            "exclude": ["transform-typeof-symbol"],
            "modules": false
        }]
    ],
    "plugins": [
        ["@babel/plugin-proposal-object-rest-spread", {
            "loose": true, 
            "useBuiltIns": true
        }],
        ["@babel/plugin-transform-template-literals", {
            "loose": true
        }]
    ],
    "env": {
        "test": {
            "presets": [
                ["@babel/preset-env", {
                    "targets": {
                        "node": "current"
                    }
                }]
            ]
        }
    }
}

================================================
FILE: .gitattributes
================================================
dist/* linguist-vendored=false


================================================
FILE: .github/FUNDING.yml
================================================
custom: ['https://www.buymeacoffee.com/rikschennink']


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug report
title: "[Bug] "
description: Report an issue with FilePond
labels: bug
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!
  - type: checkboxes
    attributes:
      label: Is there an existing issue for this?
      description: Please search to see if an issue already exists for the bug you encountered.
      options:
      - label: I have searched the existing issues
        required: true
  - type: checkboxes
    attributes:
      label: Have you updated FilePond and its plugins?
      description: Please update both FilePond and its plugins to the most recent version.
      options:
      - label: I have updated FilePond and its plugins
        required: true
  - type: textarea
    attributes:
      label: Describe the bug
      description: A concise description of what the bug is.
      placeholder: Bug description
    validations:
      required: true
  - type: textarea
    attributes:
      label: Reproduction
      description: Provide clear steps to reproduce the bug. Please take the time to create a reproduction of the bug by [forking this project on codesandbox.io](https://codesandbox.io/s/filepond-plain-javascript-24i1m)
      placeholder: Reproduction
    validations:
      required: true
  - type: textarea
    attributes:
      label: Environment
      description: |
        examples:
          - **Device**: Samsung Galaxy s20, iPhone 12 Mini, Macbook Pro (2020), etc.
          - **OS**: Android 8, iOS 12, Windows 10, etc.
          - **Browser**: Firefox 93, Chrome 94, etc.
      value: |
          - Device:
          - OS:
          - Browser:
      render: markdown
    validations:
      required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Stack Overflow
    url: https://stackoverflow.com/questions/tagged/filepond
    about: Ask questions on Stack Overflow and tag with "filepond"


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature request
title: "[Feature] "
description: Suggest an idea for this project
labels: new feature
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this feature request!
  - type: checkboxes
    attributes:
      label: Is there an existing issue for this?
      description: Please search to see if an issue already exists for the feature request you want to submit.
      options:
      - label: I have searched the existing issues
        required: true
  - type: textarea
    attributes:
      label: Is your feature request related to a problem? Please describe.
      description: A clear and concise description of what the problem is you want to solve.
      placeholder: I'm always frustrated when...
    validations:
      required: true
  - type: textarea
    attributes:
      label: Describe the solution you'd like
      description: A clear and concise description of what you want to happen.
    validations:
      required: true
  - type: textarea
    attributes:
      label: Describe alternatives you've considered
      description: A clear and concise description of any alternative solutions or features you've considered.
    validations:
      required: true


================================================
FILE: .gitignore
================================================
npm-debug.log
node_modules/
coverage/
.idea
.vscode/
upload/
test*.html
_TODO.md
_RELEASE.md

================================================
FILE: .nvmrc
================================================
v10.15.3

================================================
FILE: .prettierignore
================================================
dist/*

================================================
FILE: .prettierrc
================================================
{
    "svelteSortOrder": "scripts-markup-styles",
    "trailingComma": "es5",
    "tabWidth": 4,
    "printWidth": 100,
    "singleQuote": true,
    "semi": true
}


================================================
FILE: CHANGELOG.md
================================================
# Changelog

## 4.32.12

-   Link attribution banner to [filepond.com](https://filepond.com)

## 4.32.11

-   Merge fix file processing aborted and moving to next file. #1065

## 4.32.10

-   Added various new locales.
-   Merge fix for drop offset mismatch when scrolling.

## 4.32.9

-   Added various new locales.

## 4.32.8

-   Merged multiple PRs with various fixes.

## 4.32.7

-   Fix issue where `checkValidity` would not reset field validity status after removing an invalid file.

## 4.32.6

-   Fix `processfiles` event missing from types.`

## 4.32.5

-   Attempt to fix item dragging issue on Android.

## 4.32.4

-   Make credits link more visible to prevent authors from leaving it in place accidentally.

## 4.32.3

-   Add nofollow to credits link.

## 4.32.2

-   Fix accessibility issue with data fieldset missing a legend.

## 4.32.1

-   Fix types.

## 4.32.0

-   Add `onload` property to chunk patch request, receives `xhr` object, chunk index, and total chunks. #649
-   Fix role attribute in voice over assistant to improve WCAG compatibility #988

## 4.31.4

-   Fix issue where images pasted in contenteditable elements would be handled by FilePond.

## 4.31.3

-   Fix issue where hidden input fields in `filepond--data` fieldset would stay disabled after enabling FilePond. #1001

## 4.31.2

-   Remove `aria-hidden` from drop label as suggested by Lighthouse.
-   Fix `tabIndex` attribute on credits link not set correctly, now no longer included in tab flow.

## 4.31.1

-   Fix issue with dragging file items sometimes not working.

## 4.31.0

-   Add support for editing mock files if `item.source` is url.

## 4.30.6

-   Fix issue where using a number as source for a local file would throw a `url.split` error.

## 4.30.5

-   Fix file field value assignment #905

## 4.30.4

-   Fix a bug where the selected file is not replaced #841
-   Fix a bug where the onwarning event is not triggered #839
-   Add more translations

## 4.30.3

-   Fix accidental push.

## 4.30.2

-   Remove accidental log statement.

## 4.30.1

-   Prioritize server prop before other props when passed to setOptions #606

## 4.30.0

-   Add `labelFileSizeBytes`, `labelFileSizeKilobytes`, `labelFileSizeMegabytes`, `labelFileSizeGigabytes` #763

## 4.29.1

-   Revert chunked uploads #757

## 4.29.0

-   Enable rejecting images with an error message using the internal `DID_LOAD_ITEM` hook.

## 4.28.2

-   Fix issue where local server files could not be re-uploaded after editing and did not trigger remove of source file.

## 4.28.1

-   Fix CSS error.

## 4.28.0

-   Add `storeAsFile` property, if set to `true` FilePond will attempt to store the file objects in file input elements allowing file submit along with parent form (no need for `server` property). **This only works if the browser [supports the DataTransfer constructor](https://caniuse.com/mdn-api_datatransfer_datatransfer), this is the case on Firefox, Chrome, Chromium powered browsers and Safari version 14.1 and higher.**
-   Switch to PostCSS for style output.

## 4.27.3

-   Fix issue with file.js component leaking state causing `allowRemove` to impact other instances of FilePond. #713

## 4.27.2

-   Fix issue with fetch and `HEAD` no setting server id to hidden input element.

## 4.27.1

-   Fix issue with `allowMinimumUploadDuration` set to `false` throwing error.

## 4.27.0

-   Add `allowMinimumUploadDuration` set to `false` to prevent a minimum upload time of 750ms.

## 4.26.2

-   `setMetadata` internal `silent` bool now does fire internal `DID_UPDATE_ITEM_METADATA` but doesn't trigger upload or file prepare logic. This fixes an issue with the new image editor and file poster plugins.

## 4.26.1

-   Add metdata change info to internal `SHOULD_PREPARE_OUTPUT` call

## 4.26.0

-   Fix problem with rendering 0 items per row. #676
-   The `headers` property of the `server.process` end point can now be a function.

## 4.25.3

-   Fix issue with `chunkRetryDelays`. #671

## 4.25.2

-   Fix issue with fixed with file items not row wrapping correctly. #653
-   Fix file info label when remove button positioned to the right. #620

## 4.25.1

-   Renamed `beforePrepareOutput` hook to `beforePrepareFile`.

## 4.25.0

-   Add `beforePrepareOutput` hook to intercept and prevent preparing a new output file.

## 4.24.0

-   Add action info to internal `SHOULD_PREPARE_OUTPUT` call
-   Moved edit button CSS to FilePond

## 4.23.1

-   When files are dropped in a folder the file type cannot always be determined, this fix prevents FilePond from creating a Blob if it can't guesstimate the file type.

## 4.23.0

-   Add "powered by" footer and [credits prop](https://pqina.nl/filepond/docs/patterns/api/filepond-instance/#disabling-credits).

## 4.22.1

-   Fix problem with locale files.

## 4.22.0

-   Add internal filter for plugins to manipulate dropped files before adding them to the files list.

## 4.21.1

-   Fix calculation of file size when `fileSizeBase` is set to 1024.

## 4.21.0

-   Add `fileSizeBase` use to adjust the way files sizes are displayed. Default is `1000`.

## 4.20.1

-   Add `allowRemove` option so it actually works.

## 4.20.0

-   Add `allowRemove`, set to `false` to disable remove button.
-   Improve TypeScript definitions.
-   Fix issue where `removeFiles` would not remove all files.

## 4.19.2

-   Fix problem with locale files not ending up on npm.

## 4.19.1

-   Fix issue where removal of a server file was requested when setting new files to the `files` property.

## 4.19.0

-   Add locale folder, can now import different locales.
-   Improve `supports` method, now correctly detects MacOS Safari 8.
-   Improve type definitions file.
-   Fix issue with `removeFiles({ revert: false })` not working.
-   Fix issue where content pasted in a textarea would be interpreted as a file.

## 4.18.0

-   Add fallback for `fetch` when loading remote URLs, if no custom fetch supplied, will use default request.
-   Add TypeScript dynamic label types.
-   Fix issue where order of files wasn't correct when setting initial files.

## 4.17.1

-   Fix issue where reorder event was fired on each drag interaction, now only fires when order changes.

## 4.17.0

-   Add `allowProcess`, set to `false` to remove processing button and related abort / retry processing controls.
-   Fix issue where hidden inputs didn't reflect visual order of files in list.

## 4.16.0

-   Add `allowSyncAcceptAttribute`, set to `false` to prevent FilePond from setting the file input field `accept` attribute to the value of the `acceptedFileTypes`.

## 4.15.1

-   Fix issue with abort being called even when not supplied.

## 4.15.0

-   Add support for reording items in grid layout. Thanks @jwsinner ❤︎

## 4.14.0

-   Add `oninitfile`, called when file is first initialised, can be used to immediately set metadata.

## 4.13.7

-   Fix backwards compatibility problem with `4.13.5` and `4.13.6` where removeFile would revert upload.
-   Add `{ revert: true }` as parameter to `removeFile` and `removeFiles` methods. Where in the previous two fix versions reverting was added to be done automatically this new parameter now needs be set to revert the upload.

## 4.13.6

-   Fix problem where revert wasn't called for user added files.

## 4.13.5

-   Fix trigger of revert handler to `removeFile` API.
-   Fix problem where circular layout wouldn't work on latest Safari.

## 4.13.4

-   Fix issue where FilePond internal event mechanism would be in slowmotion mode when running in an inactive tab because of `setTimeout` use.

## 4.13.3

-   Fix issue where FilePond would excessivly pause in between processing files while running in an inactive tab.

## 4.13.2

-   Fix issue where FilePond running in an inactive tab would be very slow to pick up new files.

## 4.13.1

-   Fix issue where HEAD fetch request would try turn response into zero byte file.

## 4.13.0

-   Fix issue where hidden file fields were not in the correct order when files were sorted either automatically or manually.
-   Clean up accidental log statement left in 4.12.2 release.

## 4.12.2

-   Fix issue with re-enabling FilePond field from disabled state not applying the appropriate fields to the browse input.

## 4.12.1

-   Fix issue where browse button wasn't clickable when `styleLayoutMode` was set to `compact`.

## 4.12.0

-   Add `styleButtonRemoveItemAlign` to align remove button to the left side of the file item.
-   Fix issue where list of files could not be scrolled when FilePond was disabled.

## 4.11.0

-   Add `relativePath` property to file item.
-   Add `onreorderfiles` callback.
-   Fix issue where unkown type was `"null"` instead of an empty string.
-   Fix issue where `onactivatefile` was fired on drag end.

## 4.10.0

-   Copy webkitDirectory property to file object.

## 4.9.5

-   Fix issue with error format in TypeScript types.

## 4.9.4

-   Fix problem with API querystring containing multiple questionmarks.

## 4.9.3

-   Fix problem where ending the class attribute on a space would throw an error.

## 4.9.2

-   Add `grab` cursor to items so there's and indicator that items are grabbable.

## 4.9.1

-   Fix issue where Chrome on Android would launch pull-to-refresh when trying to drag a file item.

## 4.9.0

-   Add drag to reorder file items, enable by setting `allowReorder` to `true`.
    -   Only works in single column mode (for now).
    -   It also works when the list of files is showing a scrollbar, but dragging + scrolling isn't working correctly at the moment.
    -   Limited to [browsers supporting Pointer events](https://caniuse.com/#feat=pointer).
-   Add `moveFile(query, index)` method. Use to move a file to a different index in the file items array.

## 4.8.2

-   Fix problem with 4.8.1 fix not working with SSR.

## 4.8.1

-   Fix IE issue where adding markup would not work.

## 4.8.0

-   Add `prepareFile` and `prepareFiles` methods to the FilePond instance, use to request output files of the current items in the files list.

## 4.7.4

-   UTF-8 encode request headers to prevent issues with weird characters.

## 4.7.3

-   Switch from `setAttribute` to `cssText` for layout changes resulting in better performance and CSP compatibility. [#400](https://github.com/pqina/filepond/pull/400)

## 4.7.2

-   Fix issue where iOS 10 would throw an error when calling `delete` on a dataset property
-   Fix issue with `onwarning` being called on incorrect element

## 4.7.1

-   Fix problem where directories with over 100 files weren't read correctly.

## 4.7.0

-   Add support for [chunked uploads](https://pqina.nl/filepond/docs/patterns/api/server/#process-chunks). Thanks to Ryan Olson (@ams-ryanolson) Arctic Media for donating the funds to build this.

## 4.6.1

-   Add missing Blob type to TypeScript server config.

## 4.6.0

-   Add TypeScript declarations.

## 4.5.2

-   If `Blob` has `name` attribute use name attribute instead of URL for file name.

## 4.5.1

-   Fix issue where drag-drop from Firefox download list would not add file to drop area

## 4.5.0

-   Add option to set server end point headers on a generic level so they're applied to all end points using `server.headers`.

## 4.4.13

-   Fix problem with CSS overriding image preview markup text size.

## 4.4.12

-   Fix memory leak.

## 4.4.11

-   Fix problem with abort statement in file loader logic.

## 4.4.10

-   Fix issue where Promise returned by `addFile` would not be rejected if file failed to load.

## 4.4.9

-   Fix security issues with dependencies.

## 4.4.8

-   Fix issue where multiple calls to `setMetadata` would result in multiple successive calls to prepare file.
-   Fix issue where drop area aspect ratio would not update correctly on resize.

## 4.4.7

-   Fix issue where pasting a file would throw an error.
-   Fix issue where ignored files would be counted as files when dropping a folder.

## 4.4.6

-   Fix issue where `processFiles` would re-process `local` server images.

## 4.4.5

-   Fix issue where FilePond event loop would freeze when tab was inactive.

## 4.4.4

-   Fix issue where FilePond would not render when hidden, resulting in missing input elements and events not firing.

## 4.4.3

-   Fix issue where processing the queue didn't work correctly when files were removed while being in the queue.

## 4.4.2

-   Fix issue where UTF-8 encoded filename was not parsed correctly.

## 4.4.1

-   Fix issue where `Content-Disposition` header filename was not parsed correctly.

## 4.4.0

-   Fix issue where `addFile` did not respect `itemInsertLocation` setting.
-   Add the `beforeDropFile` hook which can be used to validate a dropped item before it's added, make sure `dropValidation` is set to `true` as well.

## 4.3.9

-   Fix problem where enabling FilePond after being `disabled` would not allow browsing for files.

## 4.3.8

-   Improve accessibility of buttons by moving label from `title` to inner hidden `<span>`.

## 4.3.7

-   Attempt #2 at fixing the issue of release `4.3.6`.

## 4.3.6

-   Fix problem where the `abortAll` call triggered when destroying FilePond would inadvertently trigger the `server.remove` end point for each local file.

## 4.3.5

-   Fix issue where changing the `stylePanelAspectRatio` would not update the container size.

## 4.3.4

-   Add source code.
-   Add build scripts.
-   Fix `onremovefile` callback not receiving an error object similar to `onaddfile`.

## 4.3.3

-   Fix issue where aborting a file load while the file was being prepared (for instance, encoded) did not work.

## 4.3.2

-   Fix issue where 0 byte files would not upload to the server.

## 4.3.1

-   Add `status` property to the FilePond instance, use this property to determine the current FilePond status (`EMPTY`, `IDLE`, `ERROR`, `BUSY`, or `READY`).

## 4.3.0

-   Fix problem where `addFiles` would not correctly map passed options to files.
-   Fix problem where upload error would prevent processing of other files.
-   Fix problem where the field would not "exist" if it had no value. Now if FilePond is empty the internal file input element is given the name attribute, this is removed when a file is added (as the name is then present on the file's hidden input element).
-   Add `onprocessfiles` which is called when all files have been processed.
-   Add `onactivatefile` which is called when a user clicks or taps on a file item.

## 4.2.0

-   Add `disabled` property, can be set as an attribute on the file input or as a property in the FilePond options object.
-   Add catching clicks on the entire pond label element to make it easier to click the label.

## 4.1.4

-   Only hide preview images when resizing the window horizontally, fixes problem with resize events on iOS.

## 4.1.3

-   Improve the way that FilePond resumes drawing when a tab retains focus.

## 4.1.2

-   Fix problem where `onaddfile` callback parameters were reversed when file validation plugins prevented file load

## 4.1.1

-   Fix problem where error shake animation would mess up preview image.

## 4.1.0

-   Add `itemInsertLocationFreedom` property, set to `false` to stop user from picking the location in the file list where the file is added.

## 4.0.2

-   Fix problem with undefine `ItemStatus` object in `processFiles` method

## 4.0.1

-   Fix problem where window resize handler was removed incorrectly resulting in an error.

## 4.0.0

Multiple improvements, small fixes and new features. As updating will result in animation speed changes, changes to the way files are added to the files list, and will require an update of the image preview plugin, the version has been bumped to 4.0.0

-   Add grid layout feature. Assign a fixed width to a filepond item and FilePond will render the items in rows.

The code below will render a list view on small viewports, a 50/50 grid on medium viewports, and a 33/33/33 grid on wide viewports. The `.5em` in each calc statement is equivalent to the combined left and right margin of each filepond item.

```css
@media (min-width: 30em) {
    .filepond--item {
        width: calc(50% - 0.5em);
    }
}

@media (min-width: 50em) {
    .filepond--item {
        width: calc(33.33% - 0.5em);
    }
}
```

-   Add `styleItemPanelAspectRatio` to control the item panel aspect ratio and render item panels in a fixed size.
-   Add `sort` method on FilePond instance for sorting FilePond files.
-   Add `itemInsertLocation` property to set default insert location of files or sort method.
-   Add `itemInsertInterval` to control the small delay between adding items to the files list.
-   Improve drag and drop performance.
-   Improve file insert logic and performance.
-   Improve rendering of file previews will now scale correctly when window is resized.
-   Improve handling of dropped directories on Firefox, file type was missing, now guestimates file type based on file extension.
-   Small tweaks and changes to file animation durations and intros.
-   Fixed drag coordinates being slightly out of place.
-   Multiple small fixes and code improvements.

## 3.9.0

-   Add `checkValidity` which is set to `false` by default. If it's set to `true`, FilePond will set the contents of the `labelInvalidField` property as the field custom validity message if it contains invalid files (files that for instance exceed max file size or fail other tests).

## 3.8.2

-   Fix problem where remove server error message was passed directly to client without label. Set `labelFileRemoveError` to a string to change default error, set it to a function to show custom server error. `{ labelFileRemoveError: serverError => serverError }`

## 3.8.1

-   Expose `dispatch` call to plugin item extensions.

## 3.8.0

-   Add `forceRevert` option, set to `true` to force a revert action to finish before continuing.

## 3.7.7

-   Improve `onlistupdate` event so it can be better synced with adapter components.

## 3.7.6

-   Switched browse text underline to `text-decoration-skip-ink: auto` instead of `text-decoration-skip: ink` to prevent eslint warnings.

## 3.7.5

-   Fix problem where calling `processFiles` without arguments would re-process already processed files.

## 3.7.4

-   Fix problem where subsequent calls to `processFile` would not automatically revert an uploaded file or abort an active upload.

## 3.7.3

-   Fix problem where upload complete indicator would not show when image preview was active.

## 3.7.2

-   Expose `createItemAPI` to plugins.

## 3.7.1

-   Fix problem where URLs would immidiately be in processed state.

## 3.7.0

-   Add `maxParallelUploads` option to limit the amount of files being uploaded in parallel.
-   Add option to only fetch file head when downloading remote URLs. File is downloaded to the server and server sends a unique file id to the client. Set `server.fetch.method` to `'HEAD'` the server needs to repond with custom header `X-Content-Transfer-Id` and a unique id. See [handle_fetch_remote_file](https://github.com/pqina/filepond-server-php/blob/master/index.php#L91) in FilePond PHP Server for an example implementation.

## 3.6.0

-   Add support for uploading transform plugin variants.
-   Add `server.process.ondata` which allows adding entries to the formdata before it's sent to the server.

## 3.5.1

-   Fix problem where `processFile` and `processFiles` would reprocess already processed files.

## 3.5.0

-   Add `beforeAddFile` hook, this can be used to quickly validate files before they're being added.

## 3.4.0

-   Add `server.remove` property, this property can be optionally set to a method to call when the remove button is tapped on a `local` file. This allows removing files from the server. Please note that allowing clients to remove files from the server is a potential security risk and requires extra caution.

By default the property is `null`. The advise is to not use this method and only make changes to the server after the parent form has been submitted. The form POST will contain all the loaded file names and relevant file data, it should be enough to determine the files to remove and the files to keep.

## 3.3.3

-   Fix filename matching of content-disposition header when the filename is not wrapped in quotes.
-   Fix problem where special characters in filename prevented a file from being added

## 3.3.2

-   Fix problem where revert call would revert wrong file item.

## 3.3.1

-   Fix problem where exceeding the max file limit would not throw an error

## 3.3.0

-   Add feature to silently update metadata so it doesn't trigger an update.

## 3.2.5

-   Fix issue where items would be removed before item sub views were all in rest state.

## 3.2.4

-   Fix problem where server side rendering would not work correctly.

## 3.2.3

-   Fix problem where `beforeRemoveFile` hook was not called when in `instantUpload` mode and reverting an upload.

## 3.2.2

-   Add preparations for queueing file processing.
-   Improve guards against errors when items are removed.
-   Improve alignment of drop label.

## 3.2.1

-   Group updateitems callback for better compatibility with React.

## 3.2.0

-   Add global scoped property for painter so multiple libraries can subscribe to read and write DOM operations. This is mostly in preparation for a standalone version of the Image Editor plugin.

## 3.1.6

-   Fix problem where remove callback would no longer work.

## 3.1.5

-   Fix problem with WebWorkers not working correctly on Edge and IE.

## 3.1.4

-   Fix syntax error [#147](https://github.com/pqina/filepond/pull/147)

## 3.1.3

-   Fix additional problem with quick file removals.

## 3.1.2

-   Fix problem where remove call would throw error depending on the state of the upload.
-   Fix problem where clicking on abort before upload had started would not cancel upload.

Please note that this update will require installing new versions of the following plugins:

-   File Validate Size

## 3.1.1

-   Fix problem where panel overflow would render incorrectly.

## 3.1.0

-   Improve diffing when updating the `files` property.
-   Add `onupdatefiles` callback that is triggered when a file is added or removed to a pond instance.

## 3.0.4

-   Fix problem where feature detection would throw error on iOS 8.x

## 3.0.3

-   Fix problem with XMLHttpRequest timeout on Internet Explorer 11.
-   Fix problem with custom properties on element on Internet Explorer 11.

## 3.0.2

-   Fix problem with label not being clickable while in integrated layout mode.

## 3.0.1

-   Fix problem where timeout would incorrectly trigger for uploads.

## 3.0.0

-   Small internal flow changes to facilitate integration with the Image Editor plugin
-   Improve performance
-   Improve file loader so it now supports `blob` URLs
-   Add `stylePanelLayout` setting to set layout mode for panel
-   Add `stylePanelAspectRatio` setting to fix aspect ratio of panel
-   Add `styleButtonRemoveItemPosition` to control remove button position on image preview
-   Add `styleButtonProcessItemPosition` to control item processing position on image preview
-   Add `styleLoadIndicatorPosition` to control load indicator position on image preview
-   Add `styleProgressIndicatorPosition` to control process indicator position on image preview
-   Add method to automatically update data when file metadata is updated
-   Fix animation rest state detection

Please note that this update will require installing new versions of the following plugins:

-   File Encode
-   Image Crop
-   Image Preview
-   Image Transform

## 2.3.1

-   Fix improved browser environment detection.

## 2.3.0

-   Improve browser environment detection [#123](https://github.com/pqina/filepond/pull/123).
-   Add `beforeRemoveFile` callback to allow user confirmation before actual file removal.

## 2.2.1

-   Fix another problem where list overflow would not render correctly.

## 2.2.0

-   Fix problem where `maxFiles` was not enforced when dropping a set of files, each file was added in sequence till `maxFiles` was reached while the set as a whole should've been invalidated at once.

## 2.1.3

-   Fix problem where max-height of filepond root would not be respected by file list.

## 2.1.2

-   Cleaned up some stray babelHelpers.
-   Fix bug in render engine style method, should result in less unnecessary redraws.

## 2.1.1

-   Fix problem where the drop indicator would render at the wrong location.
-   Fix problem where calling `removeFile` directly after `processFile` was resolved would throw an error.

## 2.1.0

-   Labels can now be set as functions, these functions will receive context information, this is useful to customize both he load error and processing error labels based on server response.

## 2.0.1

-   Add additional utilities to plugin API.

## 2.0.0

-   Automatically replace undo button counterclockwise arrow icon with remove button icon when `instantUpload` is set to `true`.

## 1.8.8

-   Add `metadata` handling to `addFile` method.

## 1.8.7

-   Fix problem where setting `allowRevert` to `false` would hide the remove button.

## 1.8.6

-   Fix problem where adding dataURIs would throw an error

## 1.8.5

-   Fix casting of input attributes without value to correct boolean

## 1.8.4

-   Fix problem where response timeout would throw an error
-   Improve handling of returned value by processing onload function

## 1.8.3

-   Cleaning up some stray code

## 1.8.2

-   Fix problem where element options did not override page level options

## 1.8.1

-   Handle `Blob` with `name` property same as actual `File` object
-   Attempt to fix a problem where `elementFromPoint` could not be found in Angular component

## 1.8.0

-   Add view filter to file info view
-   Add option to [mock server files](https://pqina.nl/filepond/docs/patterns/api/filepond-object/#creating-a-filepond-instance)
-   Add option to [set initial file metadata](https://pqina.nl/filepond/docs/patterns/api/filepond-object/#creating-a-filepond-instance) for server files
-   Bugfixes

## 1.7.4

-   Fix handling of `Content-Disposition` header to better extract the filename

## 1.7.3

-   Events are now fired asynchronous, this allows internal processes to finish up

## 1.7.2

-   Fix broken links in README

## 1.7.1

-   Add view filter to file status view
-   Fix problem where attribute object value was not read correctly

## 1.7.0

-   Add `onerror` callback to server configuration to allow custom parsing of error response

## 1.6.2

-   Fix problem where restored temp file would not be removed correctly

## 1.6.1

-   Add `FileOrigin` enum to FilePond object

## 1.6.0

-   Add `fileOrigin` property to file item

## 1.5.4

-   Update README with links to new plugins and adapters

## 1.5.3

-   Accidentally skip over this version number

## 1.5.2

-   Remove max-width on file status view

## 1.5.1

-   Prevent text wrapping for file size label

## 1.5.0

-   Add `onload` method to server configuration

## 1.4.1

-   Fix progress indicator getting stuck on subsequent uploads

## 1.4.0

-   Add `allowRevert` option to disable revert button

## 1.3.0

-   Add `dropValidation` option to enable pre-validating of dropped items

## 1.2.11

-   Improve timing of CPU heavy operations (like file encoding) when a file is added to FilePond

## 1.2.10

-   Fix `removeFiles` method. Did not correctly remove files when called with empty arguments or an array of indexes

## 1.2.9

-   Fix bug where `processFiles` only worked when receiving parameters

## 1.2.8

-   Tiny improvements so can be used when server side rendering

## 1.2.7

-   Improve loading indicator state
-   Add Angular adapter reference to README

## 1.2.6

-   Add `onprocessfile` callback to options object

## 1.2.5

-   Fix id attribute not being available on FilePond root
-   Fix FilePond not rendering correctly when initially hidden
-   Improve render performance

## 1.2.4

-   Fix `setOptions` method not correctly converting value types

## 1.2.3

-   Update the `OptionTypes` property when a plugin is registered

## 1.2.2

-   Fix `setOptions` returning an incorrectly formatted options object

## 1.2.1

-   Add README link to backlog on WIP
-   Fix problem where `destroy` would not remove FilePond
-   Switch license from GPL to MIT
-   Add jQuery to adapters list
-   Fix error message animation
-   Improve FilePond event parameters

## 1.1.0

-   Accidentally skip version 1.1.0

## 1.0.8

-   Fix problem where plugins could be registered twice
-   Improve `files` property, now compares existing files against new files and updates accordingly

## 1.0.7

-   Hide center panel before scaling panel view

## 1.0.6

-   Improve style possibilities for panel views and enforce internal panel layout properties

## 1.0.5

-   Fix problem where loading indicators would not spin for certain requests
-   Various improvements to README

## 1.0.4

-   Fix render bugs
-   Improve panel view layout

## 1.0.3

-   Fix processing complete state file item color

## 1.0.2

-   Add support for client side file manipulation
-   Add support for file metadata
-   Improve performance

## 1.0.1

-   Add correct banners to library files

## 1.0.0

-   Initial release


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Pqina

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
================================================
# [<img src="https://github.com/pqina/filepond-github-assets/blob/master/logo.svg" height="44" alt="FilePond"/>](https://pqina.nl/filepond/)

A JavaScript library that can upload anything you throw at it, optimizes images for faster uploads, and offers a great, accessible, silky smooth user experience.

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/pqina/filepond/blob/master/LICENSE)
[![npm version](https://badge.fury.io/js/filepond.svg)](https://www.npmjs.com/package/filepond)
![npm](https://img.shields.io/npm/dt/filepond)
[![minzipped size](https://img.shields.io/bundlephobia/minzip/filepond)](https://bundlephobia.com/package/filepond)
[![Discord](https://img.shields.io/discord/1422126182924554291?label=discord)](https://discord.gg/KTyymsrTrX)

FilePond adapters are available for **[React](https://github.com/pqina/react-filepond)**, **[Vue](https://github.com/pqina/vue-filepond)**, **[Angular](https://github.com/pqina/ngx-filepond)**, **[Svelte](https://github.com/pqina/svelte-filepond)**, and **[jQuery](https://github.com/pqina/jquery-filepond)**

[FilePond v5 Alpha version now available for testing](https://github.com/pqina/filepond/tree/v5)

[Documentation](https://pqina.nl/filepond/docs) • [Discord](https://discord.gg/KTyymsrTrX) • [Examples](#live-demos)

---

[<img src="https://github.com/pqina/filepond-github-assets/blob/master/header.svg" alt="FilePond"/>](https://www.buymeacoffee.com/rikschennink/)

[Buy me a Coffee](https://www.buymeacoffee.com/rikschennink/) • [Use FilePond with Pintura](https://pqina.nl/pintura/?ref=github-filepond) • [Dev updates](https://rik.schenn.ink)

---

### Core Features

-   Accepts **directories**, **files**, blobs, local URLs, **remote URLs** and Data URIs.
-   **Drop files**, select on filesystem, **copy and paste files**, or add files using the API.
-   **Async uploads** with AJAX, supports **chunk uploads**, can encode files as base64 data and send along form post.
-   **Accessible**, tested with AT software like VoiceOver and JAWS, **navigable by Keyboard**.
-   **Image optimization**, automatic image resizing, **cropping**, filtering, and **fixes EXIF orientation**.
-   **Responsive**, automatically scales to available space, is functional on both **mobile and desktop devices**.

[Learn more about FilePond](https://pqina.nl/filepond/)

[<img src="https://github.com/pqina/filepond-github-assets/blob/master/filepond-animation-01.gif?raw=true" width="370" alt=""/>](https://pqina.nl/filepond/)

---

### Also need Image Editing?

**Pintura the modern JavaScript Image Editor** is what you're looking for. Pintura supports setting **crop aspect ratios**, **resizing**, **rotating**, **cropping**, and **flipping** images. Above all, it integrates beautifully with FilePond.

[Learn more about Pintura](https://pqina.nl/pintura/?ref=github-filepond)

[<img src="https://github.com/pqina/filepond-github-assets/blob/master/filepond_pintura.gif?raw=true" width="600" alt=""/>](https://pqina.nl/pintura/?ref=github-filepond)

---

### Live Demos

-   [React](https://stackblitz.com/github/pqina/pintura-example-react?file=src%2FExampleFilePond.js)
-   [Angular](https://stackblitz.com/github/pqina/pintura-example-angular?file=src%2Fapp%2Ffilepond-example%2Ffilepond-example.component.ts)
-   [Svelte](https://stackblitz.com/github/pqina/pintura-example-svelte?file=src%2FApp.svelte%3AL152)
-   [Vue](https://stackblitz.com/github/pqina/pintura-example-vue-3?file=src%2FExampleFilePond.vue)

### Plugins

-   [File encode](https://github.com/pqina/filepond-plugin-file-encode)
-   [File rename](https://github.com/pqina/filepond-plugin-file-rename)
-   [File size validation](https://github.com/pqina/filepond-plugin-file-validate-size)
-   [File type validation](https://github.com/pqina/filepond-plugin-file-validate-type)
-   [File metadata](https://github.com/pqina/filepond-plugin-file-metadata)
-   [File poster](https://github.com/pqina/filepond-plugin-file-poster)
-   [Image editor](https://github.com/pqina/filepond-plugin-image-edit)
-   [Image size validation](https://github.com/pqina/filepond-plugin-image-validate-size)
-   [Image preview](https://github.com/pqina/filepond-plugin-image-preview)
-   [Image crop](https://github.com/pqina/filepond-plugin-image-crop)
-   [Image filter](https://github.com/pqina/filepond-plugin-image-filter)
-   [Image resize](https://github.com/pqina/filepond-plugin-image-resize)
-   [Image transform](https://github.com/pqina/filepond-plugin-image-transform)
-   [Image EXIF orientation](https://github.com/pqina/filepond-plugin-image-exif-orientation)
-   [Image overlay](https://github.com/nielsboogaard/filepond-plugin-image-overlay) ([nielsboogaard/filepond-plugin-image-overlay](https://github.com/nielsboogaard/filepond-plugin-image-overlay))
-   [Media preview](https://github.com/nielsboogaard/filepond-plugin-media-preview) ([nielsboogaard/filepond-plugin-media-preview](https://github.com/nielsboogaard/filepond-plugin-media-preview))
-   [Media preview + PDF preview](https://github.com/ErnestBrandi/filepond-plugin-media-preview) ([ErnestBrandi/filepond-plugin-media-preview](https://github.com/ErnestBrandi/filepond-plugin-media-preview))
-   [Get file](https://github.com/nielsboogaard/filepond-plugin-get-file) ([nielsboogaard/filepond-plugin-get-file](https://github.com/nielsboogaard/filepond-plugin-get-file))
-   [Zip Directory Uploads](https://github.com/tzsk/filepond-plugin-zipper) ([tzsk/filepond-plugin-zipper](https://github.com/tzsk/filepond-plugin-zipper))
-   [PDF Preview](https://github.com/Adri-Glez/filepond-plugin-pdf-preview) ([Adri-Glez/filepond-plugin-pdf-preview](https://github.com/Adri-Glez/filepond-plugin-pdf-preview))
-   [PDF Convert](https://github.com/alexandreDavid/filepond-plugin-pdf-convert) ([alexandreDavid/filepond-plugin-pdf-convert](https://github.com/alexandreDavid/filepond-plugin-pdf-convert))

### Adapters

-   [React](https://github.com/pqina/react-filepond)
-   [Vue](https://github.com/pqina/vue-filepond)
-   [Svelte](https://github.com/pqina/svelte-filepond)
-   [jQuery](https://github.com/pqina/jquery-filepond)
-   [Angular](https://github.com/pqina/ngx-filepond)
-   [Angular 1](https://github.com/johnnyasantoss/angularjs-filepond) ([johnnyasantoss/angularjs-filepond](https://github.com/johnnyasantoss/angularjs-filepond))
-   [Blazor](https://github.com/soenneker/soenneker.blazor.filepond) ([soenneker/soenneker.blazor.filepond](https://github.com/soenneker/soenneker.blazor.filepond))
-   [Ember](https://github.com/alexdiliberto/ember-filepond) ([alexdiliberto/ember-filepond](https://github.com/alexdiliberto/ember-filepond))

### Backend

-   [PHP](https://github.com/pqina/filepond-boilerplate-php)
-   [Django](https://github.com/ImperialCollegeLondon/django-drf-filepond) ([ImperialCollegeLondon/django-drf-filepond](https://github.com/ImperialCollegeLondon/django-drf-filepond))
-   [Laravel](https://github.com/Sopamo/laravel-filepond) ([Sopamo/laravel-filepond](https://github.com/Sopamo/laravel-filepond))
-   [Laravel](https://github.com/Albert221/laravel-filepond) ([Albert221/laravel-filepond](https://github.com/Albert221/laravel-filepond))
-   [SilverStripe](https://github.com/lekoala/silverstripe-filepond) ([lekoala/silverstripe-filepond](https://github.com/lekoala/silverstripe-filepond))
-   [Ruby on Rails](https://github.com/Code-With-Rails/filepond-rails) ([Code-With-Rails/filepond-rails](https://github.com/Code-With-Rails/filepond-rails))

## Quick Start

Install using npm:

```bash
npm install filepond
```

Then import in your project:

```js
import * as FilePond from 'filepond';

// Create a multi file upload component
const pond = FilePond.create({
    multiple: true,
    name: 'filepond',
});

// Add it to the DOM
document.body.appendChild(pond.element);
```

Or get it from a CDN:

```html
<!DOCTYPE html>
<html>
    <head>
        <title>FilePond from CDN</title>

        <!-- Filepond stylesheet -->
        <link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet" />
    </head>
    <body>
        <!-- We'll transform this input into a pond -->
        <input type="file" class="filepond" />

        <!-- Load FilePond library -->
        <script src="https://unpkg.com/filepond/dist/filepond.js"></script>

        <!-- Turn all file input elements into ponds -->
        <script>
            FilePond.parse(document.body);
        </script>
    </body>
</html>
```

[Getting started with FilePond](https://pqina.nl/filepond/docs/patterns/getting-started/)

## Internationalization

The [locale folder](./locale/) contains different language files, PR's are welcome, you can use locale files like this:

```js
import pt_BR from 'filepond/locale/pt-br.js';

FilePond.setOptions(pt_BR);
```

## Contributing

At the moment test coverage is not great, it's around 65%. To accept pull requests the tests need to be better, any help to improve them is very much appreciated.

Tests are based on Jest and can be run with `npm run test`

To build the library run `npm run build`

## Publications

-   [Using FilePond with NodeJS](https://www.infoworld.com/article/3627248/how-to-use-filepond-with-nodejs.html)
-   [Applying Watermarks to Images with FilePond](https://pqina.nl/blog/applying-watermarks-to-images-with-filepond/)
-   [Generating Image Thumbnails in the Browser using JavaScript and FilePond](https://dev.to/pqina/generating-image-thumbnails-in-the-browser-using-javascript-and-filepond-10b8)
-   [How to upload files with Vue and FilePond](https://dev.to/pqina/how-to-upload-files-with-vue-and-filepond-1m02)
-   [Smooth file uploading with React and FilePond](https://itnext.io/uploading-files-with-react-and-filepond-f8a798308557)
-   [5 interesting technical challenges I faced while building FilePond](https://itnext.io/filepond-frontend-trickery-a3073c934c77)
-   [Image uploads with Laravel and FilePond](https://devdojo.com/episode/image-uploads-with-laravel-and-filepond)
-   [Integrating FilePond with Ember](https://alexdiliberto.com/ember-filepond/)
-   [FilePond launch day post-mortem](https://pqina.nl/blog/filepond-launch-day-post-mortem)
-   [FilePond on ProductHunt](https://www.producthunt.com/posts/filepond-js)

### Browser Compatibility

FilePond is compatible with a wide range of desktop and mobile browsers, the oldest explicitly supported browser is IE11, for best cross browser support add [FilePond Polyfill](https://github.com/pqina/filepond-polyfill) and [Babel polyfill](https://babeljs.io/docs/en/babel-polyfill) to your project.

FilePond uses [BrowserStack](https://www.browserstack.com/) for compatibility testing.

[<img src="https://github.com/pqina/filepond-github-assets/blob/master/browserstack-logo.svg" height="32" alt="BrowserStack"/>](https://www.browserstack.com/)

## License

**Please don't remove or change the disclaimers in the source files**

MIT License

Copyright (c) 2020 PQINA | [Rik Schennink](mailto:rik@pqina.nl)

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: banner-cli.js
================================================
const banner = require('./banner');
const pkg = require('./package.json');
const args = process.argv.slice(2);
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(data) {
    process.stdout.write(banner({ id: args[0], ...pkg }) + data);
});

================================================
FILE: banner.js
================================================
module.exports = ({ id, version, license, homepage }) => `/*!
 * ${ id } ${ version }
 * Licensed under ${ license }, https://opensource.org/licenses/${ license }/
 * Please visit ${ homepage } for details.
 */

/* eslint-disable */
`;

================================================
FILE: dist/filepond.css
================================================
/*!
 * FilePond 4.32.12
 * Licensed under MIT, https://opensource.org/licenses/MIT/
 * Please visit https://pqina.nl/filepond/ for details.
 */

/* eslint-disable */
.filepond--assistant {
    position: absolute;
    overflow: hidden;
    height: 1px;
    width: 1px;
    padding: 0;
    border: 0;
    clip: rect(1px, 1px, 1px, 1px);
    -webkit-clip-path: inset(50%);
    clip-path: inset(50%);
    white-space: nowrap;
}
/* Hard to override styles */
.filepond--browser.filepond--browser {
    /* is positioned absolute so it is focusable for form validation errors */
    position: absolute;
    margin: 0;
    padding: 0;

    /* is positioned ~behind drop label */
    left: 1em;
    top: 1.75em;
    width: calc(100% - 2em);

    /* hide visually */
    opacity: 0;
    font-size: 0; /* removes text cursor in Internet Explorer 11 */
}
.filepond--data {
    position: absolute;
    width: 0;
    height: 0;
    padding: 0;
    margin: 0;
    border: none;
    visibility: hidden;
    pointer-events: none;
    contain: strict;
}
.filepond--drip {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow: hidden;
    opacity: 0.1;

    /* can't interact with this element */
    pointer-events: none;

    /* inherit border radius from parent (needed for drip-blob cut of) */
    border-radius: 0.5em;

    /* this seems to prevent Chrome from redrawing this layer constantly */
    background: rgba(0, 0, 0, 0.01);
}
.filepond--drip-blob {
    position: absolute;
    -webkit-transform-origin: center center;
    transform-origin: center center;
    top: 0;
    left: 0;
    width: 8em;
    height: 8em;
    margin-left: -4em;
    margin-top: -4em;
    background: #292625;
    border-radius: 50%;

    /* will be animated */
    will-change: transform, opacity;
}
.filepond--drop-label {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    margin: 0;
    color: #4f4f4f;

    /* center contents */
    display: flex;
    justify-content: center;
    align-items: center;

    /* fixes IE11 centering problems (is overruled by label min-height) */
    height: 0px;

    /* dont allow selection */
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;

    /* will be animated */
    will-change: transform, opacity;
}
/* Hard to override styles on purpose */
.filepond--drop-label.filepond--drop-label label {
    display: block;
    margin: 0;
    padding: 0.5em; /* use padding instead of margin so click area is not impacted */
}
.filepond--drop-label label {
    cursor: default;
    font-size: 0.875em;
    font-weight: normal;
    text-align: center;
    line-height: 1.5;
}
.filepond--label-action {
    text-decoration: underline;
    -webkit-text-decoration-skip: ink;
    text-decoration-skip-ink: auto;
    -webkit-text-decoration-color: #a7a4a4;
    text-decoration-color: #a7a4a4;
    cursor: pointer;
}
.filepond--root[data-disabled] .filepond--drop-label label {
    opacity: 0.5;
}
/* Hard to override styles */
.filepond--file-action-button.filepond--file-action-button {
    font-size: 1em;
    width: 1.625em;
    height: 1.625em;

    font-family: inherit;
    line-height: inherit;

    margin: 0;
    padding: 0;
    border: none;
    outline: none;

    will-change: transform, opacity;

    /* hidden label */
}
.filepond--file-action-button.filepond--file-action-button span {
    position: absolute;
    overflow: hidden;
    height: 1px;
    width: 1px;
    padding: 0;
    border: 0;
    clip: rect(1px, 1px, 1px, 1px);
    -webkit-clip-path: inset(50%);
    clip-path: inset(50%);
    white-space: nowrap;
}
.filepond--file-action-button.filepond--file-action-button {
    /* scale SVG to fill button */
}
.filepond--file-action-button.filepond--file-action-button svg {
    width: 100%;
    height: 100%;
}
.filepond--file-action-button.filepond--file-action-button {
    /* bigger touch area */
}
.filepond--file-action-button.filepond--file-action-button::after {
    position: absolute;
    left: -0.75em;
    right: -0.75em;
    top: -0.75em;
    bottom: -0.75em;
    content: '';
}
/* Soft styles */
.filepond--file-action-button {
    /* use default arrow cursor */
    cursor: auto;

    /* reset default button styles */
    color: #fff;

    /* set default look n feel */
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.5);
    background-image: none;

    /* we animate box shadow on focus */
    /* it's only slightly slower than animating */
    /* a pseudo-element with transforms and renders */
    /* a lot better on chrome */
    box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
    transition: box-shadow 0.25s ease-in;
}
.filepond--file-action-button:hover,
.filepond--file-action-button:focus {
    box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.9);
}
.filepond--file-action-button[disabled] {
    color: rgba(255, 255, 255, 0.5);
    background-color: rgba(0, 0, 0, 0.25);
}
.filepond--file-action-button[hidden] {
    display: none;
}
/* edit button */
.filepond--action-edit-item.filepond--action-edit-item {
    width: 2em;
    height: 2em;
    padding: 0.1875em;
}
.filepond--action-edit-item.filepond--action-edit-item[data-align*='center'] {
    margin-left: -0.1875em;
}
.filepond--action-edit-item.filepond--action-edit-item[data-align*='bottom'] {
    margin-bottom: -0.1875em;
}
.filepond--action-edit-item-alt {
    border: none;
    line-height: inherit;
    background: transparent;
    font-family: inherit;
    color: inherit;
    outline: none;
    padding: 0;
    margin: 0 0 0 0.25em;
    pointer-events: all;
    position: absolute;
}
.filepond--action-edit-item-alt svg {
    width: 1.3125em;
    height: 1.3125em;
}
.filepond--action-edit-item-alt span {
    font-size: 0;
    opacity: 0;
}
.filepond--file-info {
    position: static;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    flex: 1;
    margin: 0 0.5em 0 0;
    min-width: 0;

    /* will be animated */
    will-change: transform, opacity;

    /* can't do anything with this info */
    pointer-events: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;

    /* no margins on children */
}
.filepond--file-info * {
    margin: 0;
}
.filepond--file-info {
    /* we don't want to have these overrules so these selectors are a bit more specific */
}
.filepond--file-info .filepond--file-info-main {
    font-size: 0.75em;
    line-height: 1.2;

    /* we want ellipsis if this bar gets too wide */
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    width: 100%;
}
.filepond--file-info .filepond--file-info-sub {
    font-size: 0.625em;
    opacity: 0.5;
    transition: opacity 0.25s ease-in-out;
    white-space: nowrap;
}
.filepond--file-info .filepond--file-info-sub:empty {
    display: none;
}
.filepond--file-status {
    position: static;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    flex-grow: 0;
    flex-shrink: 0;

    margin: 0;
    min-width: 2.25em;
    text-align: right;

    /* will be animated */
    will-change: transform, opacity;

    /* can't do anything with this info */
    pointer-events: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;

    /* no margins on children */
}
.filepond--file-status * {
    margin: 0;
    white-space: nowrap;
}
.filepond--file-status {
    /* font sizes */
}
.filepond--file-status .filepond--file-status-main {
    font-size: 0.75em;
    line-height: 1.2;
}
.filepond--file-status .filepond--file-status-sub {
    font-size: 0.625em;
    opacity: 0.5;
    transition: opacity 0.25s ease-in-out;
}
/* Hard to override styles */
.filepond--file-wrapper.filepond--file-wrapper {
    border: none;
    margin: 0;
    padding: 0;
    min-width: 0;
    height: 100%;

    /* hide legend for visual users */
}
.filepond--file-wrapper.filepond--file-wrapper > legend {
    position: absolute;
    overflow: hidden;
    height: 1px;
    width: 1px;
    padding: 0;
    border: 0;
    clip: rect(1px, 1px, 1px, 1px);
    -webkit-clip-path: inset(50%);
    clip-path: inset(50%);
    white-space: nowrap;
}
.filepond--file {
    position: static;
    display: flex;
    height: 100%;
    align-items: flex-start;

    padding: 0.5625em 0.5625em;

    color: #fff;
    border-radius: 0.5em;

    /* control positions */
}
.filepond--file .filepond--file-status {
    margin-left: auto;
    margin-right: 2.25em;
}
.filepond--file .filepond--processing-complete-indicator {
    pointer-events: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    z-index: 3;
}
.filepond--file .filepond--processing-complete-indicator,
.filepond--file .filepond--progress-indicator,
.filepond--file .filepond--file-action-button {
    position: absolute;
}
.filepond--file {
    /* .filepond--file-action-button */
}
.filepond--file [data-align*='left'] {
    left: 0.5625em;
}
.filepond--file [data-align*='right'] {
    right: 0.5625em;
}
.filepond--file [data-align*='center'] {
    left: calc(50% - 0.8125em); /* .8125 is half of button width */
}
.filepond--file [data-align*='bottom'] {
    bottom: 1.125em;
}
.filepond--file [data-align='center'] {
    top: calc(50% - 0.8125em);
}
.filepond--file .filepond--progress-indicator {
    margin-top: 0.1875em;
}
.filepond--file .filepond--progress-indicator[data-align*='right'] {
    margin-right: 0.1875em;
}
.filepond--file .filepond--progress-indicator[data-align*='left'] {
    margin-left: 0.1875em;
}
/* make sure text does not overlap */
[data-filepond-item-state='cancelled'] .filepond--file-info,
[data-filepond-item-state*='invalid'] .filepond--file-info,
[data-filepond-item-state*='error'] .filepond--file-info {
    margin-right: 2.25em;
}
[data-filepond-item-state~='processing'] .filepond--file-status-sub {
    opacity: 0;
}
[data-filepond-item-state~='processing']
    .filepond--action-abort-item-processing
    ~ .filepond--file-status
    .filepond--file-status-sub {
    opacity: 0.5;
}
[data-filepond-item-state='processing-error'] .filepond--file-status-sub {
    opacity: 0;
}
[data-filepond-item-state='processing-error']
    .filepond--action-retry-item-processing
    ~ .filepond--file-status
    .filepond--file-status-sub {
    opacity: 0.5;
}
[data-filepond-item-state='processing-complete'] {
    /* busy state */
}
[data-filepond-item-state='processing-complete'] .filepond--action-revert-item-processing svg {
    -webkit-animation: fall 0.5s 0.125s linear both;
    animation: fall 0.5s 0.125s linear both;
}
[data-filepond-item-state='processing-complete'] {
    /* hide details by default, only show when can revert */
}
[data-filepond-item-state='processing-complete'] .filepond--file-status-sub {
    opacity: 0.5;
}
[data-filepond-item-state='processing-complete']
    .filepond--processing-complete-indicator:not([style*='hidden'])
    ~ .filepond--file-status
    .filepond--file-status-sub {
    opacity: 0;
}
[data-filepond-item-state='processing-complete'] .filepond--file-info-sub {
    opacity: 0;
}
[data-filepond-item-state='processing-complete']
    .filepond--action-revert-item-processing
    ~ .filepond--file-info
    .filepond--file-info-sub {
    opacity: 0.5;
}
/* file state can be invalid or error, both are visually similar but */
/* having them as separate states might be useful */
[data-filepond-item-state*='invalid'] .filepond--panel,
[data-filepond-item-state*='invalid'] .filepond--file-wrapper,
[data-filepond-item-state*='error'] .filepond--panel,
[data-filepond-item-state*='error'] .filepond--file-wrapper {
    -webkit-animation: shake 0.65s linear both;
    animation: shake 0.65s linear both;
}
/* spins progress indicator when file is marked as busy */
[data-filepond-item-state*='busy'] .filepond--progress-indicator svg {
    -webkit-animation: spin 1s linear infinite;
    animation: spin 1s linear infinite;
}
/**
 * States
 */
@-webkit-keyframes spin {
    0% {
        -webkit-transform: rotateZ(0deg);
        transform: rotateZ(0deg);
    }

    100% {
        -webkit-transform: rotateZ(360deg);
        transform: rotateZ(360deg);
    }
}
@keyframes spin {
    0% {
        -webkit-transform: rotateZ(0deg);
        transform: rotateZ(0deg);
    }

    100% {
        -webkit-transform: rotateZ(360deg);
        transform: rotateZ(360deg);
    }
}
@-webkit-keyframes shake {
    10%,
    90% {
        -webkit-transform: translateX(-0.0625em);
        transform: translateX(-0.0625em);
    }

    20%,
    80% {
        -webkit-transform: translateX(0.125em);
        transform: translateX(0.125em);
    }

    30%,
    50%,
    70% {
        -webkit-transform: translateX(-0.25em);
        transform: translateX(-0.25em);
    }

    40%,
    60% {
        -webkit-transform: translateX(0.25em);
        transform: translateX(0.25em);
    }
}
@keyframes shake {
    10%,
    90% {
        -webkit-transform: translateX(-0.0625em);
        transform: translateX(-0.0625em);
    }

    20%,
    80% {
        -webkit-transform: translateX(0.125em);
        transform: translateX(0.125em);
    }

    30%,
    50%,
    70% {
        -webkit-transform: translateX(-0.25em);
        transform: translateX(-0.25em);
    }

    40%,
    60% {
        -webkit-transform: translateX(0.25em);
        transform: translateX(0.25em);
    }
}
@-webkit-keyframes fall {
    0% {
        opacity: 0;
        -webkit-transform: scale(0.5);
        transform: scale(0.5);
        -webkit-animation-timing-function: ease-out;
        animation-timing-function: ease-out;
    }

    70% {
        opacity: 1;
        -webkit-transform: scale(1.1);
        transform: scale(1.1);
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
    }

    100% {
        -webkit-transform: scale(1);
        transform: scale(1);
        -webkit-animation-timing-function: ease-out;
        animation-timing-function: ease-out;
    }
}
@keyframes fall {
    0% {
        opacity: 0;
        -webkit-transform: scale(0.5);
        transform: scale(0.5);
        -webkit-animation-timing-function: ease-out;
        animation-timing-function: ease-out;
    }

    70% {
        opacity: 1;
        -webkit-transform: scale(1.1);
        transform: scale(1.1);
        -webkit-animation-timing-function: ease-in-out;
        animation-timing-function: ease-in-out;
    }

    100% {
        -webkit-transform: scale(1);
        transform: scale(1);
        -webkit-animation-timing-function: ease-out;
        animation-timing-function: ease-out;
    }
}
/* ignore all other interaction elements while dragging a file */
.filepond--hopper[data-hopper-state='drag-over'] > * {
    pointer-events: none;
}
/* capture all hit tests using a hidden layer, this speeds up the event flow */
.filepond--hopper[data-hopper-state='drag-over']::after {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 100;
}
.filepond--progress-indicator {
    z-index: 103;
}
.filepond--file-action-button {
    z-index: 102;
}
.filepond--file-status {
    z-index: 101;
}
.filepond--file-info {
    z-index: 100;
}
.filepond--item {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1;

    padding: 0;
    margin: 0.25em;

    will-change: transform, opacity;

    touch-action: auto;

    /* item children order */
}
.filepond--item > .filepond--panel {
    z-index: -1;
}
/* has a slight shadow */
.filepond--item > .filepond--panel .filepond--panel-bottom {
    box-shadow: 0 0.0625em 0.125em -0.0625em rgba(0, 0, 0, 0.25);
}
.filepond--item {
    /* drag related */
}
.filepond--item > .filepond--file-wrapper,
.filepond--item > .filepond--panel {
    transition: opacity 0.15s ease-out;
}
.filepond--item[data-drag-state] {
    cursor: -webkit-grab;
    cursor: grab;
}
.filepond--item[data-drag-state] > .filepond--panel {
    transition: box-shadow 0.125s ease-in-out;
    box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}
.filepond--item[data-drag-state='drag'] {
    cursor: -webkit-grabbing;
    cursor: grabbing;
}
.filepond--item[data-drag-state='drag'] > .filepond--panel {
    box-shadow: 0 0.125em 0.3125em rgba(0, 0, 0, 0.325);
}
.filepond--item[data-drag-state]:not([data-drag-state='idle']) {
    z-index: 2;
}
/* states */
.filepond--item-panel {
    background-color: #64605e;
}
[data-filepond-item-state='processing-complete'] .filepond--item-panel {
    background-color: #369763;
}
[data-filepond-item-state*='invalid'] .filepond--item-panel,
[data-filepond-item-state*='error'] .filepond--item-panel {
    background-color: #c44e47;
}
/* style of item panel */
.filepond--item-panel {
    border-radius: 0.5em;
    transition: background-color 0.25s;
}
/* normal mode */
.filepond--list-scroller {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    margin: 0;
    will-change: transform;
}
/* scroll mode */
.filepond--list-scroller[data-state='overflow'] .filepond--list {
    bottom: 0;
    right: 0;
}
.filepond--list-scroller[data-state='overflow'] {
    overflow-y: scroll;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    -webkit-mask: linear-gradient(to bottom, #000 calc(100% - 0.5em), transparent 100%);
    mask: linear-gradient(to bottom, #000 calc(100% - 0.5em), transparent 100%);
}
/* style scrollbar */
.filepond--list-scroller::-webkit-scrollbar {
    background: transparent;
}
.filepond--list-scroller::-webkit-scrollbar:vertical {
    width: 1em;
}
.filepond--list-scroller::-webkit-scrollbar:horizontal {
    height: 0;
}
.filepond--list-scroller::-webkit-scrollbar-thumb {
    background-color: rgba(0, 0, 0, 0.3);
    border-radius: 99999px;
    border: 0.3125em solid transparent;
    background-clip: content-box;
}
/* hard to overide styles on purpose */
.filepond--list.filepond--list {
    position: absolute;
    top: 0;
    margin: 0;
    padding: 0;
    list-style-type: none;

    /* prevents endless paint calls on filepond--list-scroller */
    will-change: transform;
}
/* used for padding so allowed to be restyled */
.filepond--list {
    left: 0.75em;
    right: 0.75em;
}
.filepond--root[data-style-panel-layout~='integrated'] {
    width: 100%;
    height: 100%;
    max-width: none;
    margin: 0;
}
.filepond--root[data-style-panel-layout~='circle'] .filepond--panel-root,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--panel-root {
    border-radius: 0;
}
.filepond--root[data-style-panel-layout~='circle'] .filepond--panel-root > *,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--panel-root > * {
    display: none;
}
.filepond--root[data-style-panel-layout~='circle'] .filepond--drop-label,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--drop-label {
    bottom: 0;
    height: auto;
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 7;
}
.filepond--root[data-style-panel-layout~='circle'],
.filepond--root[data-style-panel-layout~='integrated'] {
    /* we're only loading one item, this makes the intro animation a bit nicer */
}
.filepond--root[data-style-panel-layout~='circle'] .filepond--item-panel,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--item-panel {
    display: none;
}
.filepond--root[data-style-panel-layout~='compact'] .filepond--list-scroller,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--list-scroller {
    overflow: hidden;
    height: 100%;
    margin-top: 0;
    margin-bottom: 0;
}
.filepond--root[data-style-panel-layout~='compact'] .filepond--list,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--list {
    left: 0;
    right: 0;
    height: 100%;
}
.filepond--root[data-style-panel-layout~='compact'] .filepond--item,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--item {
    margin: 0;
}
.filepond--root[data-style-panel-layout~='compact'] .filepond--file-wrapper,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--file-wrapper {
    height: 100%;
}
.filepond--root[data-style-panel-layout~='compact'] .filepond--drop-label,
.filepond--root[data-style-panel-layout~='integrated'] .filepond--drop-label {
    z-index: 7;
}
.filepond--root[data-style-panel-layout~='circle'] {
    border-radius: 99999rem;
    overflow: hidden;
}
.filepond--root[data-style-panel-layout~='circle'] > .filepond--panel {
    border-radius: inherit;
}
.filepond--root[data-style-panel-layout~='circle'] > .filepond--panel > * {
    display: none;
}
.filepond--root[data-style-panel-layout~='circle'] {
    /* circle cuts of this info, so best to hide it */
}
.filepond--root[data-style-panel-layout~='circle'] .filepond--file-info {
    display: none;
}
.filepond--root[data-style-panel-layout~='circle'] .filepond--file-status {
    display: none;
}
.filepond--root[data-style-panel-layout~='circle'] .filepond--action-edit-item {
    opacity: 1 !important;
    visibility: visible !important;
}
/* dirfty way to fix circular overflow issue on safari 11+ */
@media not all and (min-resolution: 0.001dpcm) {
    @supports (-webkit-appearance: none) and (stroke-color: transparent) {
        .filepond--root[data-style-panel-layout~='circle'] {
            will-change: transform;
        }
    }
}
.filepond--panel-root {
    border-radius: 0.5em;
    background-color: #f1f0ef;
}
.filepond--panel {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    margin: 0;

    /* defaults to 100% height (fixed height mode) this fixes problem with panel height in IE11 */
    height: 100% !important;

    /* no interaction possible with panel */
    pointer-events: none;
}
.filepond-panel:not([data-scalable='false']) {
    height: auto !important;
}
.filepond--panel[data-scalable='false'] > div {
    display: none;
}
.filepond--panel[data-scalable='true'] {
    /* this seems to fix Chrome performance issues */
    /* - when box-shadow is enabled */
    /* - when multiple ponds are active on the same page */
    -webkit-transform-style: preserve-3d;
    transform-style: preserve-3d;

    /* prevent borders and backgrounds */
    background-color: transparent !important;
    border: none !important;
}
.filepond--panel-top,
.filepond--panel-bottom,
.filepond--panel-center {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    margin: 0;
    padding: 0;
}
.filepond--panel-top,
.filepond--panel-bottom {
    height: 0.5em;
}
.filepond--panel-top {
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
    border-bottom: none !important;

    /* fixes tiny transparant line between top and center panel */
}
.filepond--panel-top::after {
    content: '';
    position: absolute;
    height: 2px;
    left: 0;
    right: 0;
    bottom: -1px;
    background-color: inherit;
}
.filepond--panel-center,
.filepond--panel-bottom {
    will-change: transform;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-transform-origin: left top;
    transform-origin: left top;
    -webkit-transform: translate3d(0, 0.5em, 0);
    transform: translate3d(0, 0.5em, 0);
}
.filepond--panel-bottom {
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
    border-top: none !important;

    /* fixes tiny transparant line between bottom and center of panel */
}
.filepond--panel-bottom::before {
    content: '';
    position: absolute;
    height: 2px;
    left: 0;
    right: 0;
    top: -1px;
    background-color: inherit;
}
.filepond--panel-center {
    /* the center panel is scaled using scale3d to fit the correct height */
    /* we use 100px instead of 1px as scaling 1px to a huge height is really laggy on chrome */
    height: 100px !important;
    border-top: none !important;
    border-bottom: none !important;
    border-radius: 0 !important;

    /* hide if not transformed, prevents a little flash when the panel is at 100px height while attached for first time */
}
.filepond--panel-center:not([style]) {
    visibility: hidden;
}
.filepond--progress-indicator {
    position: static;
    width: 1.25em;
    height: 1.25em;

    color: #fff;

    /* can't have margins */
    margin: 0;

    /* no interaction possible with progress indicator */
    pointer-events: none;

    /* will be animated */
    will-change: transform, opacity;
}
.filepond--progress-indicator svg {
    width: 100%;
    height: 100%;
    vertical-align: top;
    transform-box: fill-box; /* should center the animation correctly when zoomed in */
}
.filepond--progress-indicator path {
    fill: none;
    stroke: currentColor;
}
.filepond--list-scroller {
    z-index: 6;
}
.filepond--drop-label {
    z-index: 5;
}
.filepond--drip {
    z-index: 3;
}
.filepond--root > .filepond--panel {
    z-index: 2;
}
.filepond--browser {
    z-index: 1;
}
.filepond--root {
    /* layout*/
    box-sizing: border-box;
    position: relative;
    margin-bottom: 1em;

    /* base font size for whole component */
    font-size: 1rem;

    /* base line height */
    line-height: normal;

    /* up uses default system font family */
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
        'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';

    /* will increase font weight a bit on Safari */
    font-weight: 450;

    /* default text alignment */
    text-align: left;

    /* better text rendering on Safari */
    text-rendering: optimizeLegibility;

    /* text direction is ltr for now */
    direction: ltr;

    /* optimize rendering */
    /* https://developer.mozilla.org/en-US/docs/Web/CSS/contain */
    contain: layout style size;

    /* correct box sizing, line-height and positioning on child elements */
}
.filepond--root * {
    box-sizing: inherit;
    line-height: inherit;
}
.filepond--root *:not(text) {
    font-size: inherit;
}
.filepond--root {
    /* block everything */
}
.filepond--root[data-disabled] {
    pointer-events: none;
}
.filepond--root[data-disabled] .filepond--list-scroller {
    pointer-events: all;
}
.filepond--root[data-disabled] .filepond--list {
    pointer-events: none;
}
/**
 * Root element children layout
 */
.filepond--root .filepond--drop-label {
    min-height: 4.75em;
}
.filepond--root .filepond--list-scroller {
    margin-top: 1em;
    margin-bottom: 1em;
}
.filepond--root .filepond--credits {
    position: absolute;
    right: 0;
    opacity: 0.4;
    line-height: 0.85;
    font-size: 11px;
    color: inherit;
    text-decoration: none;
    z-index: 3;
    bottom: -14px;
}
.filepond--root .filepond--credits[style] {
    top: 0;
    bottom: auto;
    margin-top: 14px;
}


================================================
FILE: dist/filepond.esm.js
================================================
/*!
 * FilePond 4.32.12
 * Licensed under MIT, https://opensource.org/licenses/MIT/
 * Please visit https://pqina.nl/filepond/ for details.
 */

/* eslint-disable */

const isNode = value => value instanceof HTMLElement;

const createStore = (initialState, queries = [], actions = []) => {
    // internal state
    const state = {
        ...initialState,
    };

    // contains all actions for next frame, is clear when actions are requested
    const actionQueue = [];
    const dispatchQueue = [];

    // returns a duplicate of the current state
    const getState = () => ({ ...state });

    // returns a duplicate of the actions array and clears the actions array
    const processActionQueue = () => {
        // create copy of actions queue
        const queue = [...actionQueue];

        // clear actions queue (we don't want no double actions)
        actionQueue.length = 0;

        return queue;
    };

    // processes actions that might block the main UI thread
    const processDispatchQueue = () => {
        // create copy of actions queue
        const queue = [...dispatchQueue];

        // clear actions queue (we don't want no double actions)
        dispatchQueue.length = 0;

        // now dispatch these actions
        queue.forEach(({ type, data }) => {
            dispatch(type, data);
        });
    };

    // adds a new action, calls its handler and
    const dispatch = (type, data, isBlocking) => {
        // is blocking action (should never block if document is hidden)
        if (isBlocking && !document.hidden) {
            dispatchQueue.push({ type, data });
            return;
        }

        // if this action has a handler, handle the action
        if (actionHandlers[type]) {
            actionHandlers[type](data);
        }

        // now add action
        actionQueue.push({
            type,
            data,
        });
    };

    const query = (str, ...args) => (queryHandles[str] ? queryHandles[str](...args) : null);

    const api = {
        getState,
        processActionQueue,
        processDispatchQueue,
        dispatch,
        query,
    };

    let queryHandles = {};
    queries.forEach(query => {
        queryHandles = {
            ...query(state),
            ...queryHandles,
        };
    });

    let actionHandlers = {};
    actions.forEach(action => {
        actionHandlers = {
            ...action(dispatch, query, state),
            ...actionHandlers,
        };
    });

    return api;
};

const defineProperty = (obj, property, definition) => {
    if (typeof definition === 'function') {
        obj[property] = definition;
        return;
    }
    Object.defineProperty(obj, property, { ...definition });
};

const forin = (obj, cb) => {
    for (const key in obj) {
        if (!obj.hasOwnProperty(key)) {
            continue;
        }

        cb(key, obj[key]);
    }
};

const createObject = definition => {
    const obj = {};
    forin(definition, property => {
        defineProperty(obj, property, definition[property]);
    });
    return obj;
};

const attr = (node, name, value = null) => {
    if (value === null) {
        return node.getAttribute(name) || node.hasAttribute(name);
    }
    node.setAttribute(name, value);
};

const ns = 'http://www.w3.org/2000/svg';
const svgElements = ['svg', 'path']; // only svg elements used

const isSVGElement = tag => svgElements.includes(tag);

const createElement = (tag, className, attributes = {}) => {
    if (typeof className === 'object') {
        attributes = className;
        className = null;
    }
    const element = isSVGElement(tag)
        ? document.createElementNS(ns, tag)
        : document.createElement(tag);
    if (className) {
        if (isSVGElement(tag)) {
            attr(element, 'class', className);
        } else {
            element.className = className;
        }
    }
    forin(attributes, (name, value) => {
        attr(element, name, value);
    });
    return element;
};

const appendChild = parent => (child, index) => {
    if (typeof index !== 'undefined' && parent.children[index]) {
        parent.insertBefore(child, parent.children[index]);
    } else {
        parent.appendChild(child);
    }
};

const appendChildView = (parent, childViews) => (view, index) => {
    if (typeof index !== 'undefined') {
        childViews.splice(index, 0, view);
    } else {
        childViews.push(view);
    }

    return view;
};

const removeChildView = (parent, childViews) => view => {
    // remove from child views
    childViews.splice(childViews.indexOf(view), 1);

    // remove the element
    if (view.element.parentNode) {
        parent.removeChild(view.element);
    }

    return view;
};

const IS_BROWSER = (() =>
    typeof window !== 'undefined' && typeof window.document !== 'undefined')();
const isBrowser = () => IS_BROWSER;

const testElement = isBrowser() ? createElement('svg') : {};
const getChildCount =
    'children' in testElement ? el => el.children.length : el => el.childNodes.length;

const getViewRect = (elementRect, childViews, offset, scale) => {
    const left = offset[0] || elementRect.left;
    const top = offset[1] || elementRect.top;
    const right = left + elementRect.width;
    const bottom = top + elementRect.height * (scale[1] || 1);

    const rect = {
        // the rectangle of the element itself
        element: {
            ...elementRect,
        },

        // the rectangle of the element expanded to contain its children, does not include any margins
        inner: {
            left: elementRect.left,
            top: elementRect.top,
            right: elementRect.right,
            bottom: elementRect.bottom,
        },

        // the rectangle of the element expanded to contain its children including own margin and child margins
        // margins will be added after we've recalculated the size
        outer: {
            left,
            top,
            right,
            bottom,
        },
    };

    // expand rect to fit all child rectangles
    childViews
        .filter(childView => !childView.isRectIgnored())
        .map(childView => childView.rect)
        .forEach(childViewRect => {
            expandRect(rect.inner, { ...childViewRect.inner });
            expandRect(rect.outer, { ...childViewRect.outer });
        });

    // calculate inner width and height
    calculateRectSize(rect.inner);

    // append additional margin (top and left margins are included in top and left automatically)
    rect.outer.bottom += rect.element.marginBottom;
    rect.outer.right += rect.element.marginRight;

    // calculate outer width and height
    calculateRectSize(rect.outer);

    return rect;
};

const expandRect = (parent, child) => {
    // adjust for parent offset
    child.top += parent.top;
    child.right += parent.left;
    child.bottom += parent.top;
    child.left += parent.left;

    if (child.bottom > parent.bottom) {
        parent.bottom = child.bottom;
    }

    if (child.right > parent.right) {
        parent.right = child.right;
    }
};

const calculateRectSize = rect => {
    rect.width = rect.right - rect.left;
    rect.height = rect.bottom - rect.top;
};

const isNumber = value => typeof value === 'number';

/**
 * Determines if position is at destination
 * @param position
 * @param destination
 * @param velocity
 * @param errorMargin
 * @returns {boolean}
 */
const thereYet = (position, destination, velocity, errorMargin = 0.001) => {
    return Math.abs(position - destination) < errorMargin && Math.abs(velocity) < errorMargin;
};

/**
 * Spring animation
 */
const spring =
    // default options
    ({ stiffness = 0.5, damping = 0.75, mass = 10 } = {}) =>
        // method definition
        {
            let target = null;
            let position = null;
            let velocity = 0;
            let resting = false;

            // updates spring state
            const interpolate = (ts, skipToEndState) => {
                // in rest, don't animate
                if (resting) return;

                // need at least a target or position to do springy things
                if (!(isNumber(target) && isNumber(position))) {
                    resting = true;
                    velocity = 0;
                    return;
                }

                // calculate spring force
                const f = -(position - target) * stiffness;

                // update velocity by adding force based on mass
                velocity += f / mass;

                // update position by adding velocity
                position += velocity;

                // slow down based on amount of damping
                velocity *= damping;

                // we've arrived if we're near target and our velocity is near zero
                if (thereYet(position, target, velocity) || skipToEndState) {
                    position = target;
                    velocity = 0;
                    resting = true;

                    // we done
                    api.onupdate(position);
                    api.oncomplete(position);
                } else {
                    // progress update
                    api.onupdate(position);
                }
            };

            /**
             * Set new target value
             * @param value
             */
            const setTarget = value => {
                // if currently has no position, set target and position to this value
                if (isNumber(value) && !isNumber(position)) {
                    position = value;
                }

                // next target value will not be animated to
                if (target === null) {
                    target = value;
                    position = value;
                }

                // let start moving to target
                target = value;

                // already at target
                if (position === target || typeof target === 'undefined') {
                    // now resting as target is current position, stop moving
                    resting = true;
                    velocity = 0;

                    // done!
                    api.onupdate(position);
                    api.oncomplete(position);

                    return;
                }

                resting = false;
            };

            // need 'api' to call onupdate callback
            const api = createObject({
                interpolate,
                target: {
                    set: setTarget,
                    get: () => target,
                },
                resting: {
                    get: () => resting,
                },
                onupdate: value => {},
                oncomplete: value => {},
            });

            return api;
        };

const easeLinear = t => t;
const easeInOutQuad = t => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t);

const tween =
    // default values
    ({ duration = 500, easing = easeInOutQuad, delay = 0 } = {}) =>
        // method definition
        {
            let start = null;
            let t;
            let p;
            let resting = true;
            let reverse = false;
            let target = null;

            const interpolate = (ts, skipToEndState) => {
                if (resting || target === null) return;

                if (start === null) {
                    start = ts;
                }

                if (ts - start < delay) return;

                t = ts - start - delay;

                if (t >= duration || skipToEndState) {
                    t = 1;
                    p = reverse ? 0 : 1;
                    api.onupdate(p * target);
                    api.oncomplete(p * target);
                    resting = true;
                } else {
                    p = t / duration;
                    api.onupdate((t >= 0 ? easing(reverse ? 1 - p : p) : 0) * target);
                }
            };

            // need 'api' to call onupdate callback
            const api = createObject({
                interpolate,
                target: {
                    get: () => (reverse ? 0 : target),
                    set: value => {
                        // is initial value
                        if (target === null) {
                            target = value;
                            api.onupdate(value);
                            api.oncomplete(value);
                            return;
                        }

                        // want to tween to a smaller value and have a current value
                        if (value < target) {
                            target = 1;
                            reverse = true;
                        } else {
                            // not tweening to a smaller value
                            reverse = false;
                            target = value;
                        }

                        // let's go!
                        resting = false;
                        start = null;
                    },
                },
                resting: {
                    get: () => resting,
                },
                onupdate: value => {},
                oncomplete: value => {},
            });

            return api;
        };

const animator = {
    spring,
    tween,
};

/*
 { type: 'spring', stiffness: .5, damping: .75, mass: 10 };
 { translation: { type: 'spring', ... }, ... }
 { translation: { x: { type: 'spring', ... } } }
*/
const createAnimator = (definition, category, property) => {
    // default is single definition
    // we check if transform is set, if so, we check if property is set
    const def =
        definition[category] && typeof definition[category][property] === 'object'
            ? definition[category][property]
            : definition[category] || definition;

    const type = typeof def === 'string' ? def : def.type;
    const props = typeof def === 'object' ? { ...def } : {};

    return animator[type] ? animator[type](props) : null;
};

const addGetSet = (keys, obj, props, overwrite = false) => {
    obj = Array.isArray(obj) ? obj : [obj];
    obj.forEach(o => {
        keys.forEach(key => {
            let name = key;
            let getter = () => props[key];
            let setter = value => (props[key] = value);

            if (typeof key === 'object') {
                name = key.key;
                getter = key.getter || getter;
                setter = key.setter || setter;
            }

            if (o[name] && !overwrite) {
                return;
            }

            o[name] = {
                get: getter,
                set: setter,
            };
        });
    });
};

// add to state,
// add getters and setters to internal and external api (if not set)
// setup animators

const animations = ({ mixinConfig, viewProps, viewInternalAPI, viewExternalAPI }) => {
    // initial properties
    const initialProps = { ...viewProps };

    // list of all active animations
    const animations = [];

    // setup animators
    forin(mixinConfig, (property, animation) => {
        const animator = createAnimator(animation);
        if (!animator) {
            return;
        }

        // when the animator updates, update the view state value
        animator.onupdate = value => {
            viewProps[property] = value;
        };

        // set animator target
        animator.target = initialProps[property];

        // when value is set, set the animator target value
        const prop = {
            key: property,
            setter: value => {
                // if already at target, we done!
                if (animator.target === value) {
                    return;
                }

                animator.target = value;
            },
            getter: () => viewProps[property],
        };

        // add getters and setters
        addGetSet([prop], [viewInternalAPI, viewExternalAPI], viewProps, true);

        // add it to the list for easy updating from the _write method
        animations.push(animator);
    });

    // expose internal write api
    return {
        write: ts => {
            let skipToEndState = document.hidden;
            let resting = true;
            animations.forEach(animation => {
                if (!animation.resting) resting = false;
                animation.interpolate(ts, skipToEndState);
            });
            return resting;
        },
        destroy: () => {},
    };
};

const addEvent = element => (type, fn) => {
    element.addEventListener(type, fn);
};

const removeEvent = element => (type, fn) => {
    element.removeEventListener(type, fn);
};

// mixin
const listeners = ({
    mixinConfig,
    viewProps,
    viewInternalAPI,
    viewExternalAPI,
    viewState,
    view,
}) => {
    const events = [];

    const add = addEvent(view.element);
    const remove = removeEvent(view.element);

    viewExternalAPI.on = (type, fn) => {
        events.push({
            type,
            fn,
        });
        add(type, fn);
    };

    viewExternalAPI.off = (type, fn) => {
        events.splice(events.findIndex(event => event.type === type && event.fn === fn), 1);
        remove(type, fn);
    };

    return {
        write: () => {
            // not busy
            return true;
        },
        destroy: () => {
            events.forEach(event => {
                remove(event.type, event.fn);
            });
        },
    };
};

// add to external api and link to props

const apis = ({ mixinConfig, viewProps, viewExternalAPI }) => {
    addGetSet(mixinConfig, viewExternalAPI, viewProps);
};

const isDefined = value => value != null;

// add to state,
// add getters and setters to internal and external api (if not set)
// set initial state based on props in viewProps
// apply as transforms each frame

const defaults = {
    opacity: 1,
    scaleX: 1,
    scaleY: 1,
    translateX: 0,
    translateY: 0,
    rotateX: 0,
    rotateY: 0,
    rotateZ: 0,
    originX: 0,
    originY: 0,
};

const styles = ({ mixinConfig, viewProps, viewInternalAPI, viewExternalAPI, view }) => {
    // initial props
    const initialProps = { ...viewProps };

    // current props
    const currentProps = {};

    // we will add those properties to the external API and link them to the viewState
    addGetSet(mixinConfig, [viewInternalAPI, viewExternalAPI], viewProps);

    // override rect on internal and external rect getter so it takes in account transforms
    const getOffset = () => [viewProps['translateX'] || 0, viewProps['translateY'] || 0];
    const getScale = () => [viewProps['scaleX'] || 0, viewProps['scaleY'] || 0];
    const getRect = () =>
        view.rect ? getViewRect(view.rect, view.childViews, getOffset(), getScale()) : null;
    viewInternalAPI.rect = { get: getRect };
    viewExternalAPI.rect = { get: getRect };

    // apply view props
    mixinConfig.forEach(key => {
        viewProps[key] =
            typeof initialProps[key] === 'undefined' ? defaults[key] : initialProps[key];
    });

    // expose api
    return {
        write: () => {
            // see if props have changed
            if (!propsHaveChanged(currentProps, viewProps)) {
                return;
            }

            // moves element to correct position on screen
            applyStyles(view.element, viewProps);

            // store new transforms
            Object.assign(currentProps, { ...viewProps });

            // no longer busy
            return true;
        },
        destroy: () => {},
    };
};

const propsHaveChanged = (currentProps, newProps) => {
    // different amount of keys
    if (Object.keys(currentProps).length !== Object.keys(newProps).length) {
        return true;
    }

    // lets analyze the individual props
    for (const prop in newProps) {
        if (newProps[prop] !== currentProps[prop]) {
            return true;
        }
    }

    return false;
};

const applyStyles = (
    element,
    {
        opacity,
        perspective,
        translateX,
        translateY,
        scaleX,
        scaleY,
        rotateX,
        rotateY,
        rotateZ,
        originX,
        originY,
        width,
        height,
    }
) => {
    let transforms = '';
    let styles = '';

    // handle transform origin
    if (isDefined(originX) || isDefined(originY)) {
        styles += `transform-origin: ${originX || 0}px ${originY || 0}px;`;
    }

    // transform order is relevant
    // 0. perspective
    if (isDefined(perspective)) {
        transforms += `perspective(${perspective}px) `;
    }

    // 1. translate
    if (isDefined(translateX) || isDefined(translateY)) {
        transforms += `translate3d(${translateX || 0}px, ${translateY || 0}px, 0) `;
    }

    // 2. scale
    if (isDefined(scaleX) || isDefined(scaleY)) {
        transforms += `scale3d(${isDefined(scaleX) ? scaleX : 1}, ${
            isDefined(scaleY) ? scaleY : 1
        }, 1) `;
    }

    // 3. rotate
    if (isDefined(rotateZ)) {
        transforms += `rotateZ(${rotateZ}rad) `;
    }

    if (isDefined(rotateX)) {
        transforms += `rotateX(${rotateX}rad) `;
    }

    if (isDefined(rotateY)) {
        transforms += `rotateY(${rotateY}rad) `;
    }

    // add transforms
    if (transforms.length) {
        styles += `transform:${transforms};`;
    }

    // add opacity
    if (isDefined(opacity)) {
        styles += `opacity:${opacity};`;

        // if we reach zero, we make the element inaccessible
        if (opacity === 0) {
            styles += `visibility:hidden;`;
        }

        // if we're below 100% opacity this element can't be clicked
        if (opacity < 1) {
            styles += `pointer-events:none;`;
        }
    }

    // add height
    if (isDefined(height)) {
        styles += `height:${height}px;`;
    }

    // add width
    if (isDefined(width)) {
        styles += `width:${width}px;`;
    }

    // apply styles
    const elementCurrentStyle = element.elementCurrentStyle || '';

    // if new styles does not match current styles, lets update!
    if (styles.length !== elementCurrentStyle.length || styles !== elementCurrentStyle) {
        element.style.cssText = styles;
        // store current styles so we can compare them to new styles later on
        // _not_ getting the style value is faster
        element.elementCurrentStyle = styles;
    }
};

const Mixins = {
    styles,
    listeners,
    animations,
    apis,
};

const updateRect = (rect = {}, element = {}, style = {}) => {
    if (!element.layoutCalculated) {
        rect.paddingTop = parseInt(style.paddingTop, 10) || 0;
        rect.marginTop = parseInt(style.marginTop, 10) || 0;
        rect.marginRight = parseInt(style.marginRight, 10) || 0;
        rect.marginBottom = parseInt(style.marginBottom, 10) || 0;
        rect.marginLeft = parseInt(style.marginLeft, 10) || 0;
        element.layoutCalculated = true;
    }

    rect.left = element.offsetLeft || 0;
    rect.top = element.offsetTop || 0;
    rect.width = element.offsetWidth || 0;
    rect.height = element.offsetHeight || 0;

    rect.right = rect.left + rect.width;
    rect.bottom = rect.top + rect.height;

    rect.scrollTop = element.scrollTop;

    rect.hidden = element.offsetParent === null;

    return rect;
};

const createView =
    // default view definition
    ({
        // element definition
        tag = 'div',
        name = null,
        attributes = {},

        // view interaction
        read = () => {},
        write = () => {},
        create = () => {},
        destroy = () => {},

        // hooks
        filterFrameActionsForChild = (child, actions) => actions,
        didCreateView = () => {},
        didWriteView = () => {},

        // rect related
        ignoreRect = false,
        ignoreRectUpdate = false,

        // mixins
        mixins = [],
    } = {}) => (
        // each view requires reference to store
        store,
        // specific properties for this view
        props = {}
    ) => {
        // root element should not be changed
        const element = createElement(tag, `filepond--${name}`, attributes);

        // style reference should also not be changed
        const style = window.getComputedStyle(element, null);

        // element rectangle
        const rect = updateRect();
        let frameRect = null;

        // rest state
        let isResting = false;

        // pretty self explanatory
        const childViews = [];

        // loaded mixins
        const activeMixins = [];

        // references to created children
        const ref = {};

        // state used for each instance
        const state = {};

        // list of writers that will be called to update this view
        const writers = [
            write, // default writer
        ];

        const readers = [
            read, // default reader
        ];

        const destroyers = [
            destroy, // default destroy
        ];

        // core view methods
        const getElement = () => element;
        const getChildViews = () => childViews.concat();
        const getReference = () => ref;
        const createChildView = store => (view, props) => view(store, props);
        const getRect = () => {
            if (frameRect) {
                return frameRect;
            }
            frameRect = getViewRect(rect, childViews, [0, 0], [1, 1]);
            return frameRect;
        };
        const getStyle = () => style;

        /**
         * Read data from DOM
         * @private
         */
        const _read = () => {
            frameRect = null;

            // read child views
            childViews.forEach(child => child._read());

            const shouldUpdate = !(ignoreRectUpdate && rect.width && rect.height);
            if (shouldUpdate) {
                updateRect(rect, element, style);
            }

            // readers
            const api = { root: internalAPI, props, rect };
            readers.forEach(reader => reader(api));
        };

        /**
         * Write data to DOM
         * @private
         */
        const _write = (ts, frameActions, shouldOptimize) => {
            // if no actions, we assume that the view is resting
            let resting = frameActions.length === 0;

            // writers
            writers.forEach(writer => {
                const writerResting = writer({
                    props,
                    root: internalAPI,
                    actions: frameActions,
                    timestamp: ts,
                    shouldOptimize,
                });
                if (writerResting === false) {
                    resting = false;
                }
            });

            // run mixins
            activeMixins.forEach(mixin => {
                // if one of the mixins is still busy after write operation, we are not resting
                const mixinResting = mixin.write(ts);
                if (mixinResting === false) {
                    resting = false;
                }
            });

            // updates child views that are currently attached to the DOM
            childViews
                .filter(child => !!child.element.parentNode)
                .forEach(child => {
                    // if a child view is not resting, we are not resting
                    const childResting = child._write(
                        ts,
                        filterFrameActionsForChild(child, frameActions),
                        shouldOptimize
                    );
                    if (!childResting) {
                        resting = false;
                    }
                });

            // append new elements to DOM and update those
            childViews
                //.filter(child => !child.element.parentNode)
                .forEach((child, index) => {
                    // skip
                    if (child.element.parentNode) {
                        return;
                    }

                    // append to DOM
                    internalAPI.appendChild(child.element, index);

                    // call read (need to know the size of these elements)
                    child._read();

                    // re-call write
                    child._write(
                        ts,
                        filterFrameActionsForChild(child, frameActions),
                        shouldOptimize
                    );

                    // we just added somthing to the dom, no rest
                    resting = false;
                });

            // update resting state
            isResting = resting;

            didWriteView({
                props,
                root: internalAPI,
                actions: frameActions,
                timestamp: ts,
            });

            // let parent know if we are resting
            return resting;
        };

        const _destroy = () => {
            activeMixins.forEach(mixin => mixin.destroy());
            destroyers.forEach(destroyer => {
                destroyer({ root: internalAPI, props });
            });
            childViews.forEach(child => child._destroy());
        };

        // sharedAPI
        const sharedAPIDefinition = {
            element: {
                get: getElement,
            },
            style: {
                get: getStyle,
            },
            childViews: {
                get: getChildViews,
            },
        };

        // private API definition
        const internalAPIDefinition = {
            ...sharedAPIDefinition,
            rect: {
                get: getRect,
            },

            // access to custom children references
            ref: {
                get: getReference,
            },

            // dom modifiers
            is: needle => name === needle,
            appendChild: appendChild(element),
            createChildView: createChildView(store),
            linkView: view => {
                childViews.push(view);
                return view;
            },
            unlinkView: view => {
                childViews.splice(childViews.indexOf(view), 1);
            },
            appendChildView: appendChildView(element, childViews),
            removeChildView: removeChildView(element, childViews),
            registerWriter: writer => writers.push(writer),
            registerReader: reader => readers.push(reader),
            registerDestroyer: destroyer => destroyers.push(destroyer),
            invalidateLayout: () => (element.layoutCalculated = false),

            // access to data store
            dispatch: store.dispatch,
            query: store.query,
        };

        // public view API methods
        const externalAPIDefinition = {
            element: {
                get: getElement,
            },
            childViews: {
                get: getChildViews,
            },
            rect: {
                get: getRect,
            },
            resting: {
                get: () => isResting,
            },
            isRectIgnored: () => ignoreRect,
            _read,
            _write,
            _destroy,
        };

        // mixin API methods
        const mixinAPIDefinition = {
            ...sharedAPIDefinition,
            rect: {
                get: () => rect,
            },
        };

        // add mixin functionality
        Object.keys(mixins)
            .sort((a, b) => {
                // move styles to the back of the mixin list (so adjustments of other mixins are applied to the props correctly)
                if (a === 'styles') {
                    return 1;
                } else if (b === 'styles') {
                    return -1;
                }
                return 0;
            })
            .forEach(key => {
                const mixinAPI = Mixins[key]({
                    mixinConfig: mixins[key],
                    viewProps: props,
                    viewState: state,
                    viewInternalAPI: internalAPIDefinition,
                    viewExternalAPI: externalAPIDefinition,
                    view: createObject(mixinAPIDefinition),
                });

                if (mixinAPI) {
                    activeMixins.push(mixinAPI);
                }
            });

        // construct private api
        const internalAPI = createObject(internalAPIDefinition);

        // create the view
        create({
            root: internalAPI,
            props,
        });

        // append created child views to root node
        const childCount = getChildCount(element); // need to know the current child count so appending happens in correct order
        childViews.forEach((child, index) => {
            internalAPI.appendChild(child.element, childCount + index);
        });

        // call did create
        didCreateView(internalAPI);

        // expose public api
        return createObject(externalAPIDefinition);
    };

const createPainter = (read, write, fps = 60) => {
    const name = '__framePainter';

    // set global painter
    if (window[name]) {
        window[name].readers.push(read);
        window[name].writers.push(write);
        return;
    }

    window[name] = {
        readers: [read],
        writers: [write],
    };

    const painter = window[name];

    const interval = 1000 / fps;
    let last = null;
    let id = null;
    let requestTick = null;
    let cancelTick = null;

    const setTimerType = () => {
        if (document.hidden) {
            requestTick = () => window.setTimeout(() => tick(performance.now()), interval);
            cancelTick = () => window.clearTimeout(id);
        } else {
            requestTick = () => window.requestAnimationFrame(tick);
            cancelTick = () => window.cancelAnimationFrame(id);
        }
    };

    document.addEventListener('visibilitychange', () => {
        if (cancelTick) cancelTick();
        setTimerType();
        tick(performance.now());
    });

    const tick = ts => {
        // queue next tick
        id = requestTick(tick);

        // limit fps
        if (!last) {
            last = ts;
        }

        const delta = ts - last;

        if (delta <= interval) {
            // skip frame
            return;
        }

        // align next frame
        last = ts - (delta % interval);

        // update view
        painter.readers.forEach(read => read());
        painter.writers.forEach(write => write(ts));
    };

    setTimerType();
    tick(performance.now());

    return {
        pause: () => {
            cancelTick(id);
        },
    };
};

const createRoute = (routes, fn) => ({ root, props, actions = [], timestamp, shouldOptimize }) => {
    actions
        .filter(action => routes[action.type])
        .forEach(action =>
            routes[action.type]({ root, props, action: action.data, timestamp, shouldOptimize })
        );
    if (fn) {
        fn({ root, props, actions, timestamp, shouldOptimize });
    }
};

const insertBefore = (newNode, referenceNode) =>
    referenceNode.parentNode.insertBefore(newNode, referenceNode);

const insertAfter = (newNode, referenceNode) => {
    return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
};

const isArray = value => Array.isArray(value);

const isEmpty = value => value == null;

const trim = str => str.trim();

const toString = value => '' + value;

const toArray = (value, splitter = ',') => {
    if (isEmpty(value)) {
        return [];
    }
    if (isArray(value)) {
        return value;
    }
    return toString(value)
        .split(splitter)
        .map(trim)
        .filter(str => str.length);
};

const isBoolean = value => typeof value === 'boolean';

const toBoolean = value => (isBoolean(value) ? value : value === 'true');

const isString = value => typeof value === 'string';

const toNumber = value =>
    isNumber(value) ? value : isString(value) ? toString(value).replace(/[a-z]+/gi, '') : 0;

const toInt = value => parseInt(toNumber(value), 10);

const toFloat = value => parseFloat(toNumber(value));

const isInt = value => isNumber(value) && isFinite(value) && Math.floor(value) === value;

const toBytes = (value, base = 1000) => {
    // is in bytes
    if (isInt(value)) {
        return value;
    }

    // is natural file size
    let naturalFileSize = toString(value).trim();

    // if is value in megabytes
    if (/MB$/i.test(naturalFileSize)) {
        naturalFileSize = naturalFileSize.replace(/MB$i/, '').trim();
        return toInt(naturalFileSize) * base * base;
    }

    // if is value in kilobytes
    if (/KB/i.test(naturalFileSize)) {
        naturalFileSize = naturalFileSize.replace(/KB$i/, '').trim();
        return toInt(naturalFileSize) * base;
    }

    return toInt(naturalFileSize);
};

const isFunction = value => typeof value === 'function';

const toFunctionReference = string => {
    let ref = self;
    let levels = string.split('.');
    let level = null;
    while ((level = levels.shift())) {
        ref = ref[level];
        if (!ref) {
            return null;
        }
    }
    return ref;
};

const methods = {
    process: 'POST',
    patch: 'PATCH',
    revert: 'DELETE',
    fetch: 'GET',
    restore: 'GET',
    load: 'GET',
};

const createServerAPI = outline => {
    const api = {};

    api.url = isString(outline) ? outline : outline.url || '';
    api.timeout = outline.timeout ? parseInt(outline.timeout, 10) : 0;
    api.headers = outline.headers ? outline.headers : {};

    forin(methods, key => {
        api[key] = createAction(key, outline[key], methods[key], api.timeout, api.headers);
    });

    // remove process if no url or process on outline
    api.process = outline.process || isString(outline) || outline.url ? api.process : null;

    // special treatment for remove
    api.remove = outline.remove || null;

    // remove generic headers from api object
    delete api.headers;

    return api;
};

const createAction = (name, outline, method, timeout, headers) => {
    // is explicitely set to null so disable
    if (outline === null) {
        return null;
    }

    // if is custom function, done! Dev handles everything.
    if (typeof outline === 'function') {
        return outline;
    }

    // build action object
    const action = {
        url: method === 'GET' || method === 'PATCH' ? `?${name}=` : '',
        method,
        headers,
        withCredentials: false,
        timeout,
        onload: null,
        ondata: null,
        onerror: null,
    };

    // is a single url
    if (isString(outline)) {
        action.url = outline;
        return action;
    }

    // overwrite
    Object.assign(action, outline);

    // see if should reformat headers;
    if (isString(action.headers)) {
        const parts = action.headers.split(/:(.+)/);
        action.headers = {
            header: parts[0],
            value: parts[1],
        };
    }

    // if is bool withCredentials
    action.withCredentials = toBoolean(action.withCredentials);

    return action;
};

const toServerAPI = value => createServerAPI(value);

const isNull = value => value === null;

const isObject = value => typeof value === 'object' && value !== null;

const isAPI = value => {
    return (
        isObject(value) &&
        isString(value.url) &&
        isObject(value.process) &&
        isObject(value.revert) &&
        isObject(value.restore) &&
        isObject(value.fetch)
    );
};

const getType = value => {
    if (isArray(value)) {
        return 'array';
    }

    if (isNull(value)) {
        return 'null';
    }

    if (isInt(value)) {
        return 'int';
    }

    if (/^[0-9]+ ?(?:GB|MB|KB)$/gi.test(value)) {
        return 'bytes';
    }

    if (isAPI(value)) {
        return 'api';
    }

    return typeof value;
};

const replaceSingleQuotes = str =>
    str
        .replace(/{\s*'/g, '{"')
        .replace(/'\s*}/g, '"}')
        .replace(/'\s*:/g, '":')
        .replace(/:\s*'/g, ':"')
        .replace(/,\s*'/g, ',"')
        .replace(/'\s*,/g, '",');

const conversionTable = {
    array: toArray,
    boolean: toBoolean,
    int: value => (getType(value) === 'bytes' ? toBytes(value) : toInt(value)),
    number: toFloat,
    float: toFloat,
    bytes: toBytes,
    string: value => (isFunction(value) ? value : toString(value)),
    function: value => toFunctionReference(value),
    serverapi: toServerAPI,
    object: value => {
        try {
            return JSON.parse(replaceSingleQuotes(value));
        } catch (e) {
            return null;
        }
    },
};

const convertTo = (value, type) => conversionTable[type](value);

const getValueByType = (newValue, defaultValue, valueType) => {
    // can always assign default value
    if (newValue === defaultValue) {
        return newValue;
    }

    // get the type of the new value
    let newValueType = getType(newValue);

    // is valid type?
    if (newValueType !== valueType) {
        // is string input, let's attempt to convert
        const convertedValue = convertTo(newValue, valueType);

        // what is the type now
        newValueType = getType(convertedValue);

        // no valid conversions found
        if (convertedValue === null) {
            throw `Trying to assign value with incorrect type to "${option}", allowed type: "${valueType}"`;
        } else {
            newValue = convertedValue;
        }
    }

    // assign new value
    return newValue;
};

const createOption = (defaultValue, valueType) => {
    let currentValue = defaultValue;
    return {
        enumerable: true,
        get: () => currentValue,
        set: newValue => {
            currentValue = getValueByType(newValue, defaultValue, valueType);
        },
    };
};

const createOptions = options => {
    const obj = {};
    forin(options, prop => {
        const optionDefinition = options[prop];
        obj[prop] = createOption(optionDefinition[0], optionDefinition[1]);
    });
    return createObject(obj);
};

const createInitialState = options => ({
    // model
    items: [],

    // timeout used for calling update items
    listUpdateTimeout: null,

    // timeout used for stacking metadata updates
    itemUpdateTimeout: null,

    // queue of items waiting to be processed
    processingQueue: [],

    // options
    options: createOptions(options),
});

const fromCamels = (string, separator = '-') =>
    string
        .split(/(?=[A-Z])/)
        .map(part => part.toLowerCase())
        .join(separator);

const createOptionAPI = (store, options) => {
    const obj = {};
    forin(options, key => {
        obj[key] = {
            get: () => store.getState().options[key],
            set: value => {
                store.dispatch(`SET_${fromCamels(key, '_').toUpperCase()}`, {
                    value,
                });
            },
        };
    });
    return obj;
};

const createOptionActions = options => (dispatch, query, state) => {
    const obj = {};
    forin(options, key => {
        const name = fromCamels(key, '_').toUpperCase();

        obj[`SET_${name}`] = action => {
            try {
                state.options[key] = action.value;
            } catch (e) {
                // nope, failed
            }

            // we successfully set the value of this option
            dispatch(`DID_SET_${name}`, { value: state.options[key] });
        };
    });
    return obj;
};

const createOptionQueries = options => state => {
    const obj = {};
    forin(options, key => {
        obj[`GET_${fromCamels(key, '_').toUpperCase()}`] = action => state.options[key];
    });
    return obj;
};

const InteractionMethod = {
    API: 1,
    DROP: 2,
    BROWSE: 3,
    PASTE: 4,
    NONE: 5,
};

const getUniqueId = () =>
    Math.random()
        .toString(36)
        .substring(2, 11);

const arrayRemove = (arr, index) => arr.splice(index, 1);

const run = (cb, sync) => {
    if (sync) {
        cb();
    } else if (document.hidden) {
        Promise.resolve(1).then(cb);
    } else {
        setTimeout(cb, 0);
    }
};

const on = () => {
    const listeners = [];
    const off = (event, cb) => {
        arrayRemove(
            listeners,
            listeners.findIndex(listener => listener.event === event && (listener.cb === cb || !cb))
        );
    };
    const fire = (event, args, sync) => {
        listeners
            .filter(listener => listener.event === event)
            .map(listener => listener.cb)
            .forEach(cb => run(() => cb(...args), sync));
    };
    return {
        fireSync: (event, ...args) => {
            fire(event, args, true);
        },
        fire: (event, ...args) => {
            fire(event, args, false);
        },
        on: (event, cb) => {
            listeners.push({ event, cb });
        },
        onOnce: (event, cb) => {
            listeners.push({
                event,
                cb: (...args) => {
                    off(event, cb);
                    cb(...args);
                },
            });
        },
        off,
    };
};

const copyObjectPropertiesToObject = (src, target, excluded) => {
    Object.getOwnPropertyNames(src)
        .filter(property => !excluded.includes(property))
        .forEach(key =>
            Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(src, key))
        );
};

const PRIVATE = [
    'fire',
    'process',
    'revert',
    'load',
    'on',
    'off',
    'onOnce',
    'retryLoad',
    'extend',
    'archive',
    'archived',
    'release',
    'released',
    'requestProcessing',
    'freeze',
];

const createItemAPI = item => {
    const api = {};
    copyObjectPropertiesToObject(item, api, PRIVATE);
    return api;
};

const removeReleasedItems = items => {
    items.forEach((item, index) => {
        if (item.released) {
            arrayRemove(items, index);
        }
    });
};

const ItemStatus = {
    INIT: 1,
    IDLE: 2,
    PROCESSING_QUEUED: 9,
    PROCESSING: 3,
    PROCESSING_COMPLETE: 5,
    PROCESSING_ERROR: 6,
    PROCESSING_REVERT_ERROR: 10,
    LOADING: 7,
    LOAD_ERROR: 8,
};

const FileOrigin = {
    INPUT: 1,
    LIMBO: 2,
    LOCAL: 3,
};

const getNonNumeric = str => /[^0-9]+/.exec(str);

const getDecimalSeparator = () => getNonNumeric((1.1).toLocaleString())[0];

const getThousandsSeparator = () => {
    // Added for browsers that do not return the thousands separator (happend on native browser Android 4.4.4)
    // We check against the normal toString output and if they're the same return a comma when decimal separator is a dot
    const decimalSeparator = getDecimalSeparator();
    const thousandsStringWithSeparator = (1000.0).toLocaleString();
    const thousandsStringWithoutSeparator = (1000.0).toString();
    if (thousandsStringWithSeparator !== thousandsStringWithoutSeparator) {
        return getNonNumeric(thousandsStringWithSeparator)[0];
    }
    return decimalSeparator === '.' ? ',' : '.';
};

const Type = {
    BOOLEAN: 'boolean',
    INT: 'int',
    NUMBER: 'number',
    STRING: 'string',
    ARRAY: 'array',
    OBJECT: 'object',
    FUNCTION: 'function',
    ACTION: 'action',
    SERVER_API: 'serverapi',
    REGEX: 'regex',
};

// all registered filters
const filters = [];

// loops over matching filters and passes options to each filter, returning the mapped results
const applyFilterChain = (key, value, utils) =>
    new Promise((resolve, reject) => {
        // find matching filters for this key
        const matchingFilters = filters.filter(f => f.key === key).map(f => f.cb);

        // resolve now
        if (matchingFilters.length === 0) {
            resolve(value);
            return;
        }

        // first filter to kick things of
        const initialFilter = matchingFilters.shift();

        // chain filters
        matchingFilters
            .reduce(
                // loop over promises passing value to next promise
                (current, next) => current.then(value => next(value, utils)),

                // call initial filter, will return a promise
                initialFilter(value, utils)

                // all executed
            )
            .then(value => resolve(value))
            .catch(error => reject(error));
    });

const applyFilters = (key, value, utils) =>
    filters.filter(f => f.key === key).map(f => f.cb(value, utils));

// adds a new filter to the list
const addFilter = (key, cb) => filters.push({ key, cb });

const extendDefaultOptions = additionalOptions => Object.assign(defaultOptions, additionalOptions);

const getOptions = () => ({ ...defaultOptions });

const setOptions = opts => {
    forin(opts, (key, value) => {
        // key does not exist, so this option cannot be set
        if (!defaultOptions[key]) {
            return;
        }
        defaultOptions[key][0] = getValueByType(
            value,
            defaultOptions[key][0],
            defaultOptions[key][1]
        );
    });
};

// default options on app
const defaultOptions = {
    // the id to add to the root element
    id: [null, Type.STRING],

    // input field name to use
    name: ['filepond', Type.STRING],

    // disable the field
    disabled: [false, Type.BOOLEAN],

    // classname to put on wrapper
    className: [null, Type.STRING],

    // is the field required
    required: [false, Type.BOOLEAN],

    // Allow media capture when value is set
    captureMethod: [null, Type.STRING],
    // - "camera", "microphone" or "camcorder",
    // - Does not work with multiple on apple devices
    // - If set, acceptedFileTypes must be made to match with media wildcard "image/*", "audio/*" or "video/*"

    // sync `acceptedFileTypes` property with `accept` attribute
    allowSyncAcceptAttribute: [true, Type.BOOLEAN],

    // Feature toggles
    allowDrop: [true, Type.BOOLEAN], // Allow dropping of files
    allowBrowse: [true, Type.BOOLEAN], // Allow browsing the file system
    allowPaste: [true, Type.BOOLEAN], // Allow pasting files
    allowMultiple: [false, Type.BOOLEAN], // Allow multiple files (disabled by default, as multiple attribute is also required on input to allow multiple)
    allowReplace: [true, Type.BOOLEAN], // Allow dropping a file on other file to replace it (only works when multiple is set to false)
    allowRevert: [true, Type.BOOLEAN], // Allows user to revert file upload
    allowRemove: [true, Type.BOOLEAN], // Allow user to remove a file
    allowProcess: [true, Type.BOOLEAN], // Allows user to process a file, when set to false, this removes the file upload button
    allowReorder: [false, Type.BOOLEAN], // Allow reordering of files
    allowDirectoriesOnly: [false, Type.BOOLEAN], // Allow only selecting directories with browse (no support for filtering dnd at this point)

    // Try store file if `server` not set
    storeAsFile: [false, Type.BOOLEAN],

    // Revert mode
    forceRevert: [false, Type.BOOLEAN], // Set to 'force' to require the file to be reverted before removal

    // Input requirements
    maxFiles: [null, Type.INT], // Max number of files
    checkValidity: [false, Type.BOOLEAN], // Enables custom validity messages

    // Where to put file
    itemInsertLocationFreedom: [true, Type.BOOLEAN], // Set to false to always add items to begin or end of list
    itemInsertLocation: ['before', Type.STRING], // Default index in list to add items that have been dropped at the top of the list
    itemInsertInterval: [75, Type.INT],

    // Drag 'n Drop related
    dropOnPage: [false, Type.BOOLEAN], // Allow dropping of files anywhere on page (prevents browser from opening file if dropped outside of Up)
    dropOnElement: [true, Type.BOOLEAN], // Drop needs to happen on element (set to false to also load drops outside of Up)
    dropValidation: [false, Type.BOOLEAN], // Enable or disable validating files on drop
    ignoredFiles: [['.ds_store', 'thumbs.db', 'desktop.ini'], Type.ARRAY],

    // Upload related
    instantUpload: [true, Type.BOOLEAN], // Should upload files immediately on drop
    maxParallelUploads: [2, Type.INT], // Maximum files to upload in parallel
    allowMinimumUploadDuration: [true, Type.BOOLEAN], // if true uploads take at least 750 ms, this ensures the user sees the upload progress giving trust the upload actually happened

    // Chunks
    chunkUploads: [false, Type.BOOLEAN], // Enable chunked uploads
    chunkForce: [false, Type.BOOLEAN], // Force use of chunk uploads even for files smaller than chunk size
    chunkSize: [5000000, Type.INT], // Size of chunks (5MB default)
    chunkRetryDelays: [[500, 1000, 3000], Type.ARRAY], // Amount of times to retry upload of a chunk when it fails

    // The server api end points to use for uploading (see docs)
    server: [null, Type.SERVER_API],

    // File size calculations, can set to 1024, this is only used for display, properties use file size base 1000
    fileSizeBase: [1000, Type.INT],

    // Labels and status messages
    labelFileSizeBytes: ['bytes', Type.STRING],
    labelFileSizeKilobytes: ['KB', Type.STRING],
    labelFileSizeMegabytes: ['MB', Type.STRING],
    labelFileSizeGigabytes: ['GB', Type.STRING],

    labelDecimalSeparator: [getDecimalSeparator(), Type.STRING], // Default is locale separator
    labelThousandsSeparator: [getThousandsSeparator(), Type.STRING], // Default is locale separator

    labelIdle: [
        'Drag & Drop your files or <span class="filepond--label-action">Browse</span>',
        Type.STRING,
    ],
    labelInvalidField: ['Field contains invalid files', Type.STRING],
    labelFileWaitingForSize: ['Waiting for size', Type.STRING],
    labelFileSizeNotAvailable: ['Size not available', Type.STRING],
    labelFileCountSingular: ['file in list', Type.STRING],
    labelFileCountPlural: ['files in list', Type.STRING],
    labelFileLoading: ['Loading', Type.STRING],
    labelFileAdded: ['Added', Type.STRING], // assistive only
    labelFileLoadError: ['Error during load', Type.STRING],
    labelFileRemoved: ['Removed', Type.STRING], // assistive only
    labelFileRemoveError: ['Error during remove', Type.STRING],
    labelFileProcessing: ['Uploading', Type.STRING],
    labelFileProcessingComplete: ['Upload complete', Type.STRING],
    labelFileProcessingAborted: ['Upload cancelled', Type.STRING],
    labelFileProcessingError: ['Error during upload', Type.STRING],
    labelFileProcessingRevertError: ['Error during revert', Type.STRING],

    labelTapToCancel: ['tap to cancel', Type.STRING],
    labelTapToRetry: ['tap to retry', Type.STRING],
    labelTapToUndo: ['tap to undo', Type.STRING],

    labelButtonRemoveItem: ['Remove', Type.STRING],
    labelButtonAbortItemLoad: ['Abort', Type.STRING],
    labelButtonRetryItemLoad: ['Retry', Type.STRING],
    labelButtonAbortItemProcessing: ['Cancel', Type.STRING],
    labelButtonUndoItemProcessing: ['Undo', Type.STRING],
    labelButtonRetryItemProcessing: ['Retry', Type.STRING],
    labelButtonProcessItem: ['Upload', Type.STRING],

    // make sure width and height plus viewpox are even numbers so icons are nicely centered
    iconRemove: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M11.586 13l-2.293 2.293a1 1 0 0 0 1.414 1.414L13 14.414l2.293 2.293a1 1 0 0 0 1.414-1.414L14.414 13l2.293-2.293a1 1 0 0 0-1.414-1.414L13 11.586l-2.293-2.293a1 1 0 0 0-1.414 1.414L11.586 13z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING,
    ],
    iconProcess: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M14 10.414v3.585a1 1 0 0 1-2 0v-3.585l-1.293 1.293a1 1 0 0 1-1.414-1.415l3-3a1 1 0 0 1 1.414 0l3 3a1 1 0 0 1-1.414 1.415L14 10.414zM9 18a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2H9z" fill="currentColor" fill-rule="evenodd"/></svg>',
        Type.STRING,
    ],
    iconRetry: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M10.81 9.185l-.038.02A4.997 4.997 0 0 0 8 13.683a5 5 0 0 0 5 5 5 5 0 0 0 5-5 1 1 0 0 1 2 0A7 7 0 1 1 9.722 7.496l-.842-.21a.999.999 0 1 1 .484-1.94l3.23.806c.535.133.86.675.73 1.21l-.804 3.233a.997.997 0 0 1-1.21.73.997.997 0 0 1-.73-1.21l.23-.928v-.002z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING,
    ],
    iconUndo: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M9.185 10.81l.02-.038A4.997 4.997 0 0 1 13.683 8a5 5 0 0 1 5 5 5 5 0 0 1-5 5 1 1 0 0 0 0 2A7 7 0 1 0 7.496 9.722l-.21-.842a.999.999 0 1 0-1.94.484l.806 3.23c.133.535.675.86 1.21.73l3.233-.803a.997.997 0 0 0 .73-1.21.997.997 0 0 0-1.21-.73l-.928.23-.002-.001z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING,
    ],
    iconDone: [
        '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M18.293 9.293a1 1 0 0 1 1.414 1.414l-7.002 7a1 1 0 0 1-1.414 0l-3.998-4a1 1 0 1 1 1.414-1.414L12 15.586l6.294-6.293z" fill="currentColor" fill-rule="nonzero"/></svg>',
        Type.STRING,
    ],

    // event handlers
    oninit: [null, Type.FUNCTION],
    onwarning: [null, Type.FUNCTION],
    onerror: [null, Type.FUNCTION],
    onactivatefile: [null, Type.FUNCTION],
    oninitfile: [null, Type.FUNCTION],
    onaddfilestart: [null, Type.FUNCTION],
    onaddfileprogress: [null, Type.FUNCTION],
    onaddfile: [null, Type.FUNCTION],
    onprocessfilestart: [null, Type.FUNCTION],
    onprocessfileprogress: [null, Type.FUNCTION],
    onprocessfileabort: [null, Type.FUNCTION],
    onprocessfilerevert: [null, Type.FUNCTION],
    onprocessfile: [null, Type.FUNCTION],
    onprocessfiles: [null, Type.FUNCTION],
    onremovefile: [null, Type.FUNCTION],
    onpreparefile: [null, Type.FUNCTION],
    onupdatefiles: [null, Type.FUNCTION],
    onreorderfiles: [null, Type.FUNCTION],

    // hooks
    beforeDropFile: [null, Type.FUNCTION],
    beforeAddFile: [null, Type.FUNCTION],
    beforeRemoveFile: [null, Type.FUNCTION],
    beforePrepareFile: [null, Type.FUNCTION],

    // styles
    stylePanelLayout: [null, Type.STRING], // null 'integrated', 'compact', 'circle'
    stylePanelAspectRatio: [null, Type.STRING], // null or '3:2' or 1
    styleItemPanelAspectRatio: [null, Type.STRING],
    styleButtonRemoveItemPosition: ['left', Type.STRING],
    styleButtonProcessItemPosition: ['right', Type.STRING],
    styleLoadIndicatorPosition: ['right', Type.STRING],
    styleProgressIndicatorPosition: ['right', Type.STRING],
    styleButtonRemoveItemAlign: [false, Type.BOOLEAN],

    // custom initial files array
    files: [[], Type.ARRAY],

    // show support by displaying credits
    credits: [['https://filepond.com', 'Powered by FilePond'], Type.ARRAY],
};

const getItemByQuery = (items, query) => {
    // just return first index
    if (isEmpty(query)) {
        return items[0] || null;
    }

    // query is index
    if (isInt(query)) {
        return items[query] || null;
    }

    // if query is item, get the id
    if (typeof query === 'object') {
        query = query.id;
    }

    // assume query is a string and return item by id
    return items.find(item => item.id === query) || null;
};

const getNumericAspectRatioFromString = aspectRatio => {
    if (isEmpty(aspectRatio)) {
        return aspectRatio;
    }
    if (/:/.test(aspectRatio)) {
        const parts = aspectRatio.split(':');
        return parts[1] / parts[0];
    }
    return parseFloat(aspectRatio);
};

const getActiveItems = items => items.filter(item => !item.archived);

const Status = {
    EMPTY: 0,
    IDLE: 1, // waiting
    ERROR: 2, // a file is in error state
    BUSY: 3, // busy processing or loading
    READY: 4, // all files uploaded
};

let res = null;
const canUpdateFileInput = () => {
    if (res === null) {
        try {
            const dataTransfer = new DataTransfer();
            dataTransfer.items.add(new File(['hello world'], 'This_Works.txt'));
            const el = document.createElement('input');
            el.setAttribute('type', 'file');
            el.files = dataTransfer.files;
            res = el.files.length === 1;
        } catch (err) {
            res = false;
        }
    }
    return res;
};

const ITEM_ERROR = [
    ItemStatus.LOAD_ERROR,
    ItemStatus.PROCESSING_ERROR,
    ItemStatus.PROCESSING_REVERT_ERROR,
];
const ITEM_BUSY = [
    ItemStatus.LOADING,
    ItemStatus.PROCESSING,
    ItemStatus.PROCESSING_QUEUED,
    ItemStatus.INIT,
];
const ITEM_READY = [ItemStatus.PROCESSING_COMPLETE];

const isItemInErrorState = item => ITEM_ERROR.includes(item.status);
const isItemInBusyState = item => ITEM_BUSY.includes(item.status);
const isItemInReadyState = item => ITEM_READY.includes(item.status);

const isAsync = state =>
    isObject(state.options.server) &&
    (isObject(state.options.server.process) || isFunction(state.options.server.process));

const queries = state => ({
    GET_STATUS: () => {
        const items = getActiveItems(state.items);

        const { EMPTY, ERROR, BUSY, IDLE, READY } = Status;

        if (items.length === 0) return EMPTY;

        if (items.some(isItemInErrorState)) return ERROR;

        if (items.some(isItemInBusyState)) return BUSY;

        if (items.some(isItemInReadyState)) return READY;

        return IDLE;
    },

    GET_ITEM: query => getItemByQuery(state.items, query),

    GET_ACTIVE_ITEM: query => getItemByQuery(getActiveItems(state.items), query),

    GET_ACTIVE_ITEMS: () => getActiveItems(state.items),

    GET_ITEMS: () => state.items,

    GET_ITEM_NAME: query => {
        const item = getItemByQuery(state.items, query);
        return item ? item.filename : null;
    },

    GET_ITEM_SIZE: query => {
        const item = getItemByQuery(state.items, query);
        return item ? item.fileSize : null;
    },

    GET_STYLES: () =>
        Object.keys(state.options)
            .filter(key => /^style/.test(key))
            .map(option => ({
                name: option,
                value: state.options[option],
            })),

    GET_PANEL_ASPECT_RATIO: () => {
        const isShapeCircle = /circle/.test(state.options.stylePanelLayout);
        const aspectRatio = isShapeCircle
            ? 1
            : getNumericAspectRatioFromString(state.options.stylePanelAspectRatio);
        return aspectRatio;
    },

    GET_ITEM_PANEL_ASPECT_RATIO: () => state.options.styleItemPanelAspectRatio,

    GET_ITEMS_BY_STATUS: status =>
        getActiveItems(state.items).filter(item => item.status === status),

    GET_TOTAL_ITEMS: () => getActiveItems(state.items).length,

    SHOULD_UPDATE_FILE_INPUT: () =>
        state.options.storeAsFile && canUpdateFileInput() && !isAsync(state),

    IS_ASYNC: () => isAsync(state),

    GET_FILE_SIZE_LABELS: query => ({
        labelBytes: query('GET_LABEL_FILE_SIZE_BYTES') || undefined,
        labelKilobytes: query('GET_LABEL_FILE_SIZE_KILOBYTES') || undefined,
        labelMegabytes: query('GET_LABEL_FILE_SIZE_MEGABYTES') || undefined,
        labelGigabytes: query('GET_LABEL_FILE_SIZE_GIGABYTES') || undefined,
    }),
});

const hasRoomForItem = state => {
    const count = getActiveItems(state.items).length;

    // if cannot have multiple items, to add one item it should currently not contain items
    if (!state.options.allowMultiple) {
        return count === 0;
    }

    // if allows multiple items, we check if a max item count has been set, if not, there's no limit
    const maxFileCount = state.options.maxFiles;
    if (maxFileCount === null) {
        return true;
    }

    // we check if the current count is smaller than the max count, if so, another file can still be added
    if (count < maxFileCount) {
        return true;
    }

    // no more room for another file
    return false;
};

const limit = (value, min, max) => Math.max(Math.min(max, value), min);

const arrayInsert = (arr, index, item) => arr.splice(index, 0, item);

const insertItem = (items, item, index) => {
    if (isEmpty(item)) {
        return null;
    }

    // if index is undefined, append
    if (typeof index === 'undefined') {
        items.push(item);
        return item;
    }

    // limit the index to the size of the items array
    index = limit(index, 0, items.length);

    // add item to array
    arrayInsert(items, index, item);

    // expose
    return item;
};

const isBase64DataURI = str =>
    /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*)\s*$/i.test(
        str
    );

const getFilenameFromURL = url =>
    `${url}`
        .split('/')
        .pop()
        .split('?')
        .shift();

const getExtensionFromFilename = name => name.split('.').pop();

const guesstimateExtension = type => {
    // if no extension supplied, exit here
    if (typeof type !== 'string') {
        return '';
    }

    // get subtype
    const subtype = type.split('/').pop();

    // is svg subtype
    if (/svg/.test(subtype)) {
        return 'svg';
    }

    if (/zip|compressed/.test(subtype)) {
        return 'zip';
    }

    if (/plain/.test(subtype)) {
        return 'txt';
    }

    if (/msword/.test(subtype)) {
        return 'doc';
    }

    // if is valid subtype
    if (/[a-z]+/.test(subtype)) {
        // always use jpg extension
        if (subtype === 'jpeg') {
            return 'jpg';
        }

        // return subtype
        return subtype;
    }

    return '';
};

const leftPad = (value, padding = '') => (padding + value).slice(-padding.length);

const getDateString = (date = new Date()) =>
    `${date.getFullYear()}-${leftPad(date.getMonth() + 1, '00')}-${leftPad(
        date.getDate(),
        '00'
    )}_${leftPad(date.getHours(), '00')}-${leftPad(date.getMinutes(), '00')}-${leftPad(
        date.getSeconds(),
        '00'
    )}`;

const getFileFromBlob = (blob, filename, type = null, extension = null) => {
    const file =
        typeof type === 'string'
            ? blob.slice(0, blob.size, type)
            : blob.slice(0, blob.size, blob.type);
    file.lastModifiedDate = new Date();

    // copy relative path
    if (blob._relativePath) file._relativePath = blob._relativePath;

    // if blob has name property, use as filename if no filename supplied
    if (!isString(filename)) {
        filename = getDateString();
    }

    // if filename supplied but no extension and filename has extension
    if (filename && extension === null && getExtensionFromFilename(filename)) {
        file.name = filename;
    } else {
        extension = extension || guesstimateExtension(file.type);
        file.name = filename + (extension ? '.' + extension : '');
    }

    return file;
};

const getBlobBuilder = () => {
    return (window.BlobBuilder =
        window.BlobBuilder ||
        window.WebKitBlobBuilder ||
        window.MozBlobBuilder ||
        window.MSBlobBuilder);
};

const createBlob = (arrayBuffer, mimeType) => {
    const BB = getBlobBuilder();

    if (BB) {
        const bb = new BB();
        bb.append(arrayBuffer);
        return bb.getBlob(mimeType);
    }

    return new Blob([arrayBuffer], {
        type: mimeType,
    });
};

const getBlobFromByteStringWithMimeType = (byteString, mimeType) => {
    const ab = new ArrayBuffer(byteString.length);
    const ia = new Uint8Array(ab);

    for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    return createBlob(ab, mimeType);
};

const getMimeTypeFromBase64DataURI = dataURI => {
    return (/^data:(.+);/.exec(dataURI) || [])[1] || null;
};

const getBase64DataFromBase64DataURI = dataURI => {
    // get data part of string (remove data:image/jpeg...,)
    const data = dataURI.split(',')[1];

    // remove any whitespace as that causes InvalidCharacterError in IE
    return data.replace(/\s/g, '');
};

const getByteStringFromBase64DataURI = dataURI => {
    return atob(getBase64DataFromBase64DataURI(dataURI));
};

const getBlobFromBase64DataURI = dataURI => {
    const mimeType = getMimeTypeFromBase64DataURI(dataURI);
    const byteString = getByteStringFromBase64DataURI(dataURI);

    return getBlobFromByteStringWithMimeType(byteString, mimeType);
};

const getFileFromBase64DataURI = (dataURI, filename, extension) => {
    return getFileFromBlob(getBlobFromBase64DataURI(dataURI), filename, null, extension);
};

const getFileNameFromHeader = header => {
    // test if is content disposition header, if not exit
    if (!/^content-disposition:/i.test(header)) return null;

    // get filename parts
    const matches = header
        .split(/filename=|filename\*=.+''/)
        .splice(1)
        .map(name => name.trim().replace(/^["']|[;"']{0,2}$/g, ''))
        .filter(name => name.length);

    return matches.length ? decodeURI(matches[matches.length - 1]) : null;
};

const getFileSizeFromHeader = header => {
    if (/content-length:/i.test(header)) {
        const size = header.match(/[0-9]+/)[0];
        return size ? parseInt(size, 10) : null;
    }
    return null;
};

const getTranfserIdFromHeader = header => {
    if (/x-content-transfer-id:/i.test(header)) {
        const id = (header.split(':')[1] || '').trim();
        return id || null;
    }
    return null;
};

const getFileInfoFromHeaders = headers => {
    const info = {
        source: null,
        name: null,
        size: null,
    };

    const rows = headers.split('\n');
    for (let header of rows) {
        const name = getFileNameFromHeader(header);
        if (name) {
            info.name = name;
            continue;
        }

        const size = getFileSizeFromHeader(header);
        if (size) {
            info.size = size;
            continue;
        }

        const source = getTranfserIdFromHeader(header);
        if (source) {
            info.source = source;
            continue;
        }
    }

    return info;
};

const createFileLoader = fetchFn => {
    const state = {
        source: null,
        complete: false,
        progress: 0,
        size: null,
        timestamp: null,
        duration: 0,
        request: null,
    };

    const getProgress = () => state.progress;
    const abort = () => {
        if (state.request && state.request.abort) {
            state.request.abort();
        }
    };

    // load source
    const load = () => {
        // get quick reference
        const source = state.source;

        api.fire('init', source);

        // Load Files
        if (source instanceof File) {
            api.fire('load', source);
        } else if (source instanceof Blob) {
            // Load blobs, set default name to current date
            api.fire('load', getFileFromBlob(source, source.name));
        } else if (isBase64DataURI(source)) {
            // Load base 64, set default name to current date
            api.fire('load', getFileFromBase64DataURI(source));
        } else {
            // Deal as if is external URL, let's load it!
            loadURL(source);
        }
    };

    // loads a url
    const loadURL = url => {
        // is remote url and no fetch method supplied
        if (!fetchFn) {
            api.fire('error', {
                type: 'error',
                body: "Can't load URL",
                code: 400,
            });
            return;
        }

        // set request start
        state.timestamp = Date.now();

        // load file
        state.request = fetchFn(
            url,
            response => {
                // update duration
                state.duration = Date.now() - state.timestamp;

                // done!
                state.complete = true;

                // turn blob response into a file
                if (response instanceof Blob) {
                    response = getFileFromBlob(response, response.name || getFilenameFromURL(url));
                }

                api.fire(
                    'load',
                    // if has received blob, we go with blob, if no response, we return null
                    response instanceof Blob ? response : response ? response.body : null
                );
            },
            error => {
                api.fire(
                    'error',
                    typeof error === 'string'
                        ? {
                              type: 'error',
                              code: 0,
                              body: error,
                          }
                        : error
                );
            },
            (computable, current, total) => {
                // collected some meta data already
                if (total) {
                    state.size = total;
                }

                // update duration
                state.duration = Date.now() - state.timestamp;

                // if we can't compute progress, we're not going to fire progress events
                if (!computable) {
                    state.progress = null;
                    return;
                }

                // update progress percentage
                state.progress = current / total;

                // expose
                api.fire('progress', state.progress);
            },
            () => {
                api.fire('abort');
            },
            response => {
                const fileinfo = getFileInfoFromHeaders(
                    typeof response === 'string' ? response : response.headers
                );
                api.fire('meta', {
                    size: state.size || fileinfo.size,
                    filename: fileinfo.name,
                    source: fileinfo.source,
                });
            }
        );
    };

    const api = {
        ...on(),
        setSource: source => (state.source = source),
        getProgress, // file load progress
        abort, // abort file load
        load, // start load
    };

    return api;
};

const isGet = method => /GET|HEAD/.test(method);

const sendRequest = (data, url, options) => {
    const api = {
        onheaders: () => {},
        onprogress: () => {},
        onload: () => {},
        ontimeout: () => {},
        onerror: () => {},
        onabort: () => {},
        abort: () => {
            aborted = true;
            xhr.abort();
        },
    };

    // timeout identifier, only used when timeout is defined
    let aborted = false;
    let headersReceived = false;

    // set default options
    options = {
        method: 'POST',
        headers: {},
        withCredentials: false,
        ...options,
    };

    // encode url
    url = encodeURI(url);

    // if method is GET, add any received data to url

    if (isGet(options.method) && data) {
        url = `${url}${encodeURIComponent(typeof data === 'string' ? data : JSON.stringify(data))}`;
    }

    // create request
    const xhr = new XMLHttpRequest();

    // progress of load
    const process = isGet(options.method) ? xhr : xhr.upload;
    process.onprogress = e => {
        // no progress event when aborted ( onprogress is called once after abort() )
        if (aborted) {
            return;
        }

        api.onprogress(e.lengthComputable, e.loaded, e.total);
    };

    // tries to get header info to the app as fast as possible
    xhr.onreadystatechange = () => {
        // not interesting in these states ('unsent' and 'openend' as they don't give us any additional info)
        if (xhr.readyState < 2) {
            return;
        }

        // no server response
        if (xhr.readyState === 4 && xhr.status === 0) {
            return;
        }

        if (headersReceived) {
            return;
        }

        headersReceived = true;

        // we've probably received some useful data in response headers
        api.onheaders(xhr);
    };

    // load successful
    xhr.onload = () => {
        // is classified as valid response
        if (xhr.status >= 200 && xhr.status < 300) {
            api.onload(xhr);
        } else {
            api.onerror(xhr);
        }
    };

    // error during load
    xhr.onerror = () => api.onerror(xhr);

    // request aborted
    xhr.onabort = () => {
        aborted = true;
        api.onabort();
    };

    // request timeout
    xhr.ontimeout = () => api.ontimeout(xhr);

    // open up open up!
    xhr.open(options.method, url, true);

    // set timeout if defined (do it after open so IE11 plays ball)
    if (isInt(options.timeout)) {
        xhr.timeout = options.timeout;
    }

    // add headers
    Object.keys(options.headers).forEach(key => {
        const value = unescape(encodeURIComponent(options.headers[key]));
        xhr.setRequestHeader(key, value);
    });

    // set type of response
    if (options.responseType) {
        xhr.responseType = options.responseType;
    }

    // set credentials
    if (options.withCredentials) {
        xhr.withCredentials = true;
    }

    // let's send our data
    xhr.send(data);

    return api;
};

const createResponse = (type, code, body, headers) => ({
    type,
    code,
    body,
    headers,
});

const createTimeoutResponse = cb => xhr => {
    cb(createResponse('error', 0, 'Timeout', xhr.getAllResponseHeaders()));
};

const hasQS = str => /\?/.test(str);
const buildURL = (...parts) => {
    let url = '';
    parts.forEach(part => {
        url += hasQS(url) && hasQS(part) ? part.replace(/\?/, '&') : part;
    });
    return url;
};

const createFetchFunction = (apiUrl = '', action) => {
    // custom handler (should also handle file, load, error, progress and abort)
    if (typeof action === 'function') {
        return action;
    }

    // no action supplied
    if (!action || !isString(action.url)) {
        return null;
    }

    // set onload hanlder
    const onload = action.onload || (res => res);
    const onerror = action.onerror || (res => null);

    // internal handler
    return (url, load, error, progress, abort, headers) => {
        // do local or remote request based on if the url is external
        const request = sendRequest(url, buildURL(apiUrl, action.url), {
            ...action,
            responseType: 'blob',
        });

        request.onload = xhr => {
            // get headers
            const headers = xhr.getAllResponseHeaders();

            // get filename
            const filename = getFileInfoFromHeaders(headers).name || getFilenameFromURL(url);

            // create response
            load(
                createResponse(
                    'load',
                    xhr.status,
                    action.method === 'HEAD'
                        ? null
                        : getFileFromBlob(onload(xhr.response), filename),
                    headers
                )
            );
        };

        request.onerror = xhr => {
            error(
                createResponse(
                    'error',
                    xhr.status,
                    onerror(xhr.response) || xhr.statusText,
                    xhr.getAllResponseHeaders()
                )
            );
        };

        request.onheaders = xhr => {
            headers(createResponse('headers', xhr.status, null, xhr.getAllResponseHeaders()));
        };

        request.ontimeout = createTimeoutResponse(error);
        request.onprogress = progress;
        request.onabort = abort;

        // should return request
        return request;
    };
};

const ChunkStatus = {
    QUEUED: 0,
    COMPLETE: 1,
    PROCESSING: 2,
    ERROR: 3,
    WAITING: 4,
};

/*
function signature:
  (file, metadata, load, error, progress, abort, transfer, options) => {
    return {
    abort:() => {}
  }
}
*/

// apiUrl, action, name, file, metadata, load, error, progress, abort, transfer, options
const processFileChunked = (
    apiUrl,
    action,
    name,
    file,
    metadata,
    load,
    error,
    progress,
    abort,
    transfer,
    options
) => {
    // all chunks
    const chunks = [];
    const { chunkTransferId, chunkServer, chunkSize, chunkRetryDelays } = options;

    // default state
    const state = {
        serverId: chunkTransferId,
        aborted: false,
    };

    // set onload handlers
    const ondata = action.ondata || (fd => fd);
    const onload =
        action.onload ||
        ((xhr, method) =>
            method === 'HEAD' ? xhr.getResponseHeader('Upload-Offset') : xhr.response);
    const onerror = action.onerror || (res => null);

    // create server hook
    const requestTransferId = cb => {
        const formData = new FormData();

        // add metadata under same name
        if (isObject(metadata)) formData.append(name, JSON.stringify(metadata));

        const headers =
            typeof action.headers === 'function'
                ? action.headers(file, metadata)
                : {
                      ...action.headers,
                      'Upload-Length': file.size,
                  };

        const requestParams = {
            ...action,
            headers,
        };

        // send request object
        const request = sendRequest(ondata(formData), buildURL(apiUrl, action.url), requestParams);

        request.onload = xhr => cb(onload(xhr, requestParams.method));

        request.onerror = xhr =>
            error(
                createResponse(
                    'error',
                    xhr.status,
                    onerror(xhr.response) || xhr.statusText,
                    xhr.getAllResponseHeaders()
                )
            );

        request.ontimeout = createTimeoutResponse(error);
    };

    const requestTransferOffset = cb => {
        const requestUrl = buildURL(apiUrl, chunkServer.url, state.serverId);

        const headers =
            typeof action.headers === 'function'
                ? action.headers(state.serverId)
                : {
                      ...action.headers,
                  };

        const requestParams = {
            headers,
            method: 'HEAD',
        };

        const request = sendRequest(null, requestUrl, requestParams);

        request.onload = xhr => cb(onload(xhr, requestParams.method));

        request.onerror = xhr =>
            error(
                createResponse(
                    'error',
                    xhr.status,
                    onerror(xhr.response) || xhr.statusText,
                    xhr.getAllResponseHeaders()
                )
            );

        request.ontimeout = createTimeoutResponse(error);
    };

    // create chunks
    const lastChunkIndex = Math.floor(file.size / chunkSize);
    for (let i = 0; i <= lastChunkIndex; i++) {
        const offset = i * chunkSize;
        const data = file.slice(offset, offset + chunkSize, 'application/offset+octet-stream');
        chunks[i] = {
            index: i,
            size: data.size,
            offset,
            data,
            file,
            progress: 0,
            retries: [...chunkRetryDelays],
            status: ChunkStatus.QUEUED,
            error: null,
            request: null,
            timeout: null,
        };
    }

    const completeProcessingChunks = () => load(state.serverId);

    const canProcessChunk = chunk =>
        chunk.status === ChunkStatus.QUEUED || chunk.status === ChunkStatus.ERROR;

    const processChunk = chunk => {
        // processing is paused, wait here
        if (state.aborted) return;

        // get next chunk to process
        chunk = chunk || chunks.find(canProcessChunk);

        // no more chunks to process
        if (!chunk) {
            // all done?
            if (chunks.every(chunk => chunk.status === ChunkStatus.COMPLETE)) {
                completeProcessingChunks();
            }

            // no chunk to handle
            return;
        }

        // now processing this chunk
        chunk.status = ChunkStatus.PROCESSING;
        chunk.progress = null;

        // allow parsing of formdata
        const ondata = chunkServer.ondata || (fd => fd);
        const onerror = chunkServer.onerror || (res => null);
        const onload = chunkServer.onload || (() => {});

        // send request object
        const requestUrl = buildURL(apiUrl, chunkServer.url, state.serverId);

        const headers =
            typeof chunkServer.headers === 'function'
                ? chunkServer.headers(chunk)
                : {
                      ...chunkServer.headers,
                      'Content-Type': 'application/offset+octet-stream',
                      'Upload-Offset': chunk.offset,
                      'Upload-Length': file.size,
                      'Upload-Name': file.name,
                  };

        const request = (chunk.request = sendRequest(ondata(chunk.data), requestUrl, {
            ...chunkServer,
            headers,
        }));

        request.onload = xhr => {
            // allow hooking into request result
            onload(xhr, chunk.index, chunks.length);

            // done!
            chunk.status = ChunkStatus.COMPLETE;

            // remove request reference
            chunk.request = null;

            // start processing more chunks
            processChunks();
        };

        request.onprogress = (lengthComputable, loaded, total) => {
            chunk.progress = lengthComputable ? loaded : null;
            updateTotalProgress();
        };

        request.onerror = xhr => {
            chunk.status = ChunkStatus.ERROR;
            chunk.request = null;
            chunk.error = onerror(xhr.response) || xhr.statusText;
            if (!retryProcessChunk(chunk)) {
                error(
                    createResponse(
                        'error',
                        xhr.status,
                        onerror(xhr.response) || xhr.statusText,
                        xhr.getAllResponseHeaders()
                    )
                );
            }
        };

        request.ontimeout = xhr => {
            chunk.status = ChunkStatus.ERROR;
            chunk.request = null;
            if (!retryProcessChunk(chunk)) {
                createTimeoutResponse(error)(xhr);
            }
        };

        request.onabort = () => {
            chunk.status = ChunkStatus.QUEUED;
            chunk.request = null;
            abort();
        };
    };

    const retryProcessChunk = chunk => {
        // no more retries left
        if (chunk.retries.length === 0) return false;

        // new retry
        chunk.status = ChunkStatus.WAITING;
        clearTimeout(chunk.timeout);
        chunk.timeout = setTimeout(() => {
            processChunk(chunk);
        }, chunk.retries.shift());

        // we're going to retry
        return true;
    };

    const updateTotalProgress = () => {
        // calculate total progress fraction
        const totalBytesTransfered = chunks.reduce((p, chunk) => {
            if (p === null || chunk.progress === null) return null;
            return p + chunk.progress;
        }, 0);

        // can't compute progress
        if (totalBytesTransfered === null) return progress(false, 0, 0);

        // calculate progress values
        const totalSize = chunks.reduce((total, chunk) => total + chunk.size, 0);

        // can update progress indicator
        progress(true, totalBytesTransfered, totalSize);
    };

    // process new chunks
    const processChunks = () => {
        const totalProcessing = chunks.filter(chunk => chunk.status === ChunkStatus.PROCESSING)
            .length;
        if (totalProcessing >= 1) return;
        processChunk();
    };

    const abortChunks = () => {
        chunks.forEach(chunk => {
            clearTimeout(chunk.timeout);
            if (chunk.request) {
                chunk.request.abort();
            }
        });
    };

    // let's go!
    if (!state.serverId) {
        requestTransferId(serverId => {
            // stop here if aborted, might have happened in between request and callback
            if (state.aborted) return;

            // pass back to item so we can use it if something goes wrong
            transfer(serverId);

            // store internally
            state.serverId = serverId;
            processChunks();
        });
    } else {
        requestTransferOffset(offset => {
            // stop here if aborted, might have happened in between request and callback
            if (state.aborted) return;

            // mark chunks with lower offset as complete
            chunks
                .filter(chunk => chunk.offset < offset)
                .forEach(chunk => {
                    chunk.status = ChunkStatus.COMPLETE;
                    chunk.progress = chunk.size;
                });

            // continue processing
            processChunks();
        });
    }

    return {
        abort: () => {
            state.aborted = true;
            abortChunks();
        },
    };
};

/*
function signature:
  (file, metadata, load, error, progress, abort) => {
    return {
    abort:() => {}
  }
}
*/
const createFileProcessorFunction = (apiUrl, action, name, options) => (
    file,
    metadata,
    load,
    error,
    progress,
    abort,
    transfer
) => {
    // no file received
    if (!file) return;

    // if was passed a file, and we can chunk it, exit here
    const canChunkUpload = options.chunkUploads;
    const shouldChunkUpload = canChunkUpload && file.size > options.chunkSize;
    const willChunkUpload = canChunkUpload && (shouldChunkUpload || options.chunkForce);
    if (file instanceof Blob && willChunkUpload)
        return processFileChunked(
            apiUrl,
            action,
            name,
            file,
            metadata,
            load,
            error,
            progress,
            abort,
            transfer,
            options
        );

    // set handlers
    const ondata = action.ondata || (fd => fd);
    const onload = action.onload || (res => res);
    const onerror = action.onerror || (res => null);

    const headers =
        typeof action.headers === 'function'
            ? action.headers(file, metadata) || {}
            : {
                  ...action.headers,
              };

    const requestParams = {
        ...action,
        headers,
    };

    // create formdata object
    var formData = new FormData();

    // add metadata under same name
    if (isObject(metadata)) {
        formData.append(name, JSON.stringify(metadata));
    }

    // Turn into an array of objects so no matter what the input, we can handle it the same way
    (file instanceof Blob ? [{ name: null, file }] : file).forEach(item => {
        formData.append(
            name,
            item.file,
            item.name === null ? item.file.name : `${item.name}${item.file.name}`
        );
    });

    // send request object
    const request = sendRequest(ondata(formData), buildURL(apiUrl, action.url), requestParams);
    request.onload = xhr => {
        load(createResponse('load', xhr.status, onload(xhr.response), xhr.getAllResponseHeaders()));
    };

    request.onerror = xhr => {
        error(
            createResponse(
                'error',
                xhr.status,
                onerror(xhr.response) || xhr.statusText,
                xhr.getAllResponseHeaders()
            )
        );
    };

    request.ontimeout = createTimeoutResponse(error);
    request.onprogress = progress;
    request.onabort = abort;

    // should return request
    return request;
};

const createProcessorFunction = (apiUrl = '', action, name, options) => {
    // custom handler (should also handle file, load, error, progress and abort)
    if (typeof action === 'function') return (...params) => action(name, ...params, options);

    // no action supplied
    if (!action || !isString(action.url)) return null;

    // internal handler
    return createFileProcessorFunction(apiUrl, action, name, options);
};

/*
 function signature:
 (uniqueFileId, load, error) => { }
 */
const createRevertFunction = (apiUrl = '', action) => {
    // is custom implementation
    if (typeof action === 'function') {
        return action;
    }

    // no action supplied, return stub function, interface will work, but file won't be removed
    if (!action || !isString(action.url)) {
        return (uniqueFileId, load) => load();
    }

    // set onload hanlder
    const onload = action.onload || (res => res);
    const onerror = action.onerror || (res => null);

    // internal implementation
    return (uniqueFileId, load, error) => {
        const request = sendRequest(
            uniqueFileId,
            apiUrl + action.url,
            action // contains method, headers and withCredentials properties
        );
        request.onload = xhr => {
            load(
                createResponse(
                    'load',
                    xhr.status,
                    onload(xhr.response),
                    xhr.getAllResponseHeaders()
                )
            );
        };

        request.onerror = xhr => {
            error(
                createResponse(
                    'error',
                    xhr.status,
                    onerror(xhr.response) || xhr.statusText,
                    xhr.getAllResponseHeaders()
                )
            );
        };

        request.ontimeout = createTimeoutResponse(error);

        return request;
    };
};

const getRandomNumber = (min = 0, max = 1) => min + Math.random() * (max - min);

const createPerceivedPerformanceUpdater = (
    cb,
    duration = 1000,
    offset = 0,
    tickMin = 25,
    tickMax = 250
) => {
    let timeout = null;
    const start = Date.now();

    const tick = () => {
        let runtime = Date.now() - start;
        let delay = getRandomNumber(tickMin, tickMax);

        if (runtime + delay > duration) {
            delay = runtime + delay - duration;
        }

        let progress = runtime / duration;
        if (progress >= 1 || document.hidden) {
            cb(1);
            return;
        }

        cb(progress);

        timeout = setTimeout(tick, delay);
    };

    if (duration > 0) tick();

    return {
        clear: () => {
            clearTimeout(timeout);
        },
    };
};

const createFileProcessor = (processFn, options) => {
    const state = {
        complete: false,
        perceivedProgress: 0,
        perceivedPerformanceUpdater: null,
        progress: null,
        timestamp: null,
        perceivedDuration: 0,
        duration: 0,
        request: null,
        response: null,
    };

    const { allowMinimumUploadDuration } = options;

    const process = (file, metadata) => {
        const progressFn = () => {
            // we've not yet started the real download, stop here
            // the request might not go through, for instance, there might be some server trouble
            // if state.progress is null, the server does not allow computing progress and we show the spinner instead
            if (state.duration === 0 || state.progress === null) return;

            // as we're now processing, fire the progress event
            api.fire('progress', api.getProgress());
        };

        const completeFn = () => {
            state.complete = true;
            api.fire('load-perceived', state.response.body);
        };

        // let's start processing
        api.fire('start');

        // set request start
        state.timestamp = Date.now();

        // create perceived performance progress indicator
        state.perceivedPerformanceUpdater = createPerceivedPerformanceUpdater(
            progress => {
                state.perceivedProgress = progress;
                state.perceivedDuration = Date.now() - state.timestamp;

                progressFn();

                // if fake progress is done, and a response has been received,
                // and we've not yet called the complete method
                if (state.response && state.perceivedProgress === 1 && !state.complete) {
                    // we done!
                    completeFn();
                }
            },
            // random delay as in a list of files you start noticing
            // files uploading at the exact same speed
            allowMinimumUploadDuration ? getRandomNumber(750, 1500) : 0
        );

        // remember request so we can abort it later
        state.request = processFn(
            // the file to process
            file,

            // the metadata to send along
            metadata,

            // callbacks (load, error, progress, abort, transfer)
            // load expects the body to be a server id if
            // you want to make use of revert
            response => {
                // we put the response in state so we can access
                // it outside of this method
                state.response = isObject(response)
                    ? response
                    : {
                          type: 'load',
                          code: 200,
                          body: `${response}`,
                          headers: {},
                      };

                // update duration
                state.duration = Date.now() - state.timestamp;

                // force progress to 1 as we're now done
                state.progress = 1;

                // actual load is done let's share results
                api.fire('load', state.response.body);

                // we are really done
                // if perceived progress is 1 ( wait for perceived progress to complete )
                // or if server does not support progress ( null )
                if (
                    !allowMinimumUploadDuration ||
                    (allowMinimumUploadDuration && state.perceivedProgress === 1)
                ) {
                    completeFn();
                }
            },

            // error is expected to be an object with type, code, body
            error => {
                // cancel updater
                state.perceivedPerformanceUpdater.clear();

                // update others about this error
                api.fire(
                    'error',
                    isObject(error)
                        ? error
                        : {
                              type: 'error',
                              code: 0,
                              body: `${error}`,
                          }
                );
            },

            // actual processing progress
            (computable, current, total) => {
                // update actual duration
                state.duration = Date.now() - state.timestamp;

                // update actual progress
                state.progress = computable ? current / total : null;

                progressFn();
            },

            // abort does not expect a value
            () => {
                // stop updater
                state.perceivedPerformanceUpdater.clear();

                // fire the abort event so we can switch visuals
                api.fire('abort', state.response ? state.response.body : null);
            },

            // register the id for this transfer
            transferId => {
                api.fire('transfer', transferId);
            }
        );
    };

    const abort = () => {
        // no request running, can't abort
        if (!state.request) return;

        // stop updater
        state.perceivedPerformanceUpdater.clear();

        // abort actual request
        if (state.request.abort) state.request.abort();

        // if has response object, we've completed the request
        state.complete = true;
    };

    const reset = () => {
        abort();
        state.complete = false;
        state.perceivedProgress = 0;
        state.progress = 0;
        state.timestamp = null;
        state.perceivedDuration = 0;
        state.duration = 0;
        state.request = null;
        state.response = null;
    };

    const getProgress = allowMinimumUploadDuration
        ? () => (state.progress ? Math.min(state.progress, state.perceivedProgress) : null)
        : () => state.progress || null;

    const getDuration = allowMinimumUploadDuration
        ? () => Math.min(state.duration, state.perceivedDuration)
        : () => state.duration;

    const api = {
        ...on(),
        process, // start processing file
        abort, // abort active process request
        getProgress,
        getDuration,
        reset,
    };

    return api;
};

const getFilenameWithoutExtension = name => name.substring(0, name.lastIndexOf('.')) || name;

const createFileStub = source => {
    let data = [source.name, source.size, source.type];

    // is blob or base64, then we need to set the name
    if (source instanceof Blob || isBase64DataURI(source)) {
        data[0] = source.name || getDateString();
    } else if (isBase64DataURI(source)) {
        // if is base64 data uri we need to determine the average size and type
        data[1] = source.length;
        data[2] = getMimeTypeFromBase64DataURI(source);
    } else if (isString(source)) {
        // url
        data[0] = getFilenameFromURL(source);
        data[1] = 0;
        data[2] = 'application/octet-stream';
    }

    return {
        name: data[0],
        size: data[1],
        type: data[2],
    };
};

const isFile = value => !!(value instanceof File || (value instanceof Blob && value.name));

const deepCloneObject = src => {
    if (!isObject(src)) return src;
    const target = isArray(src) ? [] : {};
    for (const key in src) {
        if (!src.hasOwnProperty(key)) continue;
        const v = src[key];
        target[key] = v && isObject(v) ? deepCloneObject(v) : v;
    }
    return target;
};

const createItem = (origin = null, serverFileReference = null, file = null) => {
    // unique id for this item, is used to identify the item across views
    const id = getUniqueId();

    /**
     * Internal item state
     */
    const state = {
        // is archived
        archived: false,

        // if is frozen, no longer fires events
        frozen: false,

        // removed from view
        released: false,

        // original source
        source: null,

        // file model reference
        file,

        // id of file on server
        serverFileReference,

        // id of file transfer on server
        transferId: null,

        // is aborted
        processingAborted: false,

        // current item status
        status: serverFileReference ? ItemStatus.PROCESSING_COMPLETE : ItemStatus.INIT,

        // active processes
        activeLoader: null,
        activeProcessor: null,
    };

    // callback used when abort processing is called to link back to the resolve method
    let abortProcessingRequestComplete = null;

    /**
     * Externally added item metadata
     */
    const metadata = {};

    // item data
    const setStatus = status => (state.status = status);

    // fire event unless the item has been archived
    const fire = (event, ...params) => {
        if (state.released || state.frozen) return;
        api.fire(event, ...params);
    };

    // file data
    const getFileExtension = () => getExtensionFromFilename(state.file.name);
    const getFileType = () => state.file.type;
    const getFileSize = () => state.file.size;
    const getFile = () => state.file;

    //
    // logic to load a file
    //
    const load = (source, loader, onload) => {
        // remember the original item source
        state.source = source;

        // source is known
        api.fireSync('init');

        // file stub is already there
        if (state.file) {
            api.fireSync('load-skip');
            return;
        }

        // set a stub file object while loading the actual data
        state.file = createFileStub(source);

        // starts loading
        loader.on('init', () => {
            fire('load-init');
        });

        // we'eve received a size indication, let's update the stub
        loader.on('meta', meta => {
            // set size of file stub
            state.file.size = meta.size;

            // set name of file stub
            state.file.filename = meta.filename;

            // if has received source, we done
            if (meta.source) {
                origin = FileOrigin.LIMBO;
                state.serverFileReference = meta.source;
                state.status = ItemStatus.PROCESSING_COMPLETE;
            }

            // size has been updated
            fire('load-meta');
        });

        // the file is now loading we need to update the progress indicators
        loader.on('progress', progress => {
            setStatus(ItemStatus.LOADING);

            fire('load-progress', progress);
        });

        // an error was thrown while loading the file, we need to switch to error state
        loader.on('error', error => {
            setStatus(ItemStatus.LOAD_ERROR);

            fire('load-request-error', error);
        });

        // user or another process aborted the file load (cannot retry)
        loader.on('abort', () => {
            setStatus(ItemStatus.INIT);
            fire('load-abort');
        });

        // done loading
        loader.on('load', file => {
            // as we've now loaded the file the loader is no longer required
            state.activeLoader = null;

            // called when file has loaded succesfully
            const success = result => {
                // set (possibly) transformed file
                state.file = isFile(result) ? result : state.file;

                // file received
                if (origin === FileOrigin.LIMBO && state.serverFileReference) {
                    setStatus(ItemStatus.PROCESSING_COMPLETE);
                } else {
                    setStatus(ItemStatus.IDLE);
                }

                fire('load');
            };

            const error = result => {
                // set original file
                state.file = file;
                fire('load-meta');

                setStatus(ItemStatus.LOAD_ERROR);
                fire('load-file-error', result);
            };

            // if we already have a server file reference, we don't need to call the onload method
            if (state.serverFileReference) {
                success(file);
                return;
            }

            // no server id, let's give this file the full treatment
            onload(file, success, error);
        });

        // set loader source data
        loader.setSource(source);

        // set as active loader
        state.activeLoader = loader;

        // load the source data
        loader.load();
    };

    const retryLoad = () => {
        if (!state.activeLoader) {
            return;
        }
        state.activeLoader.load();
    };

    const abortLoad = () => {
        if (state.activeLoader) {
            state.activeLoader.abort();
            return;
        }
        setStatus(ItemStatus.INIT);
        fire('load-abort');
    };

    //
    // logic to process a file
    //
    const process = (processor, onprocess) => {
        // processing was aborted
        if (state.processingAborted) {
            state.processingAborted = false;
            return;
        }

        // now processing
        setStatus(ItemStatus.PROCESSING);

        // reset abort callback
        abortProcessingRequestComplete = null;

        // if no file loaded we'll wait for the load event
        if (!(state.file instanceof Blob)) {
            api.on('load', () => {
                process(processor, onprocess);
            });
            return;
        }

        // setup processor
        processor.on('load', serverFileReference => {
            // need this id to be able to revert the upload
            state.transferId = null;
            state.serverFileReference = serverFileReference;
        });

        // register transfer id
        processor.on('transfer', transferId => {
            // need this id to be able to revert the upload
            state.transferId = transferId;
        });

        processor.on('load-perceived', serverFileReference => {
            // no longer required
            state.activeProcessor = null;

            // need this id to be able to rever the upload
            state.transferId = null;
            state.serverFileReference = serverFileReference;

            setStatus(ItemStatus.PROCESSING_COMPLETE);
            fire('process-complete', serverFileReference);
        });

        processor.on('start', () => {
            fire('process-start');
        });

        processor.on('error', error => {
            state.activeProcessor = null;
            setStatus(ItemStatus.PROCESSING_ERROR);
            fire('process-error', error);
        });

        processor.on('abort', serverFileReference => {
            state.activeProcessor = null;

            // if file was uploaded but processing was cancelled during perceived processor time store file reference
            state.serverFileReference = serverFileReference;

            setStatus(ItemStatus.IDLE);
            fire('process-abort');

            // has timeout so doesn't interfere with remove action
            if (abortProcessingRequestComplete) {
                abortProcessingRequestComplete();
            }
        });

        processor.on('progress', progress => {
            fire('process-progress', progress);
        });

        // when successfully transformed
        const success = file => {
            // if was archived in the mean time, don't process
            if (state.archived) return;

            // process file!
            processor.process(file, { ...metadata });
        };

        // something went wrong during transform phase
        const error = console.error;

        // start processing the file
        onprocess(state.file, success, error);

        // set as active processor
        state.activeProcessor = processor;
    };

    const requestProcessing = () => {
        state.processingAborted = false;
        setStatus(ItemStatus.PROCESSING_QUEUED);
    };

    const abortProcessing = () =>
        new Promise(resolve => {
            if (!state.activeProcessor) {
                state.processingAborted = true;

                setStatus(ItemStatus.IDLE);
                fire('process-abort');

                resolve();
                return;
            }

            abortProcessingRequestComplete = () => {
                resolve();
            };

            state.activeProcessor.abort();
        });

    //
    // logic to revert a processed file
    //
    const revert = (revertFileUpload, forceRevert) =>
        new Promise((resolve, reject) => {
            // a completed upload will have a serverFileReference, a failed chunked upload where
            // getting a serverId succeeded but >=0 chunks have been uploaded will have transferId set
            const serverTransferId =
                state.serverFileReference !== null ? state.serverFileReference : state.transferId;

            // cannot revert without a server id for this process
            if (serverTransferId === null) {
                resolve();
                return;
            }

            // revert the upload (fire and forget)
            revertFileUpload(
                serverTransferId,
                () => {
                    // reset file server id and transfer id as now it's not available on the server
                    state.serverFileReference = null;
                    state.transferId = null;
                    resolve();
                },
                error => {
                    // don't set error state when reverting is optional, it will always resolve
                    if (!forceRevert) {
                        resolve();
                        return;
                    }

                    // oh no errors
                    setStatus(ItemStatus.PROCESSING_REVERT_ERROR);
                    fire('process-revert-error');
                    reject(error);
                }
            );

            // fire event
            setStatus(ItemStatus.IDLE);
            fire('process-revert');
        });

    // exposed methods
    const setMetadata = (key, value, silent) => {
        const keys = key.split('.');
        const root = keys[0];
        const last = keys.pop();
        let data = metadata;
        keys.forEach(key => (data = data[key]));

        // compare old value against new value, if they're the same, we're not updating
        if (JSON.stringify(data[last]) === JSON.stringify(value)) return;

        // update value
        data[last] = value;

        // fire update
        fire('metadata-update', {
            key: root,
            value: metadata[root],
            silent,
        });
    };

    const getMetadata = key => deepCloneObject(key ? metadata[key] : metadata);

    const api = {
        id: { get: () => id },
        origin: { get: () => origin, set: value => (origin = value) },
        serverId: { get: () => state.serverFileReference },
        transferId: { get: () => state.transferId },
        status: { get: () => state.status },
        filename: { get: () => state.file.name },
        filenameWithoutExtension: { get: () => getFilenameWithoutExtension(state.file.name) },
        fileExtension: { get: getFileExtension },
        fileType: { get: getFileType },
        fileSize: { get: getFileSize },
        file: { get: getFile },
        relativePath: { get: () => state.file._relativePath },

        source: { get: () => state.source },

        getMetadata,
        setMetadata: (key, value, silent) => {
            if (isObject(key)) {
                const data = key;
                Object.keys(data).forEach(key => {
                    setMetadata(key, data[key], value);
                });
                return key;
            }
            setMetadata(key, value, silent);
            return value;
        },

        extend: (name, handler) => (itemAPI[name] = handler),

        abortLoad,
        retryLoad,
        requestProcessing,
        abortProcessing,

        load,
        process,
        revert,

        ...on(),

        freeze: () => (state.frozen = true),

        release: () => (state.released = true),
        released: { get: () => state.released },

        archive: () => (state.archived = true),
        archived: { get: () => state.archived },

        // replace source and file object
        setFile: file => (state.file = file),
    };

    // create it here instead of returning it instantly so we can extend it later
    const itemAPI = createObject(api);

    return itemAPI;
};

const getItemIndexByQuery = (items, query) => {
    // just return first index
    if (isEmpty(query)) {
        return 0;
    }

    // invalid queries
    if (!isString(query)) {
        return -1;
    }

    // return item by id (or -1 if not found)
    return items.findIndex(item => item.id === query);
};

const getItemById = (items, itemId) => {
    const index = getItemIndexByQuery(items, itemId);
    if (index < 0) {
        return;
    }
    return items[index] || null;
};

const fetchBlob = (url, load, error, progress, abort, headers) => {
    const request = sendRequest(null, url, {
        method: 'GET',
        responseType: 'blob',
    });

    request.onload = xhr => {
        // get headers
        const headers = xhr.getAllResponseHeaders();

        // get filename
        const filename = getFileInfoFromHeaders(headers).name || getFilenameFromURL(url);

        // create response
        load(createResponse('load', xhr.status, getFileFromBlob(xhr.response, filename), headers));
    };

    request.onerror = xhr => {
        error(createResponse('error', xhr.status, xhr.statusText, xhr.getAllResponseHeaders()));
    };

    request.onheaders = xhr => {
        headers(createResponse('headers', xhr.status, null, xhr.getAllResponseHeaders()));
    };

    request.ontimeout = createTimeoutResponse(error);
    request.onprogress = progress;
    request.onabort = abort;

    // should return request
    return request;
};

const getDomainFromURL = url => {
    if (url.indexOf('//') === 0) {
        url = location.protocol + url;
    }
    return url
        .toLowerCase()
        .replace('blob:', '')
        .replace(/([a-z])?:\/\//, '$1')
        .split('/')[0];
};

const isExternalURL = url =>
    (url.indexOf(':') > -1 || url.indexOf('//') > -1) &&
    getDomainFromURL(location.href) !== getDomainFromURL(url);

const dynamicLabel = label => (...params) => (isFunction(label) ? label(...params) : label);

const isMockItem = item => !isFile(item.file);

const listUpdated = (dispatch, state) => {
    clearTimeout(state.listUpdateTimeout);
    state.listUpdateTimeout = setTimeout(() => {
        dispatch('DID_UPDATE_ITEMS', { items: getActiveItems(state.items) });
    }, 0);
};

const optionalPromise = (fn, ...params) =>
    new Promise(resolve => {
        if (!fn) {
            return resolve(true);
        }

        const result = fn(...params);

        if (result == null) {
            return resolve(true);
        }

        if (typeof result === 'boolean') {
            return resolve(result);
        }

        if (typeof result.then === 'function') {
            result.then(resolve);
        }
    });

const sortItems = (state, compare) => {
    state.items.sort((a, b) => compare(createItemAPI(a), createItemAPI(b)));
};

// returns item based on state
const getItemByQueryFromState = (state, itemHandler) => ({
    query,
    success = () => {},
    failure = () => {},
    ...options
} = {}) => {
    const item = getItemByQuery(state.items, query);
    if (!item) {
        failure({
            error: createResponse('error', 0, 'Item not found'),
            file: null,
        });
        return;
    }
    itemHandler(item, success, failure, options || {});
};

const actions = (dispatch, query, state) => ({
    /**
     * Aborts all ongoing processes
     */
    ABORT_ALL: () => {
        getActiveItems(state.items).forEach(item => {
            item.freeze();
            item.abortLoad();
            item.abortProcessing();
        });
    },

    /**
     * Sets initial files
     */
    DID_SET_FILES: ({ value = [] }) => {
        // map values to file objects
        const files = value.map(file => ({
            source: file.source ? file.source : file,
            options: file.options,
        }));

        // loop over files, if file is in list, leave it be, if not, remove
        // test if items should be moved
        let activeItems = getActiveItems(state.items);

        activeItems.forEach(item => {
            // if item not is in new value, remove
            if (!files.find(file => file.source === item.source || file.source === item.file)) {
                dispatch('REMOVE_ITEM', { query: item, remove: false });
            }
        });

        // add new files
        activeItems = getActiveItems(state.items);
        files.forEach((file, index) => {
            // if file is already in list
            if (activeItems.find(item => item.source === file.source || item.file === file.source))
                return;

            // not in list, add
            dispatch('ADD_ITEM', {
                ...file,
                interactionMethod: InteractionMethod.NONE,
                index,
            });
        });
    },

    DID_UPDATE_ITEM_METADATA: ({ id, action, change }) => {
        // don't do anything
        if (change.silent) return;

        // if is called multiple times in close succession we combined all calls together to save resources
        clearTimeout(state.itemUpdateTimeout);
        state.itemUpdateTimeout = setTimeout(() => {
            const item = getItemById(state.items, id);

            // only revert and attempt to upload when we're uploading to a server
            if (!query('IS_ASYNC')) {
                // should we update the output data
                applyFilterChain('SHOULD_PREPARE_OUTPUT', false, {
                    item,
                    query,
                    action,
                    change,
                }).then(shouldPrepareOutput => {
                    // plugins determined the output data should be prepared (or not), can be adjusted with beforePrepareOutput hook
                    const beforePrepareFile = query('GET_BEFORE_PREPARE_FILE');
                    if (beforePrepareFile)
                        shouldPrepareOutput = beforePrepareFile(item, shouldPrepareOutput);

                    if (!shouldPrepareOutput) return;

                    dispatch(
                        'REQUEST_PREPARE_OUTPUT',
                        {
                            query: id,
                            item,
                            success: file => {
                                dispatch('DID_PREPARE_OUTPUT', { id, file });
                            },
                        },
                        true
                    );
                });

                return;
            }

            // if is local item we need to enable upload button so change can be propagated to server
            if (item.origin === FileOrigin.LOCAL) {
                dispatch('DID_LOAD_ITEM', {
                    id: item.id,
                    error: null,
                    serverFileReference: item.source,
                });
            }

            // for async scenarios
            const upload = () => {
                // we push this forward a bit so the interface is updated correctly
                setTimeout(() => {
                    dispatch('REQUEST_ITEM_PROCESSING', { query: id });
                }, 32);
            };

            const revert = doUpload => {
                item.revert(
                    createRevertFunction(state.options.server.url, state.options.server.revert),
                    query('GET_FORCE_REVERT')
                )
                    .then(doUpload ? upload : () => {})
                    .catch(() => {});
            };

            const abort = doUpload => {
                item.abortProcessing().then(doUpload ? upload : () => {});
            };

            // if we should re-upload the file immediately
            if (item.status === ItemStatus.PROCESSING_COMPLETE) {
                return revert(state.options.instantUpload);
            }

            // if currently uploading, cancel upload
            if (item.status === ItemStatus.PROCESSING) {
                return abort(state.options.instantUpload);
            }

            if (state.options.instantUpload) {
                upload();
            }
        }, 0);
    },

    MOVE_ITEM: ({ query, index }) => {
        const item = getItemByQuery(state.items, query);
        if (!item) return;
        const currentIndex = state.items.indexOf(item);
        index = limit(index, 0, state.items.length - 1);
        if (currentIndex === index) return;
        state.items.splice(index, 0, state.items.splice(currentIndex, 1)[0]);
    },

    SORT: ({ compare }) => {
        sortItems(state, compare);
        dispatch('DID_SORT_ITEMS', {
            items: query('GET_ACTIVE_ITEMS'),
        });
    },

    ADD_ITEMS: ({ items, index, interactionMethod, success = () => {}, failure = () => {} }) => {
        let currentIndex = index;

        if (index === -1 || typeof index === 'undefined') {
            const insertLocation = query('GET_ITEM_INSERT_LOCATION');
            const totalItems = query('GET_TOTAL_ITEMS');
            currentIndex = insertLocation === 'before' ? 0 : totalItems;
        }

        const ignoredFiles = query('GET_IGNORED_FILES');
        const isValidFile = source =>
            isFile(source) ? !ignoredFiles.includes(source.name.toLowerCase()) : !isEmpty(source);
        const validItems = items.filter(isValidFile);

        const promises = validItems.map(
            source =>
                new Promise((resolve, reject) => {
                    dispatch('ADD_ITEM', {
                        interactionMethod,
                        source: source.source || source,
                        success: resolve,
                        failure: reject,
                        index: currentIndex++,
                        options: 
Download .txt
gitextract_hbxyat0y/

├── .babelrc
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.yml
│       ├── config.yml
│       └── feature_request.yml
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── banner-cli.js
├── banner.js
├── dist/
│   ├── filepond.css
│   ├── filepond.esm.js
│   └── filepond.js
├── index.html
├── jest.config.js
├── jest.stubs.js
├── locale/
│   ├── am-et.js
│   ├── ar-ar.js
│   ├── az-az.js
│   ├── ca-ca.js
│   ├── cs-cz.js
│   ├── cy-cy.js
│   ├── da-dk.js
│   ├── de-de.js
│   ├── el-el.js
│   ├── en-en.js
│   ├── es-es.js
│   ├── et-ee.js
│   ├── fa_ir.js
│   ├── fi-fi.js
│   ├── fr-fr.js
│   ├── he-he.js
│   ├── hr-hr.js
│   ├── hu-hu.js
│   ├── id-id.js
│   ├── it-it.js
│   ├── ja-ja.js
│   ├── km-km.js
│   ├── ko-kr.js
│   ├── ku-ckb.js
│   ├── kur-ckb.js
│   ├── lt-lt.js
│   ├── lus-lus.js
│   ├── lv-lv.js
│   ├── nl-nl.js
│   ├── no_nb.js
│   ├── pl-pl.js
│   ├── pt-br.js
│   ├── pt-pt.js
│   ├── ro-ro.js
│   ├── ru-ru.js
│   ├── sk-sk.js
│   ├── sl-si.js
│   ├── sr-rs.js
│   ├── sv_se.js
│   ├── tr-tr.js
│   ├── uk-ua.js
│   ├── ur-ur.js
│   ├── vi-vi.js
│   ├── zh-cn.js
│   ├── zh-hk.js
│   └── zh-tw.js
├── package.json
├── rollup.config.js
├── rollup.scripts.js
├── src/
│   ├── css/
│   │   ├── assistant.css
│   │   ├── browser.css
│   │   ├── data.css
│   │   ├── drip.css
│   │   ├── drop-label.css
│   │   ├── file-action-button.css
│   │   ├── file-info.css
│   │   ├── file-status.css
│   │   ├── file-wrapper.css
│   │   ├── file.css
│   │   ├── hopper.css
│   │   ├── item-order.css
│   │   ├── item.css
│   │   ├── list-scroller.css
│   │   ├── list.css
│   │   ├── modifiers.css
│   │   ├── panel-root.css
│   │   ├── panel.css
│   │   ├── progress-indicator.css
│   │   ├── root-order.css
│   │   ├── root.css
│   │   └── styles.css
│   └── js/
│       ├── __tests__/
│       │   ├── addFile.test.js
│       │   ├── callbacks.test.js
│       │   ├── contentDisposition.test.js
│       │   ├── createInstance.test.js
│       │   ├── removeFile.test.js
│       │   ├── revertUploadOnRemove.test.js
│       │   ├── server.test.js
│       │   ├── setFiles.test.js
│       │   └── windowMatchMedia.mock
│       ├── app/
│       │   ├── actions.js
│       │   ├── enum/
│       │   │   ├── ChunkStatus.js
│       │   │   ├── FileOrigin.js
│       │   │   ├── InteractionMethod.js
│       │   │   ├── ItemStatus.js
│       │   │   ├── Key.js
│       │   │   ├── Status.js
│       │   │   └── Type.js
│       │   ├── frame/
│       │   │   ├── createPainter.js
│       │   │   ├── createRoute.js
│       │   │   ├── createStore.js
│       │   │   ├── createView.js
│       │   │   ├── index.js
│       │   │   ├── mixins/
│       │   │   │   ├── animations.js
│       │   │   │   ├── apis.js
│       │   │   │   ├── index.js
│       │   │   │   ├── listeners.js
│       │   │   │   ├── styles.js
│       │   │   │   └── utils/
│       │   │   │       └── addGetSet.js
│       │   │   └── utils/
│       │   │       ├── AxisEnum.js
│       │   │       ├── addEvent.js
│       │   │       ├── animators/
│       │   │       │   ├── easing.js
│       │   │       │   ├── spring.js
│       │   │       │   └── tween.js
│       │   │       ├── appendChild.js
│       │   │       ├── appendChildView.js
│       │   │       ├── createAnimator.js
│       │   │       ├── createElement.js
│       │   │       ├── getChildCount.js
│       │   │       ├── getViewRect.js
│       │   │       ├── removeChildView.js
│       │   │       ├── removeEvent.js
│       │   │       └── updateRect.js
│       │   ├── index.js
│       │   ├── options.js
│       │   ├── queries.js
│       │   ├── utils/
│       │   │   ├── buildURL.js
│       │   │   ├── convertTo.js
│       │   │   ├── createDragHelper.js
│       │   │   ├── createFetchFunction.js
│       │   │   ├── createFileLoader.js
│       │   │   ├── createFileProcessor.js
│       │   │   ├── createFileProcessorFunction.js
│       │   │   ├── createFileStub.js
│       │   │   ├── createHopper.js
│       │   │   ├── createInitialState.js
│       │   │   ├── createItem.js
│       │   │   ├── createItemAPI.js
│       │   │   ├── createOption.js
│       │   │   ├── createOptionAPI.js
│       │   │   ├── createOptionActions.js
│       │   │   ├── createOptionQueries.js
│       │   │   ├── createOptions.js
│       │   │   ├── createPaster.js
│       │   │   ├── createPerceivedPerformanceUpdater.js
│       │   │   ├── createProcessorFunction.js
│       │   │   ├── createRevertFunction.js
│       │   │   ├── createServerAPI.js
│       │   │   ├── dnd.js
│       │   │   ├── dropAreaDimensions.js
│       │   │   ├── dynamicLabel.js
│       │   │   ├── fetchBlob.js
│       │   │   ├── getActiveItems.js
│       │   │   ├── getItemById.js
│       │   │   ├── getItemByQuery.js
│       │   │   ├── getItemIndexByPosition.js
│       │   │   ├── getItemIndexByQuery.js
│       │   │   ├── getItemsPerRow.js
│       │   │   ├── getType.js
│       │   │   ├── getValueByType.js
│       │   │   ├── hasRoomForItem.js
│       │   │   ├── insertItem.js
│       │   │   ├── isAPI.js
│       │   │   ├── mergeOptionObject.js
│       │   │   ├── on.js
│       │   │   ├── processFileChunked.js
│       │   │   ├── removeReleasedItems.js
│       │   │   ├── requestDataTransferItems.js
│       │   │   └── toServerAPI.js
│       │   └── view/
│       │       ├── assistant.js
│       │       ├── blob.js
│       │       ├── browser.js
│       │       ├── data.js
│       │       ├── drip.js
│       │       ├── dropLabel.js
│       │       ├── file.js
│       │       ├── fileActionButton.js
│       │       ├── fileInfo.js
│       │       ├── fileStatus.js
│       │       ├── fileWrapper.js
│       │       ├── item.js
│       │       ├── list.js
│       │       ├── listScroller.js
│       │       ├── panel.js
│       │       ├── progressIndicator.js
│       │       └── root.js
│       ├── createApp.js
│       ├── createAppAPI.js
│       ├── createAppAtElement.js
│       ├── createAppObject.js
│       ├── createAppPlugin.js
│       ├── filter.js
│       ├── index.js
│       └── utils/
│           ├── arrayInsert.js
│           ├── arrayRemove.js
│           ├── arrayReverse.js
│           ├── attr.js
│           ├── attrToggle.js
│           ├── canUpdateFileInput.js
│           ├── capitalizeFirstLetter.js
│           ├── composeObject.js
│           ├── copyFile.js
│           ├── copyObjectPropertiesToObject.js
│           ├── createBlob.js
│           ├── createDefaultResponse.js
│           ├── createElement.js
│           ├── createObject.js
│           ├── createResponse.js
│           ├── createWorker.js
│           ├── debounce.js
│           ├── deepCloneObject.js
│           ├── defineProperty.js
│           ├── describeArc.js
│           ├── forEachDelayed.js
│           ├── forin.js
│           ├── formatFilename.js
│           ├── fromCamels.js
│           ├── getAttributesAsObject.js
│           ├── getBase64DataFromBase64DataURI.js
│           ├── getBlobBuilder.js
│           ├── getBlobFromBase64DataURI.js
│           ├── getBlobFromByteStringWithMimeType.js
│           ├── getByteStringFromBase64DataURI.js
│           ├── getDateString.js
│           ├── getDecimalSeparator.js
│           ├── getDomainFromURL.js
│           ├── getExtensionFromFilename.js
│           ├── getFileFromBase64DataURI.js
│           ├── getFileFromBlob.js
│           ├── getFileInfoFromHeaders.js
│           ├── getFilenameFromURL.js
│           ├── getFilenameWithoutExtension.js
│           ├── getMimeTypeFromBase64DataURI.js
│           ├── getNonNumeric.js
│           ├── getNumericAspectRatioFromString.js
│           ├── getParameters.js
│           ├── getRandomNumber.js
│           ├── getRootNode.js
│           ├── getThousandsSeparator.js
│           ├── getUniqueId.js
│           ├── guesstimateExtension.js
│           ├── guesstimateMimeType.js
│           ├── hasQueryString.js
│           ├── insertAfter.js
│           ├── insertBefore.js
│           ├── isArray.js
│           ├── isBase64DataURI.js
│           ├── isBoolean.js
│           ├── isBrowser.js
│           ├── isDefined.js
│           ├── isEmpty.js
│           ├── isExternalURL.js
│           ├── isFile.js
│           ├── isFunction.js
│           ├── isIOS.js
│           ├── isInt.js
│           ├── isNode.js
│           ├── isNull.js
│           ├── isNumber.js
│           ├── isObject.js
│           ├── isString.js
│           ├── leftPad.js
│           ├── limit.js
│           ├── loadImage.js
│           ├── lowerCaseFirstLetter.js
│           ├── percentageArc.js
│           ├── polarToCartesian.js
│           ├── renameFile.js
│           ├── replaceInString.js
│           ├── resetFileInput.js
│           ├── sendRequest.js
│           ├── setInputFiles.js
│           ├── text.js
│           ├── toArray.js
│           ├── toBoolean.js
│           ├── toBytes.js
│           ├── toCamels.js
│           ├── toFloat.js
│           ├── toFunctionReference.js
│           ├── toInt.js
│           ├── toNaturalFileSize.js
│           ├── toNumber.js
│           ├── toPercentage.js
│           ├── toString.js
│           └── trim.js
└── types/
    ├── index.d.ts
    └── tsconfig.json
Download .txt
SYMBOL INDEX (148 symbols across 11 files)

FILE: dist/filepond.esm.js
  constant IS_BROWSER (line 187) | const IS_BROWSER = (() =>
  constant PRIVATE (line 1662) | const PRIVATE = [
  constant ITEM_ERROR (line 2038) | const ITEM_ERROR = [
  constant ITEM_BUSY (line 2043) | const ITEM_BUSY = [
  constant ITEM_READY (line 2049) | const ITEM_READY = [ItemStatus.PROCESSING_COMPLETE];
  constant PANEL_SPRING_PROPS (line 5882) | const PANEL_SPRING_PROPS = { type: 'spring', damping: 0.6, mass: 7 };
  constant ITEM_TRANSLATE_SPRING (line 5986) | const ITEM_TRANSLATE_SPRING = {
  constant ITEM_SCALE_SPRING (line 5993) | const ITEM_SCALE_SPRING = 'spring';
  method getHeight (line 6292) | get getHeight() {
  method setHeight (line 6295) | set setHeight(val) {
  method getWidth (line 6298) | get getWidth() {
  method setWidth (line 6301) | set setWidth(val) {
  constant MAX_FILES_LIMIT (line 8010) | const MAX_FILES_LIMIT = 1000000;
  constant PRIVATE_METHODS (line 9446) | const PRIVATE_METHODS = ['fire', '_read', '_write'];

FILE: dist/filepond.js
  function _typeof (line 1881) | function _typeof(obj) {
  function _jsx (line 1902) | function _jsx(type, props, key, children) {
  function _asyncIterator (line 1950) | function _asyncIterator(iterable) {
  function _AwaitValue (line 1968) | function _AwaitValue(value) {
  function _AsyncGenerator (line 1972) | function _AsyncGenerator(gen) {
  function _wrapAsyncGenerator (line 2072) | function _wrapAsyncGenerator(fn) {
  function _awaitAsyncGenerator (line 2078) | function _awaitAsyncGenerator(value) {
  function _asyncGeneratorDelegate (line 2082) | function _asyncGeneratorDelegate(inner, awaitWrap) {
  function asyncGeneratorStep (line 2132) | function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, ar...
  function _asyncToGenerator (line 2148) | function _asyncToGenerator(fn) {
  function _classCallCheck (line 2168) | function _classCallCheck(instance, Constructor) {
  function _defineProperties (line 2174) | function _defineProperties(target, props) {
  function _createClass (line 2184) | function _createClass(Constructor, protoProps, staticProps) {
  function _defineEnumerableProperties (line 2190) | function _defineEnumerableProperties(obj, descs) {
  function _defaults (line 2213) | function _defaults(obj, defaults) {
  function _defineProperty (line 2228) | function _defineProperty(obj, key, value) {
  function _extends (line 2243) | function _extends() {
  function _objectSpread (line 2263) | function _objectSpread(target) {
  function ownKeys (line 2284) | function ownKeys(object, enumerableOnly) {
  function _objectSpread2 (line 2299) | function _objectSpread2(target) {
  function _inherits (line 2323) | function _inherits(subClass, superClass) {
  function _inheritsLoose (line 2338) | function _inheritsLoose(subClass, superClass) {
  function _getPrototypeOf (line 2344) | function _getPrototypeOf(o) {
  function _setPrototypeOf (line 2353) | function _setPrototypeOf(o, p) {
  function isNativeReflectConstruct (line 2364) | function isNativeReflectConstruct() {
  function _construct (line 2377) | function _construct(Parent, args, Class) {
  function _isNativeFunction (line 2394) | function _isNativeFunction(fn) {
  function _wrapNativeSuper (line 2398) | function _wrapNativeSuper(Class) {
  function _instanceof (line 2432) | function _instanceof(left, right) {
  function _interopRequireDefault (line 2440) | function _interopRequireDefault(obj) {
  function _interopRequireWildcard (line 2448) | function _interopRequireWildcard(obj) {
  function _newArrowCheck (line 2476) | function _newArrowCheck(innerThis, boundThis) {
  function _objectDestructuringEmpty (line 2482) | function _objectDestructuringEmpty(obj) {
  function _objectWithoutPropertiesLoose (line 2486) | function _objectWithoutPropertiesLoose(source, excluded) {
  function _objectWithoutProperties (line 2501) | function _objectWithoutProperties(source, excluded) {
  function _assertThisInitialized (line 2522) | function _assertThisInitialized(self) {
  function _possibleConstructorReturn (line 2530) | function _possibleConstructorReturn(self, call) {
  function _superPropBase (line 2538) | function _superPropBase(object, property) {
  function _get (line 2547) | function _get(target, property, receiver) {
  function set (line 2568) | function set(target, property, value, receiver) {
  function _set (line 2608) | function _set(target, property, value, receiver, isStrict) {
  function _taggedTemplateLiteral (line 2618) | function _taggedTemplateLiteral(strings, raw) {
  function _taggedTemplateLiteralLoose (line 2632) | function _taggedTemplateLiteralLoose(strings, raw) {
  function _temporalRef (line 2641) | function _temporalRef(val, name) {
  function _readOnlyError (line 2649) | function _readOnlyError(name) {
  function _classNameTDZError (line 2653) | function _classNameTDZError(name) {
  function _slicedToArray (line 2659) | function _slicedToArray(arr, i) {
  function _slicedToArrayLoose (line 2663) | function _slicedToArrayLoose(arr, i) {
  function _toArray (line 2667) | function _toArray(arr) {
  function _toConsumableArray (line 2671) | function _toConsumableArray(arr) {
  function _arrayWithoutHoles (line 2675) | function _arrayWithoutHoles(arr) {
  function _arrayWithHoles (line 2683) | function _arrayWithHoles(arr) {
  function _iterableToArray (line 2687) | function _iterableToArray(iter) {
  function _iterableToArrayLimit (line 2695) | function _iterableToArrayLimit(arr, i) {
  function _iterableToArrayLimitLoose (line 2721) | function _iterableToArrayLimitLoose(arr, i) {
  function _nonIterableSpread (line 2733) | function _nonIterableSpread() {
  function _nonIterableRest (line 2737) | function _nonIterableRest() {
  function _skipFirstGeneratorNext (line 2741) | function _skipFirstGeneratorNext(fn) {
  function _toPrimitive (line 2749) | function _toPrimitive(input, hint) {
  function _toPropertyKey (line 2762) | function _toPropertyKey(arg) {
  function _initializerWarningHelper (line 2768) | function _initializerWarningHelper(descriptor, context) {
  function _initializerDefineProperty (line 2777) | function _initializerDefineProperty(target, property, descriptor, contex...
  function _applyDecoratedDescriptor (line 2787) | function _applyDecoratedDescriptor(target, property, decorators, descrip...
  function _classPrivateFieldLooseKey (line 2821) | function _classPrivateFieldLooseKey(name) {
  function _classPrivateFieldLooseBase (line 2825) | function _classPrivateFieldLooseBase(receiver, privateKey) {
  function _classPrivateFieldGet (line 2833) | function _classPrivateFieldGet(receiver, privateMap) {
  function _classPrivateFieldSet (line 2847) | function _classPrivateFieldSet(receiver, privateMap, value) {
  function _classPrivateFieldDestructureSet (line 2867) | function _classPrivateFieldDestructureSet(receiver, privateMap) {
  function _classStaticPrivateFieldSpecGet (line 2893) | function _classStaticPrivateFieldSpecGet(receiver, classConstructor, des...
  function _classStaticPrivateFieldSpecSet (line 2901) | function _classStaticPrivateFieldSpecSet(receiver, classConstructor, des...
  function _classStaticPrivateMethodGet (line 2914) | function _classStaticPrivateMethodGet(receiver, classConstructor, method) {
  function _classStaticPrivateMethodSet (line 2922) | function _classStaticPrivateMethodSet() {
  function _decorate (line 2926) | function _decorate(decorators, factory, superClass, mixins) {
  function _getDecoratorsApi (line 2946) | function _getDecoratorsApi() {
  function _createElementDescriptor (line 3261) | function _createElementDescriptor(def) {
  function _coalesceGetterSetter (line 3304) | function _coalesceGetterSetter(element, other) {
  function _coalesceClassElements (line 3312) | function _coalesceClassElements(elements) {
  function _hasDecorators (line 3360) | function _hasDecorators(element) {
  function _isDataDescriptor (line 3364) | function _isDataDescriptor(desc) {
  function _optionalCallableProperty (line 3368) | function _optionalCallableProperty(obj, name) {
  function _classPrivateMethodGet (line 3378) | function _classPrivateMethodGet(receiver, privateSet, fn) {
  function _classPrivateMethodSet (line 3386) | function _classPrivateMethodSet() {
  function _wrapRegExp (line 3390) | function _wrapRegExp(re, groups) {
  method getHeight (line 8876) | get getHeight() {
  method setHeight (line 8879) | set setHeight(val) {
  method getWidth (line 8882) | get getWidth() {
  method setWidth (line 8885) | set setWidth(val) {

FILE: src/js/app/queries.js
  constant ITEM_ERROR (line 10) | const ITEM_ERROR = [
  constant ITEM_BUSY (line 15) | const ITEM_BUSY = [
  constant ITEM_READY (line 21) | const ITEM_READY = [ItemStatus.PROCESSING_COMPLETE];

FILE: src/js/app/utils/createItemAPI.js
  constant PRIVATE (line 3) | const PRIVATE = [

FILE: src/js/app/utils/dropAreaDimensions.js
  method getHeight (line 4) | get getHeight() {
  method setHeight (line 7) | set setHeight(val) {
  method getWidth (line 10) | get getWidth() {
  method setWidth (line 13) | set setWidth(val) {

FILE: src/js/app/view/item.js
  constant ITEM_TRANSLATE_SPRING (line 6) | const ITEM_TRANSLATE_SPRING = {
  constant ITEM_SCALE_SPRING (line 13) | const ITEM_SCALE_SPRING = 'spring';

FILE: src/js/app/view/panel.js
  constant PANEL_SPRING_PROPS (line 4) | const PANEL_SPRING_PROPS = { type: 'spring', damping: 0.6, mass: 7 };

FILE: src/js/app/view/root.js
  constant MAX_FILES_LIMIT (line 23) | const MAX_FILES_LIMIT = 1000000;

FILE: src/js/createAppAPI.js
  constant PRIVATE_METHODS (line 2) | const PRIVATE_METHODS = ['fire', '_read', '_write'];

FILE: src/js/utils/isBrowser.js
  constant IS_BROWSER (line 1) | const IS_BROWSER = (() => typeof window !== 'undefined' && typeof window...

FILE: types/index.d.ts
  type FileStatus (line 9) | enum FileStatus {
  type Status (line 21) | enum Status {
  type FileOrigin (line 29) | enum FileOrigin {
  type ActualFileObject (line 39) | type ActualFileObject = Blob & { readonly lastModified: number; readonly...
  class FilePondFile (line 44) | class FilePondFile {
  class File (line 88) | class File extends FilePondFile {}
  type ServerUrl (line 90) | interface ServerUrl {
  type ProgressServerConfigFunction (line 114) | type ProgressServerConfigFunction = (
  type ProcessServerChunkTransferOptions (line 127) | interface ProcessServerChunkTransferOptions {
  type ProcessServerConfigFunction (line 148) | type ProcessServerConfigFunction = (
  type RevertServerConfigFunction (line 179) | type RevertServerConfigFunction = (
  type RestoreServerConfigFunction (line 188) | type RestoreServerConfigFunction = (
  type LoadServerConfigFunction (line 209) | type LoadServerConfigFunction = (
  type FetchServerConfigFunction (line 229) | type FetchServerConfigFunction = (
  type RemoveServerConfigFunction (line 249) | type RemoveServerConfigFunction = (
  type FilePondInitialFile (line 258) | interface FilePondInitialFile {
  type FilePondServerConfigProps (line 275) | interface FilePondServerConfigProps {
  type FilePondDragDropProps (line 337) | interface FilePondDragDropProps {
  type FilePondLabelProps (line 362) | interface FilePondLabelProps {
  type FilePondSvgIconProps (line 534) | interface FilePondSvgIconProps {
  type FilePondErrorDescription (line 562) | interface FilePondErrorDescription {
  type FilePondCallbackProps (line 568) | interface FilePondCallbackProps {
  type FilePondHookProps (line 617) | interface FilePondHookProps {
  type FilePondStyleProps (line 638) | interface FilePondStyleProps {
  type CaptureAttribute (line 691) | type CaptureAttribute = 'camera' | 'microphone' | 'camcorder';
  type FilePondBaseProps (line 693) | interface FilePondBaseProps {
  type FilePondOptionProps (line 840) | interface FilePondOptionProps
  type FilePondOptions (line 850) | interface FilePondOptions
  type FilePondEventPrefixed (line 860) | type FilePondEventPrefixed =
  type FilePondEvent (line 877) | type FilePondEvent =
  type RemoveFileOptions (line 894) | interface RemoveFileOptions {
  type FilePond (line 899) | interface FilePond extends Required<FilePondOptions> {}
  class FilePond (line 901) | class FilePond {
Condensed preview — 298 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,378K chars).
[
  {
    "path": ".babelrc",
    "chars": 663,
    "preview": "{\n    \"retainLines\": true,\n    \"presets\": [\n        [\"@babel/preset-env\", {\n            \"exclude\": [\"transform-typeof-sy"
  },
  {
    "path": ".gitattributes",
    "chars": 31,
    "preview": "dist/* linguist-vendored=false\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 54,
    "preview": "custom: ['https://www.buymeacoffee.com/rikschennink']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 1733,
    "preview": "name: Bug report\ntitle: \"[Bug] \"\ndescription: Report an issue with FilePond\nlabels: bug\nbody:\n  - type: markdown\n    att"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 196,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Stack Overflow\n    url: https://stackoverflow.com/questions/tagged/"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1246,
    "preview": "name: Feature request\ntitle: \"[Feature] \"\ndescription: Suggest an idea for this project\nlabels: new feature\nbody:\n  - ty"
  },
  {
    "path": ".gitignore",
    "chars": 92,
    "preview": "npm-debug.log\nnode_modules/\ncoverage/\n.idea\n.vscode/\nupload/\ntest*.html\n_TODO.md\n_RELEASE.md"
  },
  {
    "path": ".nvmrc",
    "chars": 8,
    "preview": "v10.15.3"
  },
  {
    "path": ".prettierignore",
    "chars": 6,
    "preview": "dist/*"
  },
  {
    "path": ".prettierrc",
    "chars": 164,
    "preview": "{\n    \"svelteSortOrder\": \"scripts-markup-styles\",\n    \"trailingComma\": \"es5\",\n    \"tabWidth\": 4,\n    \"printWidth\": 100,\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 29128,
    "preview": "# Changelog\n\n## 4.32.12\n\n-   Link attribution banner to [filepond.com](https://filepond.com)\n\n## 4.32.11\n\n-   Merge fix "
  },
  {
    "path": "LICENSE",
    "chars": 1062,
    "preview": "MIT License\n\nCopyright (c) 2019 Pqina\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
  },
  {
    "path": "README.md",
    "chars": 12010,
    "preview": "# [<img src=\"https://github.com/pqina/filepond-github-assets/blob/master/logo.svg\" height=\"44\" alt=\"FilePond\"/>](https:/"
  },
  {
    "path": "banner-cli.js",
    "chars": 281,
    "preview": "const banner = require('./banner');\nconst pkg = require('./package.json');\nconst args = process.argv.slice(2);\nprocess.s"
  },
  {
    "path": "banner.js",
    "chars": 235,
    "preview": "module.exports = ({ id, version, license, homepage }) => `/*!\n * ${ id } ${ version }\n * Licensed under ${ license }, ht"
  },
  {
    "path": "dist/filepond.css",
    "chars": 26774,
    "preview": "/*!\n * FilePond 4.32.12\n * Licensed under MIT, https://opensource.org/licenses/MIT/\n * Please visit https://pqina.nl/fil"
  },
  {
    "path": "dist/filepond.esm.js",
    "chars": 296174,
    "preview": "/*!\n * FilePond 4.32.12\n * Licensed under MIT, https://opensource.org/licenses/MIT/\n * Please visit https://pqina.nl/fil"
  },
  {
    "path": "dist/filepond.js",
    "chars": 439131,
    "preview": "/*!\n * FilePond 4.32.12\n * Licensed under MIT, https://opensource.org/licenses/MIT/\n * Please visit https://pqina.nl/fil"
  },
  {
    "path": "index.html",
    "chars": 1769,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"widt"
  },
  {
    "path": "jest.config.js",
    "chars": 133,
    "preview": "module.exports = {\n    bail: true,\n    verbose: true,\n    setupFiles: ['<rootDir>/jest.stubs.js'],\n    roots: ['<rootDir"
  },
  {
    "path": "jest.stubs.js",
    "chars": 298,
    "preview": "const uuid = require('uuid/v4');\n\n// needed because jest doesn't have CSS on window\nif (!window.CSS) window.CSS = {};\nwi"
  },
  {
    "path": "locale/am-et.js",
    "chars": 2145,
    "preview": "export default {\n    labelIdle: 'ፋይሎች ስበው እዚህ ጋር ይልቀቁት ወይም ፋይሉን <span class=\"filepond--label-action\"> ይምረጡ </span>',\n   "
  },
  {
    "path": "locale/ar-ar.js",
    "chars": 2089,
    "preview": "export default {\n    labelIdle: 'اسحب و ادرج ملفاتك أو <span class=\"filepond--label-action\"> تصفح </span>',\n    labelInv"
  },
  {
    "path": "locale/az-az.js",
    "chars": 2258,
    "preview": "export default {\n    labelIdle: 'Faylınızı Sürüşdürün & Buraxın ya da <span class=\"filepond--label-action\"> Seçin </span"
  },
  {
    "path": "locale/ca-ca.js",
    "chars": 2262,
    "preview": "export default {\n    labelIdle: `Arrossega i deixa anar els teus fitxers o <span class=\"filepond--label-action\"> Navega "
  },
  {
    "path": "locale/cs-cz.js",
    "chars": 2280,
    "preview": "export default {\n    labelIdle: 'Přetáhněte soubor sem (drag&drop) nebo <span class=\"filepond--label-action\"> Vyhledat <"
  },
  {
    "path": "locale/cy-cy.js",
    "chars": 2322,
    "preview": "export default {\n    labelIdle: 'Llusgwch a Gollyngwch eich ffeiliau neu ddefnyddiwch y  <span class=\"filepond--label-ac"
  },
  {
    "path": "locale/da-dk.js",
    "chars": 2192,
    "preview": "export default {\n    labelIdle:\n        'Træk & slip filer eller <span class = \"filepond - label-action\"> Gennemse </spa"
  },
  {
    "path": "locale/de-de.js",
    "chars": 2209,
    "preview": "export default {\n    labelIdle: 'Dateien ablegen oder <span class=\"filepond--label-action\"> auswählen </span>',\n    labe"
  },
  {
    "path": "locale/el-el.js",
    "chars": 2453,
    "preview": "export default {\n    labelIdle: 'Σύρετε τα αρχεία σας στο πλαίσιο ή <span class=\"filepond--label-action\"> Επιλέξτε </spa"
  },
  {
    "path": "locale/en-en.js",
    "chars": 2130,
    "preview": "export default {\n    labelIdle: 'Drag & Drop your files or <span class=\"filepond--label-action\"> Browse </span>',\n    la"
  },
  {
    "path": "locale/es-es.js",
    "chars": 2314,
    "preview": "export default {\n    labelIdle: 'Arrastra y suelta tus archivos o <span class = \"filepond--label-action\"> Examina <span>"
  },
  {
    "path": "locale/et-ee.js",
    "chars": 2268,
    "preview": "export default {\n    labelIdle: 'Lohista oma failid siia või <span class=\"filepond--label-action\"> Sirvi </span>',\n    l"
  },
  {
    "path": "locale/fa_ir.js",
    "chars": 2181,
    "preview": "export default {\n    labelIdle: 'فایل را اینجا بکشید و رها کنید، یا <span class=\"filepond--label-action\"> جستجو کنید </s"
  },
  {
    "path": "locale/fi-fi.js",
    "chars": 2250,
    "preview": "export default {\n    labelIdle: 'Vedä ja pudota tiedostoja tai <span class=\"filepond--label-action\"> Selaa </span>',\n   "
  },
  {
    "path": "locale/fr-fr.js",
    "chars": 2383,
    "preview": "export default {\n    labelIdle: 'Faites glisser vos fichiers ou <span class = \"filepond--label-action\"> Parcourir </span"
  },
  {
    "path": "locale/he-he.js",
    "chars": 2245,
    "preview": "export default {\n    labelIdle: 'גרור ושחרר את הקבצים כאן או <span class=\"filepond--label-action\"> לחץ כאן לבחירה </span"
  },
  {
    "path": "locale/hr-hr.js",
    "chars": 2294,
    "preview": "export default {\n    labelIdle: 'Ovdje \"ispusti\" datoteku ili <span class=\"filepond--label-action\"> Pretraži </span>',\n "
  },
  {
    "path": "locale/hu-hu.js",
    "chars": 2332,
    "preview": "export default {\n    labelIdle: 'Mozgasd ide a fájlt a feltöltéshez, vagy <span class=\"filepond--label-action\"> tallózás"
  },
  {
    "path": "locale/id-id.js",
    "chars": 2316,
    "preview": "export default {\n    labelIdle: 'Seret & Jatuhkan berkas Anda atau <span class=\"filepond--label-action\">Jelajahi</span>'"
  },
  {
    "path": "locale/it-it.js",
    "chars": 2377,
    "preview": "export default {\n    labelIdle: 'Trascina e rilascia i tuoi file oppure <span class=\"filepond--label-action\"> Sfoglia <s"
  },
  {
    "path": "locale/ja-ja.js",
    "chars": 4335,
    "preview": "export default {\n    labelIdle: 'ファイルをドラッグ&ドロップ又は<span class=\"filepond--label-action\">ファイル選択</span>',\n    labelInvalidFi"
  },
  {
    "path": "locale/km-km.js",
    "chars": 2184,
    "preview": "export default {\n    labelIdle: 'ទាញ&ដាក់ហ្វាល់ឯកសាររបស់អ្នក ឬ <span class=\"filepond--label-action\"> ស្វែងរក </span>',\n "
  },
  {
    "path": "locale/ko-kr.js",
    "chars": 1891,
    "preview": "export default {\n    labelIdle: '파일을 드래그 하거나 <span class=\"filepond--label-action\"> 찾아보기 </span>',\n    labelInvalidField:"
  },
  {
    "path": "locale/ku-ckb.js",
    "chars": 2218,
    "preview": "export default {\n    labelIdle: 'پەڕگەکان فڕێ بدە ئێرە بۆ بارکردن یان <span class=\"filepond--label-action\"> هەڵبژێرە </s"
  },
  {
    "path": "locale/kur-ckb.js",
    "chars": 2396,
    "preview": "export default {\n    labelIdle:\n      'فایلەکانت ڕابکێشە یان <span class=\"filepond--label-action\"> دیاری بکە </span>',\n "
  },
  {
    "path": "locale/lt-lt.js",
    "chars": 2257,
    "preview": "export default {\n    labelIdle: 'Įdėkite failus čia arba <span class=\"filepond--label-action\"> Ieškokite </span>',\n    l"
  },
  {
    "path": "locale/lus-lus.js",
    "chars": 2296,
    "preview": "export default {\n    labelIdle: 'I file hnûklût rawh, emaw <span class=\"filepond--label-action\"> Zawnna </span>',\n    la"
  },
  {
    "path": "locale/lv-lv.js",
    "chars": 2376,
    "preview": "export default {\n    labelIdle: 'Ievelciet savus failus vai <span class=\"filepond--label-action\"> pārlūkojiet šeit </spa"
  },
  {
    "path": "locale/nl-nl.js",
    "chars": 2276,
    "preview": "export default {\n    labelIdle: 'Drag & Drop je bestanden of <span class=\"filepond--label-action\"> Bladeren </span>',\n  "
  },
  {
    "path": "locale/no_nb.js",
    "chars": 2243,
    "preview": "export default {\n    labelIdle: 'Dra og slipp filene dine, eller <span class=\"filepond--label-action\"> Bla gjennom... </"
  },
  {
    "path": "locale/pl-pl.js",
    "chars": 2212,
    "preview": "export default {\n    labelIdle: 'Przeciągnij i upuść lub <span class=\"filepond--label-action\">wybierz</span> pliki',\n   "
  },
  {
    "path": "locale/pt-br.js",
    "chars": 2302,
    "preview": "export default {\n    labelIdle: 'Arraste e solte os arquivos ou <span class=\"filepond--label-action\"> Clique aqui </span"
  },
  {
    "path": "locale/pt-pt.js",
    "chars": 2330,
    "preview": "export default {\n    labelIdle: 'Arraste & Largue os ficheiros ou <span class=\"filepond--label-action\"> Seleccione </spa"
  },
  {
    "path": "locale/ro-ro.js",
    "chars": 2350,
    "preview": "export default {\n    labelIdle: 'Trage și plasează fișiere sau <span class=\"filepond--label-action\"> Caută-le </span>',\n"
  },
  {
    "path": "locale/ru-ru.js",
    "chars": 2316,
    "preview": "export default {\n    labelIdle: 'Перетащите файлы или <span class=\"filepond--label-action\"> выберите </span>',\n    label"
  },
  {
    "path": "locale/sk-sk.js",
    "chars": 2273,
    "preview": "export default {\n    labelIdle: 'Natiahnúť súbor (drag&drop) alebo <span class=\"filepond--label-action\"> Vyhľadať </span"
  },
  {
    "path": "locale/sl-si.js",
    "chars": 2274,
    "preview": "export default {\n    labelIdle: 'Povleci in spusti ali <span class=\"filepond--label-action\">izberi</span>',\n    labelInv"
  },
  {
    "path": "locale/sr-rs.js",
    "chars": 2285,
    "preview": "export default {\n    labelIdle: 'Овде \"испустите\" датотеку или <span class=\"filepond--label-action\"> Претражи </span>',\n"
  },
  {
    "path": "locale/sv_se.js",
    "chars": 2272,
    "preview": "export default {\n    labelIdle: 'Drag och släpp dina filer eller <span class=\"filepond--label-action\"> Bläddra </span>',"
  },
  {
    "path": "locale/tr-tr.js",
    "chars": 2235,
    "preview": "export default {\n    labelIdle: 'Dosyanızı Sürükleyin & Bırakın ya da <span class=\"filepond--label-action\"> Seçin </span"
  },
  {
    "path": "locale/uk-ua.js",
    "chars": 2356,
    "preview": "export default {\n    labelIdle: 'Перетягніть файли або <span class=\"filepond--label-action\"> виберіть </span>',\n    labe"
  },
  {
    "path": "locale/ur-ur.js",
    "chars": 2256,
    "preview": "export default {\n    labelIdle: 'اپنی فائلز ڈریگ کریں یا <span class=\"filepond--label-action\"> براؤز کریں </span>',\n    "
  },
  {
    "path": "locale/vi-vi.js",
    "chars": 2196,
    "preview": "export default {\n    labelIdle: 'Kéo thả tệp của bạn hoặc <span class=\"filepond--label-action\"> Tìm kiếm </span>',\n    l"
  },
  {
    "path": "locale/zh-cn.js",
    "chars": 1711,
    "preview": "export default {\n    labelIdle: '拖放文件,或者 <span class=\"filepond--label-action\"> 浏览 </span>',\n    labelInvalidField: '字段包含"
  },
  {
    "path": "locale/zh-hk.js",
    "chars": 1718,
    "preview": "export default {\n    labelIdle: '拖放檔案,或者 <span class=\"filepond--label-action\"> 瀏覽 </span>',\n    labelInvalidField: '不支援此"
  },
  {
    "path": "locale/zh-tw.js",
    "chars": 1718,
    "preview": "export default {\n    labelIdle: '拖放檔案,或者 <span class=\"filepond--label-action\"> 瀏覽 </span>',\n    labelInvalidField: '不支援此"
  },
  {
    "path": "package.json",
    "chars": 2360,
    "preview": "{\n    \"name\": \"filepond\",\n    \"version\": \"4.32.12\",\n    \"description\": \"FilePond, Where files go to stretch their bits.\""
  },
  {
    "path": "rollup.config.js",
    "chars": 317,
    "preview": "import * as pkg from './package.json';\nimport build from './rollup.scripts';\n\nexport default build(\n\t{\n\t\tid: 'FilePond',"
  },
  {
    "path": "rollup.scripts.js",
    "chars": 1086,
    "preview": "import babel from 'rollup-plugin-babel';\nimport license from 'rollup-plugin-license';\nimport { terser } from 'rollup-plu"
  },
  {
    "path": "src/css/assistant.css",
    "chars": 223,
    "preview": ".filepond--assistant {\n    position: absolute;\n    overflow: hidden;\n    height: 1px;\n    width: 1px;\n    padding: 0;\n  "
  },
  {
    "path": "src/css/browser.css",
    "chars": 419,
    "preview": "/* Hard to override styles */\n.filepond--browser.filepond--browser {\n    /* is positioned absolute so it is focusable fo"
  },
  {
    "path": "src/css/data.css",
    "chars": 193,
    "preview": ".filepond--data {\n    position: absolute;\n    width: 0;\n    height: 0;\n    padding: 0;\n    margin: 0;\n    border: none;\n"
  },
  {
    "path": "src/css/drip.css",
    "chars": 727,
    "preview": ".filepond--drip {\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    overflow: hidden;\n  "
  },
  {
    "path": "src/css/drop-label.css",
    "chars": 1056,
    "preview": ".filepond--drop-label {\n    position: absolute;\n    left: 0;\n    right: 0;\n    top: 0;\n    margin: 0;\n    color: #4f4f4f"
  },
  {
    "path": "src/css/file-action-button.css",
    "chars": 2364,
    "preview": "/* Hard to override styles */\n.filepond--file-action-button.filepond--file-action-button {\n    font-size: 1em;\n    width"
  },
  {
    "path": "src/css/file-info.css",
    "chars": 993,
    "preview": ".filepond--file-info {\n    position: static;\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;"
  },
  {
    "path": "src/css/file-status.css",
    "chars": 735,
    "preview": ".filepond--file-status {\n    position: static;\n    display: flex;\n    flex-direction: column;\n    align-items: flex-end;"
  },
  {
    "path": "src/css/file-wrapper.css",
    "chars": 463,
    "preview": "/* Hard to override styles */\n.filepond--file-wrapper.filepond--file-wrapper {\n    border: none;\n    margin: 0;\n    padd"
  },
  {
    "path": "src/css/file.css",
    "chars": 3887,
    "preview": ".filepond--file {\n    position: static;\n    display: flex;\n    height: 100%;\n    align-items: flex-start;\n\n    padding: "
  },
  {
    "path": "src/css/hopper.css",
    "chars": 403,
    "preview": "/* ignore all other interaction elements while dragging a file */\n.filepond--hopper[data-hopper-state='drag-over'] > * {"
  },
  {
    "path": "src/css/item-order.css",
    "chars": 195,
    "preview": ".filepond--progress-indicator {\n    z-index: 103;\n}\n\n.filepond--file-action-button {\n    z-index: 102;\n}\n\n.filepond--fil"
  },
  {
    "path": "src/css/item.css",
    "chars": 1480,
    "preview": ".filepond--item {\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    z-index: 1;\n\n    padding: 0;\n    ma"
  },
  {
    "path": "src/css/list-scroller.css",
    "chars": 908,
    "preview": "/* normal mode */\n.filepond--list-scroller {\n    position: absolute;\n    top: 0;\n    left: 0;\n    right: 0;\n    margin: "
  },
  {
    "path": "src/css/list.css",
    "chars": 371,
    "preview": "/* hard to overide styles on purpose */\n.filepond--list.filepond--list {\n    position: absolute;\n    top: 0;\n    margin:"
  },
  {
    "path": "src/css/modifiers.css",
    "chars": 2220,
    "preview": ".filepond--root {\n    &[data-style-panel-layout~='integrated'] {\n        width: 100%;\n        height: 100%;\n        max-"
  },
  {
    "path": "src/css/panel-root.css",
    "chars": 83,
    "preview": ".filepond--panel-root {\n    border-radius: 0.5em;\n    background-color: #f1f0ef;\n}\n"
  },
  {
    "path": "src/css/panel.css",
    "chars": 2561,
    "preview": ".filepond--panel {\n    position: absolute;\n    left: 0;\n    top: 0;\n    right: 0;\n    margin: 0;\n\n    /* defaults to 100"
  },
  {
    "path": "src/css/progress-indicator.css",
    "chars": 577,
    "preview": ".filepond--progress-indicator {\n    position: static;\n    width: 1.25em;\n    height: 1.25em;\n\n    color: #fff;\n\n    /* c"
  },
  {
    "path": "src/css/root-order.css",
    "chars": 221,
    "preview": ".filepond--list-scroller {\n    z-index: 6;\n}\n\n.filepond--drop-label {\n    z-index: 5;\n}\n\n.filepond--drip {\n    z-index: "
  },
  {
    "path": "src/css/root.css",
    "chars": 1869,
    "preview": ".filepond--root {\n    /* layout*/\n    box-sizing: border-box;\n    position: relative;\n    margin-bottom: 1em;\n\n    /* ba"
  },
  {
    "path": "src/css/styles.css",
    "chars": 517,
    "preview": "@import 'assistant.css';\n@import 'browser.css';\n@import 'data.css';\n@import 'drip.css';\n@import 'drop-label.css';\n@impor"
  },
  {
    "path": "src/js/__tests__/addFile.test.js",
    "chars": 1062,
    "preview": "import './windowMatchMedia.mock';\nimport { create } from '../index.js';\n\ndescribe('adding files', () => {\n    let pond ="
  },
  {
    "path": "src/js/__tests__/callbacks.test.js",
    "chars": 3760,
    "preview": "import mockConsole from \"jest-mock-console\";\n\nimport './windowMatchMedia.mock';\nimport { create, OptionTypes, FileStatus"
  },
  {
    "path": "src/js/__tests__/contentDisposition.test.js",
    "chars": 1847,
    "preview": "import { getFileNameFromHeader } from '../utils/getFileInfoFromHeaders';\n\ndescribe('parse filename', () => {\n    [\n     "
  },
  {
    "path": "src/js/__tests__/createInstance.test.js",
    "chars": 1056,
    "preview": "import './windowMatchMedia.mock';\nimport { create } from '../index.js';\n\ndescribe('create instance', () => {\n    test('w"
  },
  {
    "path": "src/js/__tests__/removeFile.test.js",
    "chars": 3081,
    "preview": "import './windowMatchMedia.mock';\nimport { create, isSupported } from '../index.js';\n\nconst server = {\n    process: (fie"
  },
  {
    "path": "src/js/__tests__/revertUploadOnRemove.test.js",
    "chars": 5069,
    "preview": "import \"./windowMatchMedia.mock\";\nimport { create } from '../index.js';\nimport { actions } from '../app/actions.js';\n\n/*"
  },
  {
    "path": "src/js/__tests__/server.test.js",
    "chars": 2352,
    "preview": "import './windowMatchMedia.mock';\nimport { create } from '../index.js';\n\ndescribe('setting server property', () => {\n   "
  },
  {
    "path": "src/js/__tests__/setFiles.test.js",
    "chars": 2499,
    "preview": "import './windowMatchMedia.mock';\nimport { create } from '../index.js';\n\nconst next = cb => {\n    setTimeout(() => {\n   "
  },
  {
    "path": "src/js/__tests__/windowMatchMedia.mock",
    "chars": 639,
    "preview": "// Mocking window.matchMedia as described in the jest docks\n// since this is not defined in JSDOM and causes the tests\n/"
  },
  {
    "path": "src/js/app/actions.js",
    "chars": 37512,
    "preview": "import { isEmpty } from '../utils/isEmpty';\nimport { forin } from '../utils/forin';\nimport { fromCamels } from '../utils"
  },
  {
    "path": "src/js/app/enum/ChunkStatus.js",
    "chars": 96,
    "preview": "export const ChunkStatus = {\n\tQUEUED: 0,\n\tCOMPLETE: 1,\n\tPROCESSING: 2,\n\tERROR: 3,\n\tWAITING: 4\n};"
  },
  {
    "path": "src/js/app/enum/FileOrigin.js",
    "chars": 68,
    "preview": "export const FileOrigin = {\n    INPUT:1,\n    LIMBO:2,\n    LOCAL:3\n};"
  },
  {
    "path": "src/js/app/enum/InteractionMethod.js",
    "chars": 104,
    "preview": "export const InteractionMethod = {\n    API: 1,\n    DROP: 2,\n    BROWSE: 3,\n    PASTE: 4,\n    NONE: 5\n};\n"
  },
  {
    "path": "src/js/app/enum/ItemStatus.js",
    "chars": 222,
    "preview": "export const ItemStatus = {\n    INIT: 1,\n    IDLE: 2,\n    PROCESSING_QUEUED: 9,\n    PROCESSING: 3,\n    PROCESSING_COMPLE"
  },
  {
    "path": "src/js/app/enum/Key.js",
    "chars": 53,
    "preview": "export const Key = {\n    ENTER: 13,\n    SPACE: 32\n};\n"
  },
  {
    "path": "src/js/app/enum/Status.js",
    "chars": 184,
    "preview": "export const Status = {\n    EMPTY: 0,\n    IDLE: 1, // waiting\n    ERROR: 2, // a file is in error state\n    BUSY: 3, // "
  },
  {
    "path": "src/js/app/enum/Type.js",
    "chars": 247,
    "preview": "export const Type = {\n    BOOLEAN: 'boolean',\n    INT: 'int',\n    NUMBER: 'number',\n    STRING: 'string',\n    ARRAY: 'ar"
  },
  {
    "path": "src/js/app/frame/createPainter.js",
    "chars": 1663,
    "preview": "export const createPainter = (read, write, fps = 60) => {\n\n    const name = '__framePainter';\n\n    // set global painter"
  },
  {
    "path": "src/js/app/frame/createRoute.js",
    "chars": 381,
    "preview": "export const createRoute = (routes, fn) => ({ root, props, actions = [], timestamp, shouldOptimize }) => {\n    actions.f"
  },
  {
    "path": "src/js/app/frame/createStore.js",
    "chars": 2284,
    "preview": "export const createStore = (initialState, queries = [], actions = []) => {\n    // internal state\n    const state = {\n   "
  },
  {
    "path": "src/js/app/frame/createView.js",
    "chars": 10894,
    "preview": "import { createObject } from '../../utils/createObject';\nimport { createElement } from './utils/createElement';\nimport {"
  },
  {
    "path": "src/js/app/frame/index.js",
    "chars": 246,
    "preview": "import { createStore } from './createStore';\nimport { createView } from './createView';\nimport { createPainter } from '."
  },
  {
    "path": "src/js/app/frame/mixins/animations.js",
    "chars": 1961,
    "preview": "import { createAnimator } from '../utils/createAnimator';\nimport { forin } from '../../../utils/forin';\nimport { addGetS"
  },
  {
    "path": "src/js/app/frame/mixins/apis.js",
    "chars": 220,
    "preview": "import { addGetSet } from './utils/addGetSet';\n\n// add to external api and link to props\n\nexport const apis = ({ mixinCo"
  },
  {
    "path": "src/js/app/frame/mixins/index.js",
    "chars": 230,
    "preview": "import { animations } from './animations';\nimport { listeners } from './listeners';\nimport { apis } from './apis';\nimpor"
  },
  {
    "path": "src/js/app/frame/mixins/listeners.js",
    "chars": 936,
    "preview": "import { addEvent } from '../utils/addEvent';\nimport { removeEvent } from '../utils/removeEvent';\n\n// mixin\nexport const"
  },
  {
    "path": "src/js/app/frame/mixins/styles.js",
    "chars": 4998,
    "preview": "import { getViewRect } from '../utils/getViewRect';\nimport { addGetSet } from './utils/addGetSet';\nimport { isDefined } "
  },
  {
    "path": "src/js/app/frame/mixins/utils/addGetSet.js",
    "chars": 676,
    "preview": "export const addGetSet = (keys, obj, props, overwrite = false) => {\n    obj = Array.isArray(obj) ? obj : [obj];\n    obj."
  },
  {
    "path": "src/js/app/frame/utils/AxisEnum.js",
    "chars": 58,
    "preview": "export const AxisEnum = {\n    X: 0,\n    Y: 1,\n    Z: 2\n};\n"
  },
  {
    "path": "src/js/app/frame/utils/addEvent.js",
    "chars": 94,
    "preview": "export const addEvent = element => (type, fn) => {\n    element.addEventListener(type, fn);\n};\n"
  },
  {
    "path": "src/js/app/frame/utils/animators/easing.js",
    "chars": 114,
    "preview": "export const easeLinear = t => t;\nexport const easeInOutQuad = t => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t);\n"
  },
  {
    "path": "src/js/app/frame/utils/animators/spring.js",
    "chars": 3686,
    "preview": "import { createObject } from '../../../../utils/createObject';\nimport { isNumber } from '../../../../utils/isNumber';\n\n/"
  },
  {
    "path": "src/js/app/frame/utils/animators/tween.js",
    "chars": 2549,
    "preview": "import { createObject } from '../../../../utils/createObject';\nimport { easeInOutQuad } from './easing';\n\nexport const t"
  },
  {
    "path": "src/js/app/frame/utils/appendChild.js",
    "chars": 240,
    "preview": "export const appendChild = parent => (child, index) => {\n    if (typeof index !== 'undefined' && parent.children[index])"
  },
  {
    "path": "src/js/app/frame/utils/appendChildView.js",
    "chars": 237,
    "preview": "export const appendChildView = (parent, childViews) => (view, index) => {\n    \n    if (typeof index !== 'undefined') {\n "
  },
  {
    "path": "src/js/app/frame/utils/createAnimator.js",
    "chars": 857,
    "preview": "import { spring } from './animators/spring';\nimport { tween } from './animators/tween';\n\nconst animator = {\n    spring,\n"
  },
  {
    "path": "src/js/app/frame/utils/createElement.js",
    "chars": 831,
    "preview": "import { forin } from '../../../utils/forin';\nimport { attr } from '../../../utils/attr';\n\nconst ns = 'http://www.w3.org"
  },
  {
    "path": "src/js/app/frame/utils/getChildCount.js",
    "chars": 275,
    "preview": "import { createElement } from './createElement';\nimport { isBrowser } from '../../../utils/isBrowser';\nconst testElement"
  },
  {
    "path": "src/js/app/frame/utils/getViewRect.js",
    "chars": 2165,
    "preview": "export const getViewRect = (elementRect, childViews, offset, scale) => {\n    const left = offset[0] || elementRect.left;"
  },
  {
    "path": "src/js/app/frame/utils/removeChildView.js",
    "chars": 279,
    "preview": "export const removeChildView = (parent, childViews) => view => {\n    // remove from child views\n    childViews.splice(ch"
  },
  {
    "path": "src/js/app/frame/utils/removeEvent.js",
    "chars": 100,
    "preview": "export const removeEvent = element => (type, fn) => {\n    element.removeEventListener(type, fn);\n};\n"
  },
  {
    "path": "src/js/app/frame/utils/updateRect.js",
    "chars": 838,
    "preview": "export const updateRect = (rect = {}, element = {}, style = {}) => {\n\n    if (!element.layoutCalculated) {\n        rect."
  },
  {
    "path": "src/js/app/index.js",
    "chars": 19945,
    "preview": "import { createStore } from './frame/index';\nimport { insertBefore } from '../utils/insertBefore';\nimport { insertAfter "
  },
  {
    "path": "src/js/app/options.js",
    "chars": 10711,
    "preview": "import { getDecimalSeparator } from '../utils/getDecimalSeparator';\nimport { getThousandsSeparator } from '../utils/getT"
  },
  {
    "path": "src/js/app/queries.js",
    "chars": 3339,
    "preview": "import { isObject } from '../utils/isObject';\nimport { isFunction } from '../utils/isFunction';\nimport { getItemByQuery "
  },
  {
    "path": "src/js/app/utils/buildURL.js",
    "chars": 223,
    "preview": "const hasQS = str => /\\?/.test(str);\nexport const buildURL = (...parts) => {\n    let url = '';\n    parts.forEach(part =>"
  },
  {
    "path": "src/js/app/utils/convertTo.js",
    "chars": 1296,
    "preview": "import { toArray } from '../../utils/toArray';\nimport { toBoolean } from '../../utils/toBoolean';\nimport { toInt } from "
  },
  {
    "path": "src/js/app/utils/createDragHelper.js",
    "chars": 309,
    "preview": "export const createDragHelper = (items) => {\n    const itemIds = items.map(item => item.id);\n    let prevIndex = undefin"
  },
  {
    "path": "src/js/app/utils/createFetchFunction.js",
    "chars": 2598,
    "preview": "import { sendRequest } from '../../utils/sendRequest';\nimport { createResponse } from '../../utils/createResponse';\nimpo"
  },
  {
    "path": "src/js/app/utils/createFileLoader.js",
    "chars": 4533,
    "preview": "import { isBase64DataURI } from '../../utils/isBase64DataURI';\nimport { getFilenameFromURL } from '../../utils/getFilena"
  },
  {
    "path": "src/js/app/utils/createFileProcessor.js",
    "chars": 6509,
    "preview": "import { createPerceivedPerformanceUpdater } from './createPerceivedPerformanceUpdater';\nimport { getRandomNumber } from"
  },
  {
    "path": "src/js/app/utils/createFileProcessorFunction.js",
    "chars": 2929,
    "preview": "import { sendRequest } from '../../utils/sendRequest';\nimport { createResponse } from '../../utils/createResponse';\nimpo"
  },
  {
    "path": "src/js/app/utils/createFileStub.js",
    "chars": 1067,
    "preview": "import { getMimeTypeFromBase64DataURI } from '../../utils/getMimeTypeFromBase64DataURI';\nimport { getFilenameFromURL } f"
  },
  {
    "path": "src/js/app/utils/createHopper.js",
    "chars": 1844,
    "preview": "import { createDragNDropClient } from '../utils/dnd';\n\nexport const createHopper = (scope, validateItems, options) => {\n"
  },
  {
    "path": "src/js/app/utils/createInitialState.js",
    "chars": 414,
    "preview": "import { createOptions } from './createOptions';\n\nexport const createInitialState = options => ({\n\n    // model\n    item"
  },
  {
    "path": "src/js/app/utils/createItem.js",
    "chars": 13634,
    "preview": "import { getUniqueId } from '../../utils/getUniqueId';\nimport { getFilenameWithoutExtension } from '../../utils/getFilen"
  },
  {
    "path": "src/js/app/utils/createItemAPI.js",
    "chars": 462,
    "preview": "import { copyObjectPropertiesToObject } from '../../utils/copyObjectPropertiesToObject';\n\nconst PRIVATE = [\n    'fire',\n"
  },
  {
    "path": "src/js/app/utils/createOption.js",
    "chars": 345,
    "preview": "import { getValueByType } from './getValueByType';\n\nexport const createOption = (defaultValue, valueType) => {\n    let c"
  },
  {
    "path": "src/js/app/utils/createOptionAPI.js",
    "chars": 477,
    "preview": "import { fromCamels } from '../../utils/fromCamels';\nimport { forin } from '../../utils/forin';\n\nexport const createOpti"
  },
  {
    "path": "src/js/app/utils/createOptionActions.js",
    "chars": 640,
    "preview": "import { fromCamels } from '../../utils/fromCamels';\nimport { forin } from '../../utils/forin';\n\nexport const createOpti"
  },
  {
    "path": "src/js/app/utils/createOptionQueries.js",
    "chars": 330,
    "preview": "import { fromCamels } from '../../utils/fromCamels';\nimport { forin } from '../../utils/forin';\n\nexport const createOpti"
  },
  {
    "path": "src/js/app/utils/createOptions.js",
    "chars": 438,
    "preview": "import { createObject } from '../../utils/createObject';\nimport { createOption } from './createOption';\nimport { forin }"
  },
  {
    "path": "src/js/app/utils/createPaster.js",
    "chars": 2082,
    "preview": "import { arrayRemove } from '../../utils/arrayRemove';\nimport { requestDataTransferItems } from './requestDataTransferIt"
  },
  {
    "path": "src/js/app/utils/createPerceivedPerformanceUpdater.js",
    "chars": 820,
    "preview": "import { getRandomNumber } from '../../utils/getRandomNumber';\n\nexport const createPerceivedPerformanceUpdater = (\n    c"
  },
  {
    "path": "src/js/app/utils/createProcessorFunction.js",
    "chars": 568,
    "preview": "import { isString } from '../../utils/isString';\nimport { createFileProcessorFunction } from './createFileProcessorFunct"
  },
  {
    "path": "src/js/app/utils/createRevertFunction.js",
    "chars": 1738,
    "preview": "import { sendRequest } from '../../utils/sendRequest';\nimport { createResponse } from '../../utils/createResponse';\nimpo"
  },
  {
    "path": "src/js/app/utils/createServerAPI.js",
    "chars": 2084,
    "preview": "import { isString } from '../../utils/isString';\nimport { toBoolean } from '../../utils/toBoolean';\nimport { forin } fro"
  },
  {
    "path": "src/js/app/utils/dnd.js",
    "chars": 6859,
    "preview": "import { forin } from '../../utils/forin';\nimport { getRootNode } from '../../utils/getRootNode';\nimport { requestDataTr"
  },
  {
    "path": "src/js/app/utils/dropAreaDimensions.js",
    "chars": 552,
    "preview": "export const dropAreaDimensions = {\n    height: 0,\n    width: 0,\n    get getHeight() {\n        return this.height;\n    }"
  },
  {
    "path": "src/js/app/utils/dynamicLabel.js",
    "chars": 153,
    "preview": "import { isFunction } from '../../utils/isFunction';\n\nexport const dynamicLabel = (label) => (...params) => isFunction(l"
  },
  {
    "path": "src/js/app/utils/fetchBlob.js",
    "chars": 1684,
    "preview": "import { sendRequest } from '../../utils/sendRequest';\nimport { createResponse } from '../../utils/createResponse';\nimpo"
  },
  {
    "path": "src/js/app/utils/getActiveItems.js",
    "chars": 78,
    "preview": "export const getActiveItems = (items) => items.filter(item => !item.archived);"
  },
  {
    "path": "src/js/app/utils/getItemById.js",
    "chars": 243,
    "preview": "import { getItemIndexByQuery } from './getItemIndexByQuery';\n\nexport const getItemById = (items, itemId) => {\n    const "
  },
  {
    "path": "src/js/app/utils/getItemByQuery.js",
    "chars": 549,
    "preview": "import { isEmpty } from '../../utils/isEmpty';\nimport { isInt } from '../../utils/isInt';\n\nexport const getItemByQuery ="
  },
  {
    "path": "src/js/app/utils/getItemIndexByPosition.js",
    "chars": 2059,
    "preview": "import getItemsPerRow from './getItemsPerRow';\n\nexport const getItemIndexByPosition = (view, children, positionInView) ="
  },
  {
    "path": "src/js/app/utils/getItemIndexByQuery.js",
    "chars": 415,
    "preview": "import { isEmpty } from '../../utils/isEmpty';\nimport { isString } from '../../utils/isString';\n\nexport const getItemInd"
  },
  {
    "path": "src/js/app/utils/getItemsPerRow.js",
    "chars": 223,
    "preview": "export default (horizontalSpace, itemWidth) => {\n    // add one pixel leeway, when using percentages for item width tota"
  },
  {
    "path": "src/js/app/utils/getType.js",
    "chars": 535,
    "preview": "import { isArray } from '../../utils/isArray';\nimport { isInt } from '../../utils/isInt';\nimport { isNull } from '../../"
  },
  {
    "path": "src/js/app/utils/getValueByType.js",
    "chars": 902,
    "preview": "import { convertTo } from './convertTo';\nimport { getType } from './getType';\n\nexport const getValueByType = (newValue, "
  },
  {
    "path": "src/js/app/utils/hasRoomForItem.js",
    "chars": 750,
    "preview": "import { getActiveItems } from './getActiveItems';\nexport const hasRoomForItem = state => {\n    const count = getActiveI"
  },
  {
    "path": "src/js/app/utils/insertItem.js",
    "chars": 577,
    "preview": "import { isEmpty } from '../../utils/isEmpty';\nimport { limit } from '../../utils/limit';\nimport { arrayInsert } from '."
  },
  {
    "path": "src/js/app/utils/isAPI.js",
    "chars": 346,
    "preview": "import { isObject } from '../../utils/isObject';\nimport { isString } from '../../utils/isString';\n\nexport const isAPI = "
  },
  {
    "path": "src/js/app/utils/mergeOptionObject.js",
    "chars": 249,
    "preview": "import { forin } from '../../utils/forin';\nexport const mergeOptionObject = (originalObject, newObject) => {\n    const o"
  },
  {
    "path": "src/js/app/utils/on.js",
    "chars": 1269,
    "preview": "import { arrayRemove } from '../../utils/arrayRemove';\n\nconst run = (cb, sync) => {\n    if (sync) {\n        cb();\n    }\n"
  },
  {
    "path": "src/js/app/utils/processFileChunked.js",
    "chars": 9671,
    "preview": "import { sendRequest } from '../../utils/sendRequest';\nimport { createResponse } from '../../utils/createResponse';\nimpo"
  },
  {
    "path": "src/js/app/utils/removeReleasedItems.js",
    "chars": 229,
    "preview": "import { arrayRemove } from '../../utils/arrayRemove';\n\nexport const removeReleasedItems = (items) => {\n    items.forEac"
  },
  {
    "path": "src/js/app/utils/requestDataTransferItems.js",
    "chars": 6424,
    "preview": "import { guesstimateMimeType } from '../../utils/guesstimateMimeType';\nimport { getExtensionFromFilename } from '../../u"
  },
  {
    "path": "src/js/app/utils/toServerAPI.js",
    "chars": 113,
    "preview": "import { createServerAPI } from './createServerAPI';\nexport const toServerAPI = value => createServerAPI(value);\n"
  },
  {
    "path": "src/js/app/view/assistant.js",
    "chars": 3516,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { attr } from '../../utils/attr';\n\n/**\n * Creates the f"
  },
  {
    "path": "src/js/app/view/blob.js",
    "chars": 439,
    "preview": "import { createView } from '../frame/index';\n\nexport const blob = createView({\n    name: 'drip-blob',\n    ignoreRect: tr"
  },
  {
    "path": "src/js/app/view/browser.js",
    "chars": 5755,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { attrToggle } from '../../utils/attrToggle';\nimport { "
  },
  {
    "path": "src/js/app/view/data.js",
    "chars": 3252,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { createElement } from '../../utils/createElement';\nimp"
  },
  {
    "path": "src/js/app/view/drip.js",
    "chars": 1572,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { blob } from './blob';\n\nconst addBlob = ({ root }) => "
  },
  {
    "path": "src/js/app/view/dropLabel.js",
    "chars": 2361,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { attr } from '../../utils/attr';\nimport { createElemen"
  },
  {
    "path": "src/js/app/view/file.js",
    "chars": 15713,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { progressIndicator } from './progressIndicator';\nimpor"
  },
  {
    "path": "src/js/app/view/fileActionButton.js",
    "chars": 1243,
    "preview": "import { createView } from '../frame/index';\nimport { attr } from '../../utils/attr';\n\nconst create = ({ root, props }) "
  },
  {
    "path": "src/js/app/view/fileInfo.js",
    "chars": 2489,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { toNaturalFileSize } from '../../utils/toNaturalFileSi"
  },
  {
    "path": "src/js/app/view/fileStatus.js",
    "chars": 3348,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { toPercentage } from '../../utils/toPercentage';\nimpor"
  },
  {
    "path": "src/js/app/view/fileWrapper.js",
    "chars": 1214,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { formatFilename } from '../../utils/formatFilename';\ni"
  },
  {
    "path": "src/js/app/view/item.js",
    "chars": 7197,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { fileWrapper } from './fileWrapper';\nimport { panel } "
  },
  {
    "path": "src/js/app/view/list.js",
    "chars": 12407,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { InteractionMethod } from '../enum/InteractionMethod';"
  },
  {
    "path": "src/js/app/view/listScroller.js",
    "chars": 1888,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { list } from './list';\n\nconst create = ({ root, props "
  },
  {
    "path": "src/js/app/view/panel.js",
    "chars": 2750,
    "preview": "import { createView } from '../frame/index';\nimport { isBoolean } from '../../utils/isBoolean';\n\nconst PANEL_SPRING_PROP"
  },
  {
    "path": "src/js/app/view/progressIndicator.js",
    "chars": 1999,
    "preview": "import { createView } from '../frame/index';\nimport { createElement } from '../frame/utils/createElement';\nimport { attr"
  },
  {
    "path": "src/js/app/view/root.js",
    "chars": 22761,
    "preview": "import { createView, createRoute } from '../frame/index';\nimport { applyFilterChain, applyFilters } from '../../filter';"
  },
  {
    "path": "src/js/createApp.js",
    "chars": 372,
    "preview": "import { isNode } from './utils/isNode';\nimport { createAppObject } from './createAppObject';\nimport { createAppAtElemen"
  },
  {
    "path": "src/js/createAppAPI.js",
    "chars": 272,
    "preview": "import { copyObjectPropertiesToObject } from './utils/copyObjectPropertiesToObject';\nconst PRIVATE_METHODS = ['fire', '_"
  },
  {
    "path": "src/js/createAppAtElement.js",
    "chars": 2916,
    "preview": "import { getAttributesAsObject } from './utils/getAttributesAsObject';\nimport { createAppObject } from './createAppObjec"
  }
]

// ... and 98 more files (download for full content)

About this extraction

This page contains the full source code of the pqina/filepond GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 298 files (1.3 MB), approximately 297.5k tokens, and a symbol index with 148 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!