diff --git a/.gitattributes b/.gitattributes
index bb6265e..70dce41 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,11 +1,23 @@
# Path-based git attributes
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
-# Ignore all test and documentation with "export-ignore".
+# Keep development files out of the Composer package. docs, llms.txt, dist and
+# resources/boost ship with it, for apps without Vite, AI assistants and Laravel Boost.
/.gitattributes export-ignore
/.gitignore export-ignore
-/.travis.yml export-ignore
+/.github export-ignore
+/.editorconfig export-ignore
+/.prettierrc.json export-ignore
+/.prettierignore export-ignore
+/eslint.config.js export-ignore
/phpunit.xml.dist export-ignore
-/.scrutinizer.yml export-ignore
+/pint.json export-ignore
/tests export-ignore
-/.editorconfig export-ignore
+/AGENTS.md export-ignore
+/CLAUDE.md export-ignore
+/node_modules export-ignore
+/package.json export-ignore
+/pnpm-lock.yaml export-ignore
+/tsconfig.json export-ignore
+/vite.config.ts export-ignore
+/vitest.config.ts export-ignore
diff --git a/.github/coverage.svg b/.github/coverage.svg
deleted file mode 100644
index de5e7b8..0000000
--- a/.github/coverage.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
\ No newline at end of file
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
deleted file mode 100644
index d415a0e..0000000
--- a/.github/workflows/run-tests.yml
+++ /dev/null
@@ -1,73 +0,0 @@
-name: tests
-
-on:
- pull_request:
- branches:
- - main
- push:
- branches:
- - main
- - dev
-
-jobs:
- tests:
- runs-on: ${{ matrix.os }}
-
- strategy:
- fail-fast: true
- matrix:
- os: [ ubuntu-latest ]
- php: [ 8.4, 8.3, 8.2 ]
- laravel: [ 13.*, 12.*, 11.*, ^10.2 ]
- stability: [ prefer-lowest, prefer-stable ]
- include:
- - laravel: 13.*
- testbench: 11.*
- carbon: ^3.8.4
- - laravel: 12.*
- testbench: 10.*
- carbon: ^3.8.4
- - laravel: 11.*
- testbench: ^9.16
- carbon: ^2.63
- - laravel: ^10.2
- testbench: 8.*
- carbon: ^2.63
- exclude:
- - laravel: 13.*
- php: 8.2
-
- name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Install SQLite 3
- run: |
- sudo apt-get update
- sudo apt-get install sqlite3 -y
-
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: ${{ matrix.php }}
- extensions: curl, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, iconv
- coverage: ${{ startsWith(github.event.head_commit.message, 'coverage') && matrix.php == '8.4' && matrix.laravel == '13.*' && matrix.stability == 'prefer-stable' && 'xdebug' || 'none' }}
-
- - name: Install dependencies
- run: |
- composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.carbon }}" --no-interaction --no-update
- composer update --${{ matrix.stability }} --prefer-dist --no-interaction
-
- - name: Execute tests
- run: |
- vendor/bin/phpunit ${{ startsWith(github.event.head_commit.message, 'coverage') && matrix.php == '8.4' && matrix.laravel == '13.*' && matrix.stability == 'prefer-stable' && '--coverage-clover=clover.xml' || '' }}
-
- - name: Make code coverage badge
- if: startsWith(github.event.head_commit.message, 'coverage') && matrix.php == '8.4' && matrix.laravel == '13.*' && matrix.stability == 'prefer-stable'
- uses: timkrase/phpunit-coverage-badge@v1.2.1
- with:
- coverage_badge_path: .github/coverage.svg
- push_badge: true
- repo_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..e6ed400
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,233 @@
+name: tests
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ - next-gen
+ workflow_dispatch:
+
+# The unreleased javaabu/* dependencies come from VCS repositories; the job's own
+# token keeps Composer within GitHub's API limits
+env:
+ COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ github.token }}"}}'
+
+jobs:
+ pint:
+ runs-on: ubuntu-latest
+
+ name: Pint and dependencies
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.4
+ coverage: none
+
+ - name: Get Composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
+
+ - name: Cache Composer downloads
+ uses: actions/cache@v6
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-${{ hashFiles('composer.json', 'composer.lock') }}
+ restore-keys: |
+ composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-
+ composer-${{ runner.os }}-${{ github.job }}-
+
+ - name: Install dependencies
+ run: composer update --prefer-dist --no-interaction --no-progress
+
+ - name: Check code style
+ run: vendor/bin/pint --test
+
+ # javaabu/helpers pins javaabu/settings ^1, which breaks installs next to settings v2
+ - name: No javaabu/helpers or javaabu/settings ^1 in production dependencies
+ run: |
+ tree="$(composer show --tree --no-dev)"
+ if echo "$tree" | grep -E 'javaabu/helpers'; then
+ echo "javaabu/mediapicker must not depend on javaabu/helpers"
+ exit 1
+ fi
+ if echo "$tree" | grep -E 'javaabu/settings (\^|~)?1'; then
+ echo "javaabu/mediapicker must not depend on javaabu/settings ^1"
+ exit 1
+ fi
+
+ js:
+ runs-on: ubuntu-latest
+
+ name: TypeScript (lint, typecheck, tests, dist)
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ # @javaabu/block-editor and @javaabu/stencil come from their Composer packages in vendor/
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.4
+ coverage: none
+
+ - name: Get Composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
+
+ - name: Cache Composer downloads
+ uses: actions/cache@v6
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-${{ hashFiles('composer.json', 'composer.lock') }}
+ restore-keys: |
+ composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-
+ composer-${{ runner.os }}-${{ github.job }}-
+
+ - name: Install Composer dependencies
+ run: composer update --prefer-dist --no-interaction --no-progress
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+
+ # Keep the store out of node_modules, where @javaabu/forms' prepare build can't
+ # emit portable types (Javaabu/react-forms#20)
+ - name: Use a pnpm store outside node_modules
+ run: pnpm config set store-dir ~/.pnpm-store
+
+ - name: Setup Node
+ uses: actions/setup-node@v5
+ with:
+ node-version: 22
+ cache: pnpm
+
+ - name: Install npm dependencies
+ run: pnpm install --frozen-lockfile
+
+ - run: pnpm lint
+ - run: pnpm format:check
+ - run: pnpm typecheck
+
+ - name: Tests with coverage
+ run: pnpm test:coverage
+
+ - name: Upload coverage
+ uses: codecov/codecov-action@v5
+ with:
+ files: coverage/lcov.info
+ flags: js
+ token: ${{ secrets.CODECOV_TOKEN }}
+ fail_ci_if_error: false
+
+ - name: Check that the committed dist is up to date
+ run: |
+ pnpm build
+ git diff --exit-code -- dist || (echo "dist is out of date: run pnpm build and commit it." && exit 1)
+
+ without-translatable:
+ runs-on: ubuntu-latest
+
+ name: Tests without javaabu/translatable
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.4
+ extensions: curl, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, iconv, gd, exif, fileinfo
+ coverage: none
+
+ - name: Get Composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
+
+ - name: Cache Composer downloads
+ uses: actions/cache@v6
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-${{ hashFiles('composer.json', 'composer.lock') }}
+ restore-keys: |
+ composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-
+ composer-${{ runner.os }}-${{ github.job }}-
+
+ - name: Install dependencies without javaabu/translatable
+ run: |
+ composer remove --dev javaabu/translatable --no-update
+ composer update --prefer-dist --no-interaction --no-progress
+
+ - name: Execute tests
+ run: vendor/bin/phpunit --testsuite "Test Suite"
+
+ tests:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php: [8.4, 8.5]
+ laravel: [12.*, 13.*]
+ stability: [prefer-lowest, prefer-stable]
+ include:
+ - laravel: 12.*
+ testbench: 10.*
+ - laravel: 13.*
+ testbench: 11.*
+
+ name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }}
+
+ env:
+ COVERAGE: ${{ matrix.php == '8.4' && matrix.laravel == '13.*' && matrix.stability == 'prefer-stable' }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: curl, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, iconv, gd, exif, fileinfo
+ coverage: ${{ env.COVERAGE == 'true' && 'pcov' || 'none' }}
+
+ - name: Get Composer cache directory
+ id: composer-cache
+ run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
+
+ - name: Cache Composer downloads
+ uses: actions/cache@v6
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-${{ hashFiles('composer.json', 'composer.lock') }}
+ restore-keys: |
+ composer-${{ runner.os }}-${{ github.job }}-${{ strategy.job-index }}-
+ composer-${{ runner.os }}-${{ github.job }}-
+
+ - name: Install dependencies
+ run: |
+ composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
+ composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress
+
+ - name: Execute tests
+ if: env.COVERAGE != 'true'
+ run: vendor/bin/phpunit
+
+ - name: Execute tests with coverage
+ if: env.COVERAGE == 'true'
+ run: vendor/bin/phpunit --coverage-clover build/coverage.xml
+
+ - name: Upload coverage to Codecov
+ if: env.COVERAGE == 'true'
+ uses: codecov/codecov-action@v5
+ with:
+ files: build/coverage.xml
+ fail_ci_if_error: false
+ token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 81d795b..8903f58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,8 +3,10 @@ composer.lock
vendor
coverage
.phpunit.result.cache
+.phpunit.cache
.php-cs-fixer.cache
.idea
+node_modules
clover.xml
tests/TestSupport/temp
tests/temp
diff --git a/.php_cs.dist.php b/.php_cs.dist.php
deleted file mode 100644
index 8648fdc..0000000
--- a/.php_cs.dist.php
+++ /dev/null
@@ -1,35 +0,0 @@
-in([
- __DIR__ . '/src',
- __DIR__ . '/tests',
- ])
- ->name('*.php')
- ->notName('*.blade.php')
- ->ignoreDotFiles(true)
- ->ignoreVCS(true);
-
-return (new PhpCsFixer\Config())
- ->setRules([
- '@PSR12' => true,
- 'array_syntax' => ['syntax' => 'short'],
- 'ordered_imports' => ['sort_algorithm' => 'alpha'],
- 'no_unused_imports' => true,
- 'not_operator_with_successor_space' => true,
- 'trailing_comma_in_multiline' => true,
- 'phpdoc_scalar' => true,
- 'unary_operator_spaces' => true,
- 'binary_operator_spaces' => true,
- 'blank_line_before_statement' => [
- 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
- ],
- 'phpdoc_single_line_var_spacing' => true,
- 'phpdoc_var_without_name' => true,
- 'method_argument_space' => [
- 'on_multiline' => 'ensure_fully_multiline',
- 'keep_multiple_spaces_after_comma' => true,
- ],
- 'single_trait_insert_per_statement' => true,
- ])
- ->setFinder($finder);
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..130c840
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,12 @@
+dist
+coverage
+node_modules
+vendor
+build
+pnpm-lock.yaml
+*.php
+*.md
+composer.json
+composer.lock
+docs/**/*.json
+lang/*.json
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..6c1772b
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,5 @@
+{
+ "semi": false,
+ "printWidth": 120,
+ "tabWidth": 2
+}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..2d8a4a0
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,48 @@
+# mediapicker: notes for AI coding agents
+
+`javaabu/mediapicker` is a Laravel package: a media library on spatie/laravel-medialibrary v11, attachments of library media to other models with their own (fingerprinted) conversions, a non-destructive cropper, library pages and picker fields in Blade (javaabu/forms v2) and Inertia React (@javaabu/forms, Onu), a stencil field, javaabu/block-editor blocks, and permissions through javaabu/permissions. It's a Composer package that also ships TypeScript (imported by apps as `@javaabu/mediapicker` through javaabu/manifest) and a prebuilt `dist/` for the Blade scripts.
+
+## Layout
+
+| Path | What |
+| --- | --- |
+| `src/Mediapicker.php` | Models, URLs, `pickerConfig()`, `image()` |
+| `src/Routing/MediapickerRoutes.php` | `Route::mediapicker()` |
+| `src/Http/` | `MediaController`, the requests, `MediaResource` |
+| `src/Policies/MediaPolicy.php` | Abilities over javaabu/permissions' `ResourcePolicy` |
+| `src/Models/` | `Media`, `Translatable\Media`, `Attachment`, observers |
+| `src/Concerns/`, `src/Contracts/` | `OwnsMedia`/`MediaOwner`, `InteractsWithAttachments`/`HasAttachments` |
+| `src/AttachmentCollections/` | `MediaAdder`, the repository, exceptions |
+| `src/Conversions/` | `AttachmentConversion` (fingerprints), the manipulator, actions, metadata, stale-file cleaning |
+| `src/Cropping/`, `src/Events/`, `src/Listeners/` | Crops and their regeneration |
+| `src/Views/` | `ViewRenderer` (Blade or Inertia), the `picker` and `image` components |
+| `src/Stencil/` | `MediaPickerField`, `MediaDetailsForm` |
+| `src/Blocks/` | The block-editor blocks |
+| `src/Support/` | `FileTypes`, `CropPresets`, `PickerConfig`, `ImageData`, `Strings`, `Translations`, `Icons` |
+| `resources/views/` | Blade pages, components, stencil and block views |
+| `resources/js/` | `Pages/Media/*`, `react/`, `picker/` (framework-free dialog and Blade field), `cropper/`, `blocks/`, `blade/` (entries), `api.ts` |
+| `resources/css/mediapicker.css` | Every style, logical properties only |
+| `dist/` | The Blade entries, built and **committed**. CI fails if it's stale |
+| `lang/` | `en.json`, `dv.json` |
+| `tests/` | PHPUnit (`Unit`, `Feature`, `Http`, `Translatable`) and Vitest (`tests/js`) |
+| `docs`, `llms.txt`, `resources/boost` | Docs, the index for assistants, the Boost guideline and skill |
+
+## Rules
+
+- Follow the Javaabu PHP guidelines (`vendor/javaabu/boost-javaabu-guidelines`): snake_case variables and properties, camelCase methods, `#[Test]` methods named `it_…`, early returns, no `else`.
+- Never depend on javaabu/helpers or javaabu/settings v1 (CI checks it). javaabu/translatable is optional: reference its classes only behind `Support\Translations`; `tests/Translatable` needs it, and CI runs the rest without it.
+- Every user-facing string goes through `__()` with an entry in `lang/en.json` and `lang/dv.json` (a test checks). UI strings for JS are in `Support\Strings::STRINGS`; after changing them, regenerate `resources/js/strings.en.json` (a test compares them).
+- Logical CSS and Tailwind only (`ms-`, `pe-`, `inset-inline-*`); tests use Thaana for user-facing behaviour.
+- Attachment conversion files must stay under `conversions/attachments` and keep their fingerprint in the name.
+- After changing `resources/js/blade`, `resources/js/picker`, `resources/js/cropper`, `resources/js/blocks` or `resources/css`, run `pnpm build` and commit `dist/`. After changing a block's PHP attributes, run `MEDIAPICKER_UPDATE_FIXTURES=1 vendor/bin/phpunit --filter JsFixtureTest`.
+- Every change needs tests, docs (linked from `llms.txt`), the Boost skill if it changes usage, and a line in `CHANGELOG.md`.
+
+## Commands
+
+```bash
+composer install && pnpm install # pnpm 10.28 (packageManager)
+composer lint && composer test # Pint and PHPUnit (both suites)
+vendor/bin/phpunit --testsuite "Test Suite" # without the translatable tests
+pnpm lint && pnpm format:check && pnpm typecheck && pnpm test
+pnpm build # dist/
+```
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8869e77..1ea4679 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,20 @@
# Changelog
-All notable changes will be documented in this file
+All notable changes to `javaabu/mediapicker` are documented in this file.
-## 1.0.0 - 202X-XX-XX
+## 1.0.0 - unreleased
-- initial release
+The first release. Compared with the unreleased version on `main` (see `docs/upgrading.md`):
+
+- PHP 8.4 and Laravel 12/13; no javaabu/helpers, javaabu/activitylog 1 or javaabu/forms 1
+- `Route::mediapicker()` with a guard, prefix, name, middleware and controller; overridable controller; every action answers JSON
+- Permissions and an overridable `MediaPolicy` through javaabu/permissions; a library per guard
+- Library pages in Blade (javaabu/forms v2) and Inertia React (@javaabu/forms and Onu, through javaabu/manifest)
+- npm dependencies split by scope: Blade apps are asked for `cropperjs` and `@tiptap/core` only; the React and Inertia packages are declared `for: 'pages'` (javaabu/manifest#17)
+- ``, React `MediaPicker`/`MediaPickerField`/`MediaLibraryDialog`, and the stencil `MediaPickerField`; the `LibraryMedia` rule and `syncAttachmentsFromRequest()`
+- Fingerprinted attachment conversions: shared between models, never made twice, extra formats (WebP, AVIF), responsive widths; `` and React ``
+- Non-destructive cropping to aspect-ratio presets, on upload and later, driving conversions
+- Image, gallery, file and video blocks for javaabu/block-editor
+- Optional javaabu/translatable support for names, descriptions, alt text and captions
+- Dhivehi translations and RTL layouts
+- Docs, `llms.txt`, and a Laravel Boost guideline (in a verbatim block, tested with Boost's own renderer) and skill
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..43c994c
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+@AGENTS.md
diff --git a/README.md b/README.md
index 340028b..d5e962a 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,40 @@
# Mediapicker
-[](https://packagist.org/packages/javaabu/mediapicker)
-[](../../actions/workflows/run-tests.yml)
-
-[](https://packagist.org/packages/javaabu/mediapicker)
-
-
+[](https://github.com/Javaabu/mediapicker/actions/workflows/tests.yml?query=branch%3Amain)
+[](https://codecov.io/gh/Javaabu/mediapicker/tree/main)
+[](https://packagist.org/packages/javaabu/mediapicker)
+[](LICENSE.md)
+
+A media library for Laravel on [spatie/laravel-medialibrary](https://github.com/spatie/laravel-medialibrary): people upload once and pick files wherever a model needs an image, a gallery or a document.
+
+- Library pages in Blade ([javaabu/forms](https://github.com/Javaabu/forms) v2) or Inertia React, with uploads, search, filters, details and bulk delete, and a JSON API
+- Picker fields: ``, React `MediaPickerField` and a [javaabu/stencil](https://github.com/Javaabu/stencil) field
+- Attachments with their own conversions: queued, only for the collections that need them, shared and never made twice, WebP/AVIF and responsive widths, rendered as ``
+- Non-destructive cropping to aspect-ratio presets
+- Image, gallery, file and video blocks for [javaabu/block-editor](https://github.com/Javaabu/block-editor)
+- A library per guard, with permissions and an overridable policy through [javaabu/permissions](https://github.com/Javaabu/permissions)
+- Dhivehi and RTL, and optional translated details with [javaabu/translatable](https://github.com/Javaabu/translatable)
+
+```php
+Route::mediapicker(['guard' => 'web_admin', 'prefix' => 'admin/media', 'name' => 'admin.media']);
+```
-## Introduction
-Allow for file uploads from a persistent storage
+```blade
+
+
+```
## Documentation
-You'll find the documentation on [https://docs.javaabu.com/mediapicker](https://docs.javaabu.com/mediapicker).
+The docs are in [`docs/`](docs/intro.md) and on [docs.javaabu.com/mediapicker](https://docs.javaabu.com/mediapicker). AI assistants: start with [`llms.txt`](llms.txt); Laravel Boost picks up the package's guideline and skill.
Find yourself stuck using the package? Found a bug? Do you have general questions or suggestions for improving this package? Feel free to create an [issue](../../issues) on GitHub, we'll try to address it as soon as possible.
-If you've found a bug regarding security please mail [info@javaabu.com](mailto:info@javaabu.com) instead of using the issue tracker.
-
## Testing
-You can run the tests with
-
-``` bash
-./vendor/bin/phpunit
+```bash
+composer test
+pnpm install && pnpm test
```
## Changelog
diff --git a/composer.json b/composer.json
index 77664b5..4d54145 100644
--- a/composer.json
+++ b/composer.json
@@ -1,12 +1,15 @@
{
"name": "javaabu/mediapicker",
- "description": "Allow for file uploads from a persistent storage",
+ "description": "A media library for Laravel on spatie/laravel-medialibrary: Blade and Inertia React library pages, a picker field, attachments with their own conversions, an image cropper and javaabu/block-editor blocks",
"homepage": "https://github.com/Javaabu/mediapicker",
"license": "MIT",
"keywords": [
"javaabu",
"laravel",
- "mediapicker"
+ "mediapicker",
+ "media-library",
+ "cropper",
+ "block-editor"
],
"authors": [
{
@@ -20,18 +23,64 @@
"role": "Developer"
}
],
+ "repositories": [
+ {
+ "type": "vcs",
+ "url": "https://github.com/Javaabu/block-editor",
+ "no-api": true
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/Javaabu/forms",
+ "no-api": true
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/Javaabu/manifest",
+ "no-api": true
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/Javaabu/permissions",
+ "no-api": true
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/Javaabu/stencil",
+ "no-api": true
+ },
+ {
+ "type": "vcs",
+ "url": "https://github.com/n4ff4h/translatable",
+ "no-api": true
+ }
+ ],
"require": {
- "php": "^8.2",
- "illuminate/support": "^10.2 || ^11.0 || ^12.0 || ^13.0",
- "spatie/laravel-medialibrary": "^11.12",
- "javaabu/helpers": "^1.70",
- "javaabu/activitylog": "^1.5",
- "javaabu/forms": "^1.67"
+ "php": "^8.4",
+ "illuminate/database": "^12.0 || ^13.0",
+ "illuminate/http": "^12.0 || ^13.0",
+ "illuminate/routing": "^12.0 || ^13.0",
+ "illuminate/support": "^12.0 || ^13.0",
+ "illuminate/view": "^12.0 || ^13.0",
+ "javaabu/block-editor": "^1.0@dev",
+ "javaabu/forms": "^2.0@dev",
+ "javaabu/manifest": "^1.0@dev",
+ "javaabu/permissions": "^3.0@dev",
+ "javaabu/stencil": "^1.0@dev",
+ "spatie/laravel-medialibrary": "^11.17"
},
"require-dev": {
- "laravel/pint": "^1.14",
- "orchestra/testbench": "^8.0 || ^9.16 || ^10.0 || ^11.0",
- "phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.8 || ^13.0.3"
+ "inertiajs/inertia-laravel": "^2.0",
+ "javaabu/boost-javaabu-guidelines": "^1.0",
+ "javaabu/translatable": "^3.0@dev",
+ "laravel/boost": "^2.10",
+ "laravel/pint": "^1.24",
+ "orchestra/testbench": "^10.0 || ^11.0",
+ "phpunit/phpunit": "^11.5.3 || ^12.5.8"
+ },
+ "suggest": {
+ "inertiajs/inertia-laravel": "Renders the media library pages with Inertia React when mediapicker.views is inertia (^2.0)",
+ "javaabu/translatable": "Translates media names, descriptions, alt text and captions (^3.0)"
},
"autoload": {
"psr-4": {
@@ -43,9 +92,15 @@
"Javaabu\\Mediapicker\\Tests\\": "tests/"
}
},
+ "scripts": {
+ "test": "vendor/bin/phpunit",
+ "test-coverage": "vendor/bin/phpunit --coverage-clover build/coverage.xml",
+ "lint": "vendor/bin/pint --test",
+ "format": "vendor/bin/pint"
+ },
"extra": {
"branch-alias": {
- "dev-main": "1.0-dev"
+ "dev-next-gen": "1.x-dev"
},
"laravel": {
"providers": [
@@ -53,6 +108,9 @@
]
}
},
+ "config": {
+ "sort-packages": true
+ },
"minimum-stability": "dev",
"prefer-stable": true
}
diff --git a/config/mediapicker.php b/config/mediapicker.php
index 25d94bf..ed5d2e3 100644
--- a/config/mediapicker.php
+++ b/config/mediapicker.php
@@ -1,119 +1,196 @@
\Javaabu\Mediapicker\Models\Media::class,
+ 'media_model' => null,
+
+ 'attachment_model' => Attachment::class,
/*
|--------------------------------------------------------------------------
- | Attachment Model
+ | Library collection
|--------------------------------------------------------------------------
|
- | Which model to use for Attachments.
- | It should implement the Javaabu\Mediapicker\Contracts\Attachment interface
- | and extend Illuminate\Database\Eloquent\Model.
+ | The media collection on the owner (the user model) that holds its
+ | library uploads.
|
*/
- 'attachment_model' => \Javaabu\Mediapicker\Models\Attachment::class,
-
- /*
- * Controller used to upload and manage media
- */
- 'media_controller' => \Javaabu\Mediapicker\Http\Controllers\MediaController::class,
-
- /*
- * Collection name to be used for media picker uploads
- */
'collection_name' => 'mediapicker',
/*
|--------------------------------------------------------------------------
- | Attachment Observer
+ | File types
|--------------------------------------------------------------------------
|
- | Class used to observe changes to attachments
+ | What the library accepts, by type. Pickers and uploads can be limited to
+ | some types (`type="image"`). Each type has its mime types, a Lucide icon
+ | and an optional max_size in KB. SVG isn't in the defaults: an uploaded
+ | SVG can run scripts when it's opened from your domain.
|
*/
- 'attachment_observer' => \Javaabu\Mediapicker\Models\Observers\AttachmentObserver::class,
+ 'file_types' => [
+ 'image' => [
+ 'mime_types' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif'],
+ 'icon' => 'image',
+ 'max_size' => null,
+ ],
+ 'video' => [
+ 'mime_types' => ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime'],
+ 'icon' => 'film',
+ 'max_size' => null,
+ ],
+ 'audio' => [
+ 'mime_types' => ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/x-wav', 'audio/ogg', 'audio/mp4', 'audio/aac', 'audio/webm'],
+ 'icon' => 'music',
+ 'max_size' => null,
+ ],
+ 'document' => [
+ 'mime_types' => [
+ 'application/pdf',
+ 'application/msword',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'application/vnd.ms-excel',
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'application/vnd.ms-powerpoint',
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'text/plain',
+ 'text/csv',
+ ],
+ 'icon' => 'file-text',
+ 'max_size' => null,
+ ],
+ 'archive' => [
+ 'mime_types' => ['application/zip', 'application/x-zip-compressed'],
+ 'icon' => 'file-archive',
+ 'max_size' => null,
+ ],
+ ],
/*
|--------------------------------------------------------------------------
- | Media Observer
+ | Upload size
|--------------------------------------------------------------------------
|
- | Class used to observe changes to medias
+ | The largest upload in KB, for types without their own max_size. Null
+ | uses spatie's media-library.max_file_size.
|
*/
- 'media_observer' => \Javaabu\Mediapicker\Models\Observers\MediaObserver::class,
+ 'max_upload_size' => null,
/*
- * Here you can override the class names of the jobs used by this package. Make sure
- * your custom jobs extend the ones provided by the package.
- */
- 'jobs' => [
- 'perform_conversions' => \Javaabu\Mediapicker\Conversions\Jobs\PerformAttachmentConversionsJob::class,
- ],
+ |--------------------------------------------------------------------------
+ | Crop presets
+ |--------------------------------------------------------------------------
+ |
+ | The aspect ratios images can be cropped to (width / height; null crops
+ | freely). Crops are saved per preset on the media and never change the
+ | original. Attachment conversions use one with ->usingCrop('wide').
+ |
+ */
- /*
- * The class that contains the strategy for determining how to remove files.
- */
- 'file_remover_class' => \Javaabu\Mediapicker\FileRemover\AttachmentFileRemover::class,
+ 'crop_presets' => [
+ 'square' => ['label' => 'Square', 'aspect' => 1],
+ 'wide' => ['label' => 'Wide (16:9)', 'aspect' => 16 / 9],
+ 'landscape' => ['label' => 'Landscape (4:3)', 'aspect' => 4 / 3],
+ 'portrait' => ['label' => 'Portrait (3:4)', 'aspect' => 3 / 4],
+ 'free' => ['label' => 'Free', 'aspect' => null],
+ ],
/*
|--------------------------------------------------------------------------
- | Default CSS Framework
+ | Block editor
|--------------------------------------------------------------------------
|
- | This option controls the default CSS framework that will be used by the
- | package when rendering views
- |
- | Supported: "material-admin-26"
+ | Register the image, gallery, file and mediaVideo blocks with
+ | javaabu/block-editor. The image block replaces the core URL image.
|
*/
- 'framework' => 'material-admin-26',
+ 'blocks' => true,
/*
|--------------------------------------------------------------------------
- | Default Layout
+ | Pages
|--------------------------------------------------------------------------
|
- | Default layout view for media library views
+ | views: `blade` (javaabu/forms) or `inertia` (React pages through
+ | javaabu/manifest). The Blade pages extend `layout` and fill
+ | `layout_section`. per_page is the library's page size.
|
*/
- 'default_layout' => 'layouts.admin',
+ 'views' => 'blade',
+
+ 'layout' => 'layouts.app',
+
+ 'layout_section' => 'content',
+
+ 'per_page' => 24,
/*
|--------------------------------------------------------------------------
- | Picker Layout
+ | Controllers
|--------------------------------------------------------------------------
|
- | Default layout view for picker modal views
+ | Extend the package controller to change how a page works. The
+ | `controller` option of Route::mediapicker() wins over this.
|
*/
- 'picker_layout' => 'layouts.admin-blank',
+ 'media_controller' => MediaController::class,
/*
|--------------------------------------------------------------------------
- | Scripts Stack
+ | Permissions and policy
|--------------------------------------------------------------------------
|
- | The name of the stack to push scripts
+ | The media permissions are registered with javaabu/permissions (run
+ | `php artisan permissions:sync`). The policy can be swapped here, in
+ | config('permissions.policies') or with Gate::policy().
|
*/
- 'scripts_stack' => 'scripts',
+ 'register_permissions' => true,
+
+ 'policy' => MediaPolicy::class,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Observers, jobs and file removal
+ |--------------------------------------------------------------------------
+ */
+
+ 'attachment_observer' => AttachmentObserver::class,
+
+ 'media_observer' => MediaObserver::class,
+
+ 'jobs' => [
+ 'perform_conversions' => PerformAttachmentConversionsJob::class,
+ ],
+
+ 'file_remover_class' => AttachmentFileRemover::class,
];
diff --git a/database/migrations/2025_03_20_142419_create_attachments_table.php b/database/migrations/2025_03_20_142419_create_attachments_table.php
index df897cd..ea32524 100644
--- a/database/migrations/2025_03_20_142419_create_attachments_table.php
+++ b/database/migrations/2025_03_20_142419_create_attachments_table.php
@@ -1,8 +1,8 @@
json('translations')->nullable();
+ }
+
+ if (! Schema::hasColumn('media', 'lang')) {
+ $table->string('lang')->nullable()->index();
+ }
+ });
+ }
+
+ public function down(): void
+ {
+ if (! Schema::hasTable('media') || ! Schema::hasColumn('media', 'translations')) {
+ return;
+ }
+
+ Schema::table('media', function (Blueprint $table) {
+ $table->dropIndex(['lang']);
+ $table->dropColumn(['translations', 'lang']);
+ });
+ }
+};
diff --git a/dist/.vite/manifest.json b/dist/.vite/manifest.json
new file mode 100644
index 0000000..4344ac4
--- /dev/null
+++ b/dist/.vite/manifest.json
@@ -0,0 +1,64 @@
+{
+ "_dialog-BNieuy7N.js": {
+ "file": "assets/dialog-BNieuy7N.js",
+ "name": "dialog",
+ "imports": [
+ "_strings-BSe7uzRa.js"
+ ]
+ },
+ "_mount-DD7-9_Qt.js": {
+ "file": "assets/mount-DD7-9_Qt.js",
+ "name": "mount"
+ },
+ "_rolldown-runtime-WkwZ5CU-.js": {
+ "file": "assets/rolldown-runtime-WkwZ5CU-.js",
+ "name": "rolldown-runtime"
+ },
+ "_strings-BGZ7j9fb.css": {
+ "file": "assets/strings-BGZ7j9fb.css",
+ "src": "_strings-BGZ7j9fb.css"
+ },
+ "_strings-BSe7uzRa.js": {
+ "file": "assets/strings-BSe7uzRa.js",
+ "name": "strings",
+ "css": [
+ "assets/strings-BGZ7j9fb.css"
+ ]
+ },
+ "resources/js/blade/cropper.ts": {
+ "file": "assets/cropper-B0MRTFK5.js",
+ "name": "cropper",
+ "src": "resources/js/blade/cropper.ts",
+ "isEntry": true,
+ "imports": [
+ "_rolldown-runtime-WkwZ5CU-.js",
+ "_strings-BSe7uzRa.js",
+ "_mount-DD7-9_Qt.js"
+ ],
+ "css": [
+ "assets/cropper-DMIal2X7.css"
+ ]
+ },
+ "resources/js/blade/picker.ts": {
+ "file": "assets/picker-DZpII1oN.js",
+ "name": "picker",
+ "src": "resources/js/blade/picker.ts",
+ "isEntry": true,
+ "imports": [
+ "_strings-BSe7uzRa.js",
+ "_dialog-BNieuy7N.js",
+ "_mount-DD7-9_Qt.js"
+ ]
+ },
+ "resources/js/blocks/index.ts": {
+ "file": "assets/index-CTa77IUl.js",
+ "name": "index",
+ "src": "resources/js/blocks/index.ts",
+ "isEntry": true,
+ "imports": [
+ "_rolldown-runtime-WkwZ5CU-.js",
+ "_strings-BSe7uzRa.js",
+ "_dialog-BNieuy7N.js"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/dist/assets/cropper-B0MRTFK5.js b/dist/assets/cropper-B0MRTFK5.js
new file mode 100644
index 0000000..17b4002
--- /dev/null
+++ b/dist/assets/cropper-B0MRTFK5.js
@@ -0,0 +1,3 @@
+import{n as e,t}from"./rolldown-runtime-WkwZ5CU-.js";import{c as n,i as r,l as i,t as a}from"./strings-BSe7uzRa.js";import{n as o,t as s}from"./mount-DD7-9_Qt.js";var c=e(t(((e,t,n)=>{(function(n,r){typeof e==`object`&&t!==void 0?t.exports=r():typeof define==`function`&&define.amd?define(r):(n=typeof globalThis<`u`?globalThis:n||self,n.Cropper=r())})(n,(function(){function e(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n
`,Ie=Number.isNaN||b.isNaN,Le=Number.isFinite||b.isFinite;function z(e){return typeof e==`number`&&!Ie(e)}var Re=function(e){return e>0&&e<1/0};function ze(e){return e===void 0}function B(e){return _(e)===`object`&&e!==null}var Be=Object.prototype.hasOwnProperty;function V(e){if(!B(e))return!1;try{var t=e.constructor,n=t.prototype;return t&&n&&Be.call(n,`isPrototypeOf`)}catch{return!1}}function H(e){return typeof e==`function`}var Ve=Array.prototype.slice;function He(e){return Array.from?Array.from(e):Ve.call(e)}function U(e,t){return e&&H(t)&&(Array.isArray(e)||z(e.length)?He(e).forEach(function(n,r){t.call(e,n,r,e)}):B(e)&&Object.keys(e).forEach(function(n){t.call(e,e[n],n,e)})),e}var W=Object.assign||function(e){var t=[...arguments].slice(1);return B(e)&&t.length>0&&t.forEach(function(t){B(t)&&Object.keys(t).forEach(function(n){e[n]=t[n]})}),e},Ue=/\.\d*(?:0|9){12}\d*$/;function G(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:1e11;return Math.abs(e)<1/t||Ue.test(e)?Math.round(e*t)/t:e}var We=/^width|height|left|top|marginLeft|marginTop$/;function K(e,t){var n=e.style;U(t,function(e,t){We.test(t)&&z(e)&&(e=`${e}px`),n[t]=e})}function Ge(e,t){return e.classList?e.classList.contains(t):e.className.split(Ae).indexOf(t)>-1}function q(e,t){if(t){if(z(e.length))U(e,function(e){q(e,t)});else if(e.classList)e.classList.add(t);else{var n=e.className.trim();n?n.indexOf(t)<0&&(e.className=`${n} ${t}`):e.className=t}}}function J(e,t){t&&(z(e.length)?U(e,function(e){J(e,t)}):e.classList?e.classList.remove(t):e.className.indexOf(t)>=0&&(e.className=e.className.split(Ae).filter(function(e){return e&&e!==t}).join(` `)))}function Y(e,t,n){t&&(z(e.length)?U(e,function(e){Y(e,t,n)}):n?q(e,t):J(e,t))}var Ke=/([a-z\d])([A-Z])/g;function qe(e){return typeof e==`string`?e.replace(Ke,`$1-$2`).toLowerCase():``}function Je(e,t){return B(e[t])?e[t]:e.dataset?e.dataset[t]:e.getAttribute(`data-${qe(t)}`)}function Ye(e,t,n){B(n)?e[t]=n:e.dataset?e.dataset[t]=n:e.setAttribute(`data-${qe(t)}`,n)}function Xe(e,t){if(B(e[t]))try{delete e[t]}catch{e[t]=void 0}else if(e.dataset)try{delete e.dataset[t]}catch{e.dataset[t]=void 0}else e.removeAttribute(`data-${qe(t)}`)}var Ze=function(){var e=!1;if(y){var t=!1,n=function(){},r=Object.defineProperty({},"once",{get:function(){return e=!0,t},set:function(e){t=e}});b.addEventListener(`test`,n,r),b.removeEventListener(`test`,n,r)}return e}();function X(e,t,n){var r=arguments.length>3&&arguments[3]!==void 0?arguments[3]:{};if(e&&t&&H(e.removeEventListener)){var i=n;t.trim().split(Ae).forEach(function(t){if(!Ze){var a=e.__cropperListeners__||{};a[t]&&a[t][n]&&(i=a[t][n],delete a[t][n],Object.keys(a[t]).length===0&&delete a[t],Object.keys(a).length===0&&delete e.__cropperListeners__)}e.removeEventListener(t,i,r)})}}function Z(e,t,n){var r=arguments.length>3&&arguments[3]!==void 0?arguments[3]:{};if(e&&t&&H(e.addEventListener)&&H(n)){var i=n;t.trim().split(Ae).forEach(function(t){if(r.once&&!Ze){var a=e.__cropperListeners__||{};i=function(){a[t]&&a[t][n]&&delete a[t][n],e.removeEventListener(t,i,r);var o=[...arguments];n.apply(e,o)},a[t]||(a[t]={}),a[t][n]&&e.removeEventListener(t,a[t][n],r),a[t][n]=i,e.__cropperListeners__=a}e.addEventListener(t,i,r)})}}function Q(e,t,n){if(!e||!t||!H(e.dispatchEvent))return!1;var r;if(H(Event)&&H(CustomEvent))r=new CustomEvent(t,{detail:n,bubbles:!0,cancelable:!0});else if(B(document)&&H(document.createEvent))r=document.createEvent(`CustomEvent`),r.initCustomEvent(t,!0,!0,n);else return!1;return e.dispatchEvent(r)}function Qe(e){if(!e||!H(e.getBoundingClientRect))return{left:0,top:0};var t=e.getBoundingClientRect(),n=(document||{}).documentElement,r=n===void 0?{}:n,i=b.pageXOffset||0,a=b.pageYOffset||0;return{left:t.left+(i-(r.clientLeft||0)),top:t.top+(a-(r.clientTop||0))}}var $e=b.location,et=/^(\w+:)\/\/([^:/?#]*):?(\d*)/i;function tt(e){var t=e.match(et);return t!==null&&(t[1]!==$e.protocol||t[2]!==$e.hostname||t[3]!==$e.port)}function nt(e){var t=e.indexOf(`#`),n=t>=0?e.slice(t):``,r=t>=0?e.slice(0,t):e,i=`timestamp=${new Date().getTime()}`;return`${r}${r.indexOf(`?`)===-1?`?`:`&`}${i}${n}`}function rt(e){var t=e.rotate,n=e.scaleX,r=e.scaleY,i=e.translateX,a=e.translateY,o=[];z(i)&&i!==0&&o.push(`translateX(${i}px)`),z(a)&&a!==0&&o.push(`translateY(${a}px)`),z(t)&&t!==0&&o.push(`rotate(${t}deg)`),z(n)&&n!==1&&o.push(`scaleX(${n})`),z(r)&&r!==1&&o.push(`scaleY(${r})`);var s=o.length?o.join(` `):`none`;return{WebkitTransform:s,msTransform:s,transform:s}}function it(e){var t=f({},e),n=0;return U(e,function(e,r){delete t[r],U(t,function(t){var r=Math.abs(e.startX-t.startX),i=Math.abs(e.startY-t.startY),a=Math.abs(e.endX-t.endX),o=Math.abs(e.endY-t.endY),s=Math.sqrt(r*r+i*i),c=Math.sqrt(a*a+o*o);if(Le(s)&&Le(c)&&s!==0&&c!==s){var l=(c-s)/s;Le(l)&&Math.abs(l)>Math.abs(n)&&(n=l)}})}),n}function at(e,t){var n=e.pageX,r=e.pageY,i={endX:n,endY:r};return t?i:f({startX:n,startY:r},i)}function ot(e){var t=0,n=0,r=0;return U(e,function(e){var i=e.startX,a=e.startY;t+=i,n+=a,r+=1}),t/=r,n/=r,{pageX:t,pageY:n}}function $(e){var t=e.aspectRatio,n=e.height,r=e.width,i=arguments.length>1&&arguments[1]!==void 0?arguments[1]:`contain`,a=Re(r),o=Re(n);if(a&&o){var s=n*t;i===`contain`&&s>r||i===`cover`&&s90?{width:c,height:s}:{width:s,height:c}}function ct(e,t,n,r){var i=t.aspectRatio,a=t.naturalWidth,o=t.naturalHeight,s=t.rotate,c=s===void 0?0:s,l=t.scaleX,u=l===void 0?1:l,d=t.scaleY,f=d===void 0?1:d,p=n.aspectRatio,h=n.naturalWidth,g=n.naturalHeight,_=r.fillColor,v=_===void 0?`transparent`:_,y=r.imageSmoothingEnabled,b=y===void 0||y,x=r.imageSmoothingQuality,S=x===void 0?`low`:x,C=r.maxWidth,w=C===void 0?1/0:C,T=r.maxHeight,E=T===void 0?1/0:T,D=r.minWidth,O=D===void 0?0:D,k=r.minHeight,A=k===void 0?0:k,j=document.createElement(`canvas`),M=j.getContext(`2d`),N=$({aspectRatio:p,width:w,height:E}),P=$({aspectRatio:p,width:O,height:A},`cover`),F=Math.min(N.width,Math.max(P.width,h)),I=Math.min(N.height,Math.max(P.height,g)),L=$({aspectRatio:i,width:w,height:E}),R=$({aspectRatio:i,width:O,height:A},`cover`),ee=Math.min(L.width,Math.max(R.width,a)),te=Math.min(L.height,Math.max(R.height,o)),ne=[-ee/2,-te/2,ee,te];return j.width=G(F),j.height=G(I),M.fillStyle=v,M.fillRect(0,0,F,I),M.save(),M.translate(F/2,I/2),M.rotate(c*Math.PI/180),M.scale(u,f),M.imageSmoothingEnabled=b,M.imageSmoothingQuality=S,M.drawImage.apply(M,[e].concat(m(ne.map(function(e){return Math.floor(G(e))})))),M.restore(),j}var lt=String.fromCharCode;function ut(e,t,n){var r=``;n+=t;for(var i=t;i0;)n.push(lt.apply(null,He(i.subarray(0,r)))),i=i.subarray(r);return`data:${t};base64,${btoa(n.join(``))}`}function mt(e){var t=new DataView(e),n;try{var r,i,a;if(t.getUint8(0)===255&&t.getUint8(1)===216)for(var o=t.byteLength,s=2;s+1=8&&(a=l+d)}}}if(a){for(var f=t.getUint16(a,r),p,m=0;m=0?i:Me),height:Math.max(n.offsetHeight,a>=0?a:Ne)};this.containerData=o,K(r,{width:o.width,height:o.height}),q(e,R),J(r,R)},initCanvas:function(){var e=this.containerData,t=this.imageData,n=this.options.viewMode,r=Math.abs(t.rotate)%180==90,i=r?t.naturalHeight:t.naturalWidth,a=r?t.naturalWidth:t.naturalHeight,o=i/a,s=e.width,c=e.height;e.height*o>e.width?n===3?s=e.height*o:c=e.width/o:n===3?c=e.width/o:s=e.height*o;var l={aspectRatio:o,naturalWidth:i,naturalHeight:a,width:s,height:c};this.canvasData=l,this.limited=n===1||n===2,this.limitCanvas(!0,!0),l.width=Math.min(Math.max(l.width,l.minWidth),l.maxWidth),l.height=Math.min(Math.max(l.height,l.minHeight),l.maxHeight),l.left=(e.width-l.width)/2,l.top=(e.height-l.height)/2,l.oldLeft=l.left,l.oldTop=l.top,this.initialCanvasData=W({},l)},limitCanvas:function(e,t){var n=this.options,r=this.containerData,i=this.canvasData,a=this.cropBoxData,o=n.viewMode,s=i.aspectRatio,c=this.cropped&&a;if(e){var l=Number(n.minCanvasWidth)||0,u=Number(n.minCanvasHeight)||0;o>1?(l=Math.max(l,r.width),u=Math.max(u,r.height),o===3&&(u*s>l?l=u*s:u=l/s)):o>0&&(l?l=Math.max(l,c?a.width:0):u?u=Math.max(u,c?a.height:0):c&&(l=a.width,u=a.height,u*s>l?l=u*s:u=l/s));var d=$({aspectRatio:s,width:l,height:u});l=d.width,u=d.height,i.minWidth=l,i.minHeight=u,i.maxWidth=1/0,i.maxHeight=1/0}if(t){if(o>+!c){var f=r.width-i.width,p=r.height-i.height;i.minLeft=Math.min(0,f),i.minTop=Math.min(0,p),i.maxLeft=Math.max(0,f),i.maxTop=Math.max(0,p),c&&this.limited&&(i.minLeft=Math.min(a.left,a.left+(a.width-i.width)),i.minTop=Math.min(a.top,a.top+(a.height-i.height)),i.maxLeft=a.left,i.maxTop=a.top,o===2&&(i.width>=r.width&&(i.minLeft=Math.min(0,f),i.maxLeft=Math.max(0,f)),i.height>=r.height&&(i.minTop=Math.min(0,p),i.maxTop=Math.max(0,p))))}else i.minLeft=-i.width,i.minTop=-i.height,i.maxLeft=r.width,i.maxTop=r.height}},renderCanvas:function(e,t){var n=this.canvasData,r=this.imageData;if(t){var i=st({width:r.naturalWidth*Math.abs(r.scaleX||1),height:r.naturalHeight*Math.abs(r.scaleY||1),degree:r.rotate||0}),a=i.width,o=i.height,s=n.width*(a/n.naturalWidth),c=n.height*(o/n.naturalHeight);n.left-=(s-n.width)/2,n.top-=(c-n.height)/2,n.width=s,n.height=c,n.aspectRatio=a/o,n.naturalWidth=a,n.naturalHeight=o,this.limitCanvas(!0,!1)}(n.width>n.maxWidth||n.widthn.maxHeight||n.heightt.width?i.height=i.width/n:i.width=i.height*n),this.cropBoxData=i,this.limitCropBox(!0,!0),i.width=Math.min(Math.max(i.width,i.minWidth),i.maxWidth),i.height=Math.min(Math.max(i.height,i.minHeight),i.maxHeight),i.width=Math.max(i.minWidth,i.width*r),i.height=Math.max(i.minHeight,i.height*r),i.left=t.left+(t.width-i.width)/2,i.top=t.top+(t.height-i.height)/2,i.oldLeft=i.left,i.oldTop=i.top,this.initialCropBoxData=W({},i)},limitCropBox:function(e,t){var n=this.options,r=this.containerData,i=this.canvasData,a=this.cropBoxData,o=this.limited,s=n.aspectRatio;if(e){var c=Number(n.minCropBoxWidth)||0,l=Number(n.minCropBoxHeight)||0,u=o?Math.min(r.width,i.width,i.width+i.left,r.width-i.left):r.width,d=o?Math.min(r.height,i.height,i.height+i.top,r.height-i.top):r.height;c=Math.min(c,r.width),l=Math.min(l,r.height),s&&(c&&l?l*s>c?l=c/s:c=l*s:c?l=c/s:l&&(c=l*s),d*s>u?d=u/s:u=d*s),a.minWidth=Math.min(c,u),a.minHeight=Math.min(l,d),a.maxWidth=u,a.maxHeight=d}t&&(o?(a.minLeft=Math.max(0,i.left),a.minTop=Math.max(0,i.top),a.maxLeft=Math.min(r.width,i.left+i.width)-a.width,a.maxTop=Math.min(r.height,i.top+i.height)-a.height):(a.minLeft=0,a.minTop=0,a.maxLeft=r.width-a.width,a.maxTop=r.height-a.height))},renderCropBox:function(){var e=this.options,t=this.containerData,n=this.cropBoxData;(n.width>n.maxWidth||n.widthn.maxHeight||n.height=t.width&&n.height>=t.height?E:w),K(this.cropBox,W({width:n.width,height:n.height,borderRadius:e.borderRadius},rt({translateX:n.left,translateY:n.top}))),this.cropped&&this.limited&&this.limitCanvas(!0,!0),this.disabled||this.output()},output:function(){this.preview(),Q(this.element,ue,this.getData())}},_t={initPreview:function(){var e=this.element,t=this.crossOrigin,n=this.options.preview,r=t?this.crossOriginUrl:this.url,i=e.alt||`The image to preview`,a=document.createElement(`img`);if(t&&(a.crossOrigin=t),a.src=r,a.alt=i,this.viewBox.appendChild(a),this.viewBoxImage=a,n){var o=n;typeof n==`string`?o=e.ownerDocument.querySelectorAll(n):n.querySelector&&(o=[n]),this.previews=o,U(o,function(e){var n=document.createElement(`img`);Ye(e,ae,{width:e.offsetWidth,height:e.offsetHeight,html:e.innerHTML}),t&&(n.crossOrigin=t),n.src=r,n.alt=i,n.style.cssText=`display:block;width:100%;height:auto;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation:0deg!important;`,e.innerHTML=``,e.appendChild(n)})}},resetPreview:function(){U(this.previews,function(e){var t=Je(e,ae);K(e,{width:t.width,height:t.height}),e.innerHTML=t.html,Xe(e,ae)})},preview:function(){var e=this.imageData,t=this.canvasData,n=this.cropBoxData,r=n.width,i=n.height,a=e.width,o=e.height,s=n.left-t.left-e.left,c=n.top-t.top-e.top;this.cropped&&!this.disabled&&(K(this.viewBoxImage,W({width:a,height:o},rt(W({translateX:-s,translateY:-c},e)))),U(this.previews,function(t){var n=Je(t,ae),l=n.width,u=n.height,d=l,f=u,p=1;r&&(p=l/r,f=i*p),i&&f>u&&(p=u/i,d=r*p,f=u),K(t,{width:d,height:f}),K(t.getElementsByTagName(`img`)[0],W({width:a*p,height:o*p},rt(W({translateX:-s*p,translateY:-c*p},e))))}))}},vt={bind:function(){var e=this.element,t=this.options,n=this.cropper;H(t.cropstart)&&Z(e,pe,t.cropstart),H(t.cropmove)&&Z(e,fe,t.cropmove),H(t.cropend)&&Z(e,de,t.cropend),H(t.crop)&&Z(e,ue,t.crop),H(t.zoom)&&Z(e,Te,t.zoom),Z(n,ye,this.onCropStart=this.cropStart.bind(this)),t.zoomable&&t.zoomOnWheel&&Z(n,we,this.onWheel=this.wheel.bind(this),{passive:!1,capture:!0}),t.toggleDragModeOnDblclick&&Z(n,me,this.onDblclick=this.dblclick.bind(this)),Z(e.ownerDocument,be,this.onCropMove=this.cropMove.bind(this)),Z(e.ownerDocument,xe,this.onCropEnd=this.cropEnd.bind(this)),Z(e.ownerDocument.defaultView,le,this.onCropBlur=this.cropEnd.bind(this)),t.responsive&&Z(window,Ce,this.onResize=this.resize.bind(this))},unbind:function(){var e=this.element,t=this.options,n=this.cropper;H(t.cropstart)&&X(e,pe,t.cropstart),H(t.cropmove)&&X(e,fe,t.cropmove),H(t.cropend)&&X(e,de,t.cropend),H(t.crop)&&X(e,ue,t.crop),H(t.zoom)&&X(e,Te,t.zoom),X(n,ye,this.onCropStart),t.zoomable&&t.zoomOnWheel&&X(n,we,this.onWheel,{passive:!1,capture:!0}),t.toggleDragModeOnDblclick&&X(n,me,this.onDblclick),X(e.ownerDocument,be,this.onCropMove),X(e.ownerDocument,xe,this.onCropEnd),X(e.ownerDocument.defaultView,le,this.onCropBlur),t.responsive&&X(window,Ce,this.onResize)}},yt=.5522848;function bt(e){return e.length===1?[e[0],e[0],e[0],e[0]]:e.length===2?[e[0],e[1],e[0],e[1]]:e.length===3?[e[0],e[1],e[2],e[1]]:e.slice(0,4)}function xt(e,t,n){return e.trim().split(/\s+/).map(function(e){var r=Number.parseFloat(e);return e.endsWith(`%`)?r*t/100:(r||0)*n})}function St(e,t,n,r){var i=String(e||0).trim().split(`/`),a=bt(xt(i[0],t,r)),o=bt(xt(i[1]||i[0],n,r)),s=[{x:a[0],y:o[0]},{x:a[1],y:o[1]},{x:a[2],y:o[2]},{x:a[3],y:o[3]}],c=Math.min(1,t/(s[0].x+s[1].x||1),t/(s[3].x+s[2].x||1),n/(s[0].y+s[3].y||1),n/(s[1].y+s[2].y||1));return s.map(function(e){var t=e.x,n=e.y;return{x:t*c,y:n*c}})}function Ct(e,t,n,r,i){var a=p(St(r,t,n,i),4),o=a[0],s=a[1],c=a[2],l=a[3],u=yt;e.beginPath(),e.moveTo(o.x,0),e.lineTo(t-s.x,0),e.bezierCurveTo(t-s.x+s.x*u,0,t,s.y-s.y*u,t,s.y),e.lineTo(t,n-c.y),e.bezierCurveTo(t,n-c.y+c.y*u,t-c.x+c.x*u,n,t-c.x,n),e.lineTo(l.x,n),e.bezierCurveTo(l.x-l.x*u,n,0,n-l.y+l.y*u,0,n-l.y),e.lineTo(0,o.y),e.bezierCurveTo(0,o.y-o.y*u,o.x-o.x*u,0,o.x,0),e.closePath(),e.fill()}function wt(e,t,n){var r=this.options,i=this.canvasData,a=this.containerData,o=i.width,s=i.height,c=i.naturalWidth,l=i.naturalHeight;if(e=Number(e),e>=0&&this.ready&&!this.disabled&&r.zoomable){var u=c*e,d=l*e;if(Q(this.element,Te,{ratio:e,oldRatio:o/c,originalEvent:n})===!1)return this;if(V(t)&&z(t.x)&&z(t.y))i.left-=(u-o)*((t.x-i.left)/o),i.top-=(d-s)*((t.y-i.top)/s);else if(r.zoomAroundCenter){var f=a.width/2,p=a.height/2;i.left-=(u-o)*((f-i.left)/o),i.top-=(d-s)*((p-i.top)/s)}else if(n){var m=this.pointers,h=Qe(this.cropper),g=m&&Object.keys(m).length?ot(m):{pageX:n.pageX,pageY:n.pageY};i.left-=(u-o)*((g.pageX-h.left-i.left)/o),i.top-=(d-s)*((g.pageY-h.top-i.top)/s)}else i.left-=(u-o)/2,i.top-=(d-s)/2;i.width=u,i.height=d,this.renderCanvas(!0)}return this}function Tt(e,t){var n=this.canvasData;return e=Number(e),e=e<0?1/(1-e):1+e,wt.call(this,n.width*e/n.naturalWidth,null,t)}function Et(e,t){return Tt.call(this,e,t)}var Dt={crop:function(){return this.ready&&!this.cropped&&!this.disabled&&(this.cropped=!0,this.limitCropBox(!0,!0),this.options.modal&&q(this.dragBox,ne),J(this.cropBox,R),this.setCropBoxData(this.initialCropBoxData)),this},reset:function(){return this.ready&&!this.disabled&&(this.imageData=W({},this.initialImageData),this.canvasData=W({},this.initialCanvasData),this.cropBoxData=W({},this.initialCropBoxData),this.renderCanvas(),this.cropped&&this.renderCropBox()),this},clear:function(){return this.cropped&&!this.disabled&&(W(this.cropBoxData,{left:0,top:0,width:0,height:0}),this.cropped=!1,this.renderCropBox(),this.limitCanvas(!0,!0),this.renderCanvas(),J(this.dragBox,ne),q(this.cropBox,R)),this},replace:function(e){var t=arguments.length>1&&arguments[1]!==void 0&&arguments[1];return!this.disabled&&e&&(this.isImg&&(this.element.src=e),t?(this.url=e,this.image.src=e,this.ready&&(this.viewBoxImage.src=e,U(this.previews,function(t){t.getElementsByTagName(`img`)[0].src=e}))):(this.isImg&&(this.replaced=!0),this.options.data=null,this.uncreate(),this.load(e))),this},enable:function(){return this.ready&&this.disabled&&(this.disabled=!1,J(this.cropper,L)),this},disable:function(){return this.ready&&!this.disabled&&(this.disabled=!0,q(this.cropper,L)),this},destroy:function(){var e=this.element;return e[C]&&(e[C]=void 0,this.isImg&&this.replaced&&(e.src=this.originalUrl),this.uncreate()),this},move:function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:e,n=this.canvasData,r=n.left,i=n.top;return this.moveTo(ze(e)?e:r+Number(e),ze(t)?t:i+Number(t))},moveTo:function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:e,n=this.canvasData,r=!1;return e=Number(e),t=Number(t),this.ready&&!this.disabled&&this.options.movable&&(z(e)&&(n.left=e,r=!0),z(t)&&(n.top=t,r=!0),r&&this.renderCanvas(!0)),this},zoom:function(e){return Tt.call(this,e)},zoomTo:function(e,t){return wt.call(this,e,t)},rotate:function(e){return this.rotateTo((this.imageData.rotate||0)+Number(e))},rotateTo:function(e){return e=Number(e),z(e)&&this.ready&&!this.disabled&&this.options.rotatable&&(this.imageData.rotate=e%360,this.renderCanvas(!0,!0)),this},scaleX:function(e){var t=this.imageData.scaleY;return this.scale(e,z(t)?t:1)},scaleY:function(e){var t=this.imageData.scaleX;return this.scale(z(t)?t:1,e)},scale:function(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:e,n=this.imageData,r=!1;return e=Number(e),t=Number(t),this.ready&&!this.disabled&&this.options.scalable&&(z(e)&&(n.scaleX=e,r=!0),z(t)&&(n.scaleY=t,r=!0),r&&this.renderCanvas(!0,!0)),this},getData:function(){var e=arguments.length>0&&arguments[0]!==void 0&&arguments[0],t=this.options,n=this.imageData,r=this.canvasData,i=this.cropBoxData,a;if(this.ready&&this.cropped){a={x:i.left-r.left,y:i.top-r.top,width:i.width,height:i.height};var o=n.width/n.naturalWidth;if(U(a,function(e,t){a[t]=G(e/o)}),e){var s=Math.round(a.y+a.height),c=Math.round(a.x+a.width);a.x=Math.round(a.x),a.y=Math.round(a.y),a.width=c-a.x,a.height=s-a.y}}else a={x:0,y:0,width:0,height:0};return t.rotatable&&(a.rotate=n.rotate||0),t.scalable&&(a.scaleX=n.scaleX||1,a.scaleY=n.scaleY||1),a},setData:function(e){var t=this.options,n=this.imageData,r=this.canvasData,i={};if(this.ready&&!this.disabled&&V(e)){var a=!1;t.rotatable&&z(e.rotate)&&e.rotate!==n.rotate&&(n.rotate=e.rotate,a=!0),t.scalable&&(z(e.scaleX)&&e.scaleX!==n.scaleX&&(n.scaleX=e.scaleX,a=!0),z(e.scaleY)&&e.scaleY!==n.scaleY&&(n.scaleY=e.scaleY,a=!0)),a&&this.renderCanvas(!0,!0);var o=n.width/n.naturalWidth;z(e.x)&&(i.left=e.x*o+r.left),z(e.y)&&(i.top=e.y*o+r.top),z(e.width)&&(i.width=e.width*o),z(e.height)&&(i.height=e.height*o),this.setCropBoxData(i)}return this},getContainerData:function(){return this.ready?W({},this.containerData):{}},getImageData:function(){return this.sized?W({},this.imageData):{}},getCanvasData:function(){var e=this.canvasData,t={};return this.ready&&U([`left`,`top`,`width`,`height`,`naturalWidth`,`naturalHeight`],function(n){t[n]=e[n]}),t},setCanvasData:function(e){var t=this.canvasData,n=t.aspectRatio;return this.ready&&!this.disabled&&V(e)&&(z(e.left)&&(t.left=e.left),z(e.top)&&(t.top=e.top),z(e.width)?(t.width=e.width,t.height=e.width/n):z(e.height)&&(t.height=e.height,t.width=e.height*n),this.renderCanvas(!0)),this},getCropBoxData:function(){var e=this.cropBoxData,t;return this.ready&&this.cropped&&(t={left:e.left,top:e.top,width:e.width,height:e.height}),t||{}},setCropBoxData:function(e){var t=this.cropBoxData,n=this.options.aspectRatio,r,i;return this.ready&&this.cropped&&!this.disabled&&V(e)&&(z(e.left)&&(t.left=e.left),z(e.top)&&(t.top=e.top),z(e.width)&&e.width!==t.width&&(r=!0,t.width=e.width),z(e.height)&&e.height!==t.height&&(i=!0,t.height=e.height),n&&(r?t.height=t.width/n:i&&(t.width=t.height*n)),this.renderCropBox()),this},getCroppedCanvas:function(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};if(!this.ready||!window.HTMLCanvasElement)return null;var t=this.canvasData,n=ct(this.image,this.imageData,t,e);if(!this.cropped)return n;var r=this.getData(e.rounded),i=r.x,a=r.y,o=r.width,s=r.height,c=n.width/Math.floor(t.naturalWidth);c!==1&&(i*=c,a*=c,o*=c,s*=c);var l=o/s,u=$({aspectRatio:l,width:e.maxWidth||1/0,height:e.maxHeight||1/0}),d=$({aspectRatio:l,width:e.minWidth||0,height:e.minHeight||0},`cover`),f=$({aspectRatio:l,width:e.width||(c===1?o:n.width),height:e.height||(c===1?s:n.height)}),p=f.width,h=f.height;p=Math.min(u.width,Math.max(d.width,p)),h=Math.min(u.height,Math.max(d.height,h));var g=document.createElement(`canvas`),_=g.getContext(`2d`);g.width=G(p),g.height=G(h),_.fillStyle=e.fillColor||`transparent`,_.fillRect(0,0,p,h);var v=e.imageSmoothingEnabled,y=v===void 0||v,b=e.imageSmoothingQuality;_.imageSmoothingEnabled=y,b&&(_.imageSmoothingQuality=b),H(e.beforeDraw)&&e.beforeDraw.call(this,_,g);var x=n.width,S=n.height,C=i,w=a,T,E,D,O,k,A;C<=-o||C>x?(C=0,T=0,D=0,k=0):C<=0?(D=-C,C=0,T=Math.min(x,o+C),k=T):C<=x&&(D=0,T=Math.min(o,x-C),k=T),T<=0||w<=-s||w>S?(w=0,E=0,O=0,A=0):w<=0?(O=-w,w=0,E=Math.min(S,s+w),A=E):w<=S&&(O=0,E=Math.min(s,S-w),A=E);var j=[C,w,T,E];if(k>0&&A>0){var M=p/o;j.push(D*M,O*M,k*M,A*M)}return _.drawImage.apply(_,[n].concat(m(j.map(function(e){return Math.floor(G(e))})))),this.options.borderRadius&&(_.globalCompositeOperation=`destination-in`,_.fillStyle=`#000`,Ct(_,p,h,this.options.borderRadius,p/this.cropBoxData.width)),g},setAspectRatio:function(e){var t=this.options;return!this.disabled&&!ze(e)&&(t.aspectRatio=Math.max(0,e)||NaN,this.ready&&(this.initCropBox(),this.cropped&&this.renderCropBox())),this},setDragMode:function(e){var t=this.options,n=this.dragBox,r=this.face;if(this.ready&&!this.disabled){var i=e===oe,a=t.movable&&e===se;e=i||a?e:ce,t.dragMode=e,Ye(n,ie,e),Y(n,I,i),Y(n,re,a),t.cropBoxMovable||(Ye(r,ie,e),Y(r,I,i),Y(r,re,a))}return this}},Ot={resize:function(){if(!this.disabled){var e=this.options,t=this.container,n=this.containerData,r=t.offsetWidth/n.width,i=t.offsetHeight/n.height,a=Math.abs(r-1)>Math.abs(i-1)?r:i;if(a!==1){var o,s;e.restore&&(o=this.getCanvasData(),s=this.getCropBoxData()),this.render(),e.restore&&(this.setCanvasData(U(o,function(e,t){o[t]=e*a})),this.setCropBoxData(U(s,function(e,t){s[t]=e*a})))}}},dblclick:function(){this.disabled||this.options.dragMode===ce||this.setDragMode(Ge(this.dragBox,I)?se:oe)},wheel:function(e){var t=this,n=Number(this.options.wheelZoomRatio)||.1,r=1;this.disabled||(e.preventDefault(),this.wheeling||(this.wheeling=!0,setTimeout(function(){t.wheeling=!1},50),e.deltaY?r=e.deltaY>0?1:-1:e.wheelDelta?r=-e.wheelDelta/120:e.detail&&(r=e.detail>0?1:-1),Et.call(this,-r*n,e)))},cropStart:function(e){var t=e.buttons,n=e.button;if(!(this.disabled||(e.type===`mousedown`||e.type===`pointerdown`&&e.pointerType===`mouse`)&&(z(t)&&t!==1||z(n)&&n!==0||e.ctrlKey))){var r=this.options,i=this.pointers,a;e.changedTouches?U(e.changedTouches,function(e){i[e.identifier]=at(e)}):i[e.pointerId||0]=at(e),a=Object.keys(i).length>1&&r.zoomable&&r.zoomOnTouch?D:Je(e.target,ie),De.test(a)&&Q(this.element,pe,{originalEvent:e,action:a})!==!1&&(e.preventDefault(),this.action=a,this.cropping=!1,this.cropBoxDragStartData=null,this.cropBoxDragStartAction=r.cropBoxMoveMode===`dragStart`&&a!==T&&a!==E&&a!==D?a:``,a===T&&(this.cropping=!0,q(this.dragBox,ne)))}},cropMove:function(e){var t=this.action;if(!this.disabled&&t){var n=this.pointers;e.preventDefault(),Q(this.element,fe,{originalEvent:e,action:t})!==!1&&(e.changedTouches?U(e.changedTouches,function(e){W(n[e.identifier]||{},at(e,!0))}):W(n[e.pointerId||0]||{},at(e,!0)),this.change(e))}},cropEnd:function(e){if(!this.disabled){var t=this.action,n=this.pointers;e.type===le?Object.keys(n).forEach(function(e){delete n[e]}):e.changedTouches?U(e.changedTouches,function(e){delete n[e.identifier]}):delete n[e.pointerId||0],t&&(e.preventDefault(),Object.keys(n).length||(this.action=``,this.cropBoxDragStartData=null,this.cropBoxDragStartAction=``),this.cropping&&(this.cropping=!1,Y(this.dragBox,ne,this.cropped&&this.options.modal)),Q(this.element,de,{originalEvent:e,action:t}))}}};function kt(e,t,n,r,i){var a=r.left+r.width/2,o=r.top+r.height/2,s=e===O||e===M||e===P,c=e===A||e===P||e===F,l=e===O||e===k,u=e===j||e===A,d=r.width,f=r.height;n?l||!u&&c?(d+=(s?2:-2)*t.x,f=d/n):(f+=(c?2:-2)*t.y,d=f*n):(d+=(s?2:-2)*t.x,f+=(c?2:-2)*t.y),d<0&&(d=-d,(!n||l||!u&&c)&&(s=!s)),f<0&&(f=-f,(!n||u||!u&&!c)&&(c=!c));var p=Math.max(0,2*Math.min(a-i.minLeft,i.maxWidth-a)),m=Math.max(0,2*Math.min(o-i.minTop,i.maxHeight-o)),h=Math.min(r.minWidth,p),g=Math.min(r.minHeight,m);if(n){var _=Math.max(d?h/d:0,f?g/f:0,1),v=Math.min(d?p/d:0,f?m/f:0),y=Math.min(_,v);d*=y,f*=y}else d=Math.min(Math.max(d,h),p),f=Math.min(Math.max(f,g),m);return e=s?c?P:M:c?F:N,l?e=s?O:k:u&&(e=c?A:j),{action:e,left:a-d/2,top:o-f/2,width:d,height:f}}var At={change:function(e){var t=this.options,n=this.canvasData,r=this.containerData,i=this.cropBoxData,a=this.pointers,o=this.action,s=t.aspectRatio,c=i.left,l=i.top,u=i.width,d=i.height,p=c+u,m=l+d,h=0,g=0,_=r.width,v=r.height,y=!0,b;!s&&e.shiftKey&&(s=u&&d?u/d:1),this.limited&&(h=i.minLeft,g=i.minTop,_=h+Math.min(r.width,n.width,n.left+n.width),v=g+Math.min(r.height,n.height,n.top+n.height));var x=a[Object.keys(a)[0]],S=t.cropBoxMoveMode===`dragStart`&&this.cropBoxDragStartAction&&[w,O,j,A,k,M,N,P,F].includes(o);S&&(this.cropBoxDragStartData||(this.cropBoxDragStartData=f({},i)),Object.assign(i,this.cropBoxDragStartData),c=i.left,l=i.top,u=i.width,d=i.height,p=c+u,m=l+d,o=this.cropBoxDragStartAction);var C={x:x.endX-x.startX,y:x.endY-x.startY};if(t.cropBoxResizeAroundCenter&&[O,j,A,k,M,N,P,F].includes(o)){var I=kt(o,C,s,i,{minLeft:h,minTop:g,maxWidth:_,maxHeight:v});i.left=I.left,i.top=I.top,i.width=I.width,i.height=I.height,this.action=I.action,this.renderCropBox(),S||U(a,function(e){e.startX=e.endX,e.startY=e.endY})}else{var L=function(e){switch(e){case O:p+C.x>_&&(C.x=_-p);break;case k:c+C.xv&&(C.y=v-m)}};switch(o){case w:c+=C.x,l+=C.y;break;case O:if(C.x>=0&&(p>=_||s&&(l<=g||m>=v))){y=!1;break}L(O),u+=C.x,u<0&&(o=k,u=-u,c-=u,u=_))){y=!1;break}L(j),d-=C.y,l+=C.y,d<0&&(o=A,d=-d,l-=d,d=v))){y=!1;break}L(k),u-=C.x,c+=C.x,u<0&&(o=O,u=-u,c-=u,u=0&&(m>=v||s&&(c<=h||p>=_))){y=!1;break}L(A),d+=C.y,d<0&&(o=j,d=-d,l-=d,d=_)){y=!1;break}L(j),d-=C.y,l+=C.y,u=d*s}else L(j),L(O),C.x>=0?p<_?u+=C.x:C.y<=0&&l<=g&&(y=!1):u+=C.x,C.y<=0?l>g&&(d-=C.y,l+=C.y):(d-=C.y,l+=C.y);u<0&&d<0?(o=F,d=-d,u=-u,l-=d,c-=u):u<0?(o=N,u=-u,c-=u):d<0&&(o=P,d=-d,l-=d);break;case N:if(s){if(C.y<=0&&(l<=g||c<=h)){y=!1;break}L(j),d-=C.y,l+=C.y,u=d*s,c+=i.width-u}else L(j),L(k),C.x<=0?c>h?(u-=C.x,c+=C.x):C.y<=0&&l<=g&&(y=!1):(u-=C.x,c+=C.x),C.y<=0?l>g&&(d-=C.y,l+=C.y):(d-=C.y,l+=C.y);u<0&&d<0?(o=P,d=-d,u=-u,l-=d,c-=u):u<0?(o=M,u=-u,c-=u):d<0&&(o=F,d=-d,l-=d);break;case F:if(s){if(C.x<=0&&(c<=h||m>=v)){y=!1;break}L(k),u-=C.x,c+=C.x,d=u/s}else L(A),L(k),C.x<=0?c>h?(u-=C.x,c+=C.x):C.y>=0&&m>=v&&(y=!1):(u-=C.x,c+=C.x),C.y>=0?m=0&&(p>=_||m>=v)){y=!1;break}L(O),u+=C.x,d=u/s}else L(A),L(O),C.x>=0?p<_?u+=C.x:C.y>=0&&m>=v&&(y=!1):u+=C.x,C.y>=0?m0?o=C.y>0?P:M:C.x<0&&(c-=u,o=C.y>0?F:N),C.y<0&&(l-=d),this.cropped||(J(this.cropBox,R),this.cropped=!0,this.limited&&this.limitCropBox(!0,!0))}y&&(i.width=u,i.height=d,i.left=c,i.top=l,this.action=o,this.renderCropBox()),U(a,function(e){o!==T&&!S&&(e.startX=e.endX,e.startY=e.endY)})}}},jt=b.Cropper,Mt=function(){function e(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};if(r(this,e),!t||!je.test(t.tagName))throw Error(`The first argument is required and must be an
or