diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..71464e186 --- /dev/null +++ b/.babelrc @@ -0,0 +1,13 @@ +{ + "presets": ["@babel/preset-env"], + "plugins": [ + [ + "module-resolver", { + "root": ["."], + "alias": { + "helpers": "./tests/js/helpers" + } + } + ] + ] +} diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d8d3638a2..021d7417a 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,10 +1,21 @@ # Contributing to OctoberCMS -Thank you for your interest in contributing to the OctoberCMS project! +Thank you for your interest in contributing to the OctoberCMS project. We appreciate any assistance that community members and users of OctoberCMS are willing to provide. You can contribute to the project in several different ways: + +- [Reporting a Security Vulnerability](#reporting-a-security-vulnerability) +- [Reporting an issue with OctoberCMS](#reporting-an-issue-with-octobercms) + - [Reporting an issue with an OctoberCMS plugin](#reporting-an-issue-with-an-octobercms-plugin) +- [Making a Feature Request](#making-a-feature-request) +- [Making a Pull Request](#making-a-pull-request) +- [Testing Pull Requests](#testing-a-pull-request) + +## Reporting a Security Vulnerability + +Please review [our security policy](https://github.com/octobercms/october/security/policy) on how to report security vulnerabilities. ## Reporting an issue with OctoberCMS -**Please don't use the main GitHub for reporting issues with plugins.** If you have found a bug in a plugin, the best place to report it is with the [plugin author](https://octobercms.com/plugins). +>**NOTE:** If your issue is related to an OctoberCMS plugin, please see the [Reporting an issue with an OctoberCMS plugin](#reporting-an-issue-with-an-octobercms-plugin) section below. We work hard to process bugs that are reported, to assist with this please ensure the following details are always included: @@ -16,7 +27,9 @@ We work hard to process bugs that are reported, to assist with this please ensur - **Actual behavior**: What is the actual result on running above steps i.e. the bug behavior - **include any error messages**. ->**NOTE:** Screenshots and GIFs are very helpful in visuallizing what exactly is going wrong +If possible, please provide any screenshots or GIFs of the issue occurring to provide us with additional context to determine the cause of the issue. + +>**NOTE**: If you're reporting an issue that you intend to fix yourself, you can skip the Issue step and just submit a Pull Request that fixes the issue (along with a detailed description of the original problem) instead. #### Here's how to report an issue on GitHub @@ -32,13 +45,21 @@ We work hard to process bugs that are reported, to assist with this please ensur If you find out your bug is actually a duplicate of another bug and only notice that after you created it, please also close your bug with a short reference to the other issue that was there before. -#### Reporting security issues +#### Reporting an issue with an OctoberCMS plugin -If you wish to contact us privately about any security exploits in OctoberCMS you may find, you can find our email on the [OctoberCMS website](https://octobercms.com). +>Please don't use the main GitHub for reporting issues with plugins. -## Feature requests +If you have found a bug in a plugin, the best place to report it is with the [plugin author](https://octobercms.com/plugins). -**Please don't use GitHub issues for suggesting a new feature.** If you have a feature idea, the best place to suggest it is the [OctoberCMS website forum](https://octobercms.com/forum/chan/feature-requests). +If you are unable to contact the plugin author and the issue prevents the plugin from being used correctly, please feel free to email `hello@octobercms.com`, mentioning the plugin name, URL and the issue found. We will then determine if the plugin needs to be delisted. + +#### Escalation process + +We do our best to attend to all reported issues. If you have an important issue that requires attention, consider submitting a bounty using the [OctoberCMS Bounty Program](https://www.bountysource.com/teams/october). + +## Making a Feature Request + +>**NOTE:** Please don't use GitHub issues for suggesting a new feature. If you have a feature idea, the best place to suggest it is the [OctoberCMS website forum](https://octobercms.com/forum/chan/feature-requests). Only use GitHub if you are planning on contributing a new feature and developing it. If you want to discuss your idea first, before "officially" posting it anywhere, you can always join us on [IRC](https://octobercms.com/chat) or [Slack](https://octobercms.slack.com). @@ -48,7 +69,7 @@ Feature Requests submitted as GitHub Issues specifically mean *"I'd like to see It's a great way to launch discussions on the developer side of things because both the core team and the community developer get a chance to talk about the technical side of the feature implementation. It's a great way to exchange ideas about how the logic could work in code. -## Pull Requests +## Making a Pull Request Your contributions to the project are very welcome. If you would like to fix a bug or propose a new feature, you can submit a Pull Request. @@ -61,6 +82,43 @@ To help us merge your Pull Request, please make sure you follow these points: Thank you for your contributions! +#### Best practices + +It is ideal to keep your development branch or fork synchronised with the core OctoberCMS `develop` branch when submitting Pull Requests, as this minimises the possibility of merge conflicts. + +To keep in sync with OctoberCMS, add the core OctoberCMS repository as a Git remote (ie. `upstream`) and pull changes from the OctoberCMS repository into your local `develop` branch as often as possible: + +``` +git remote add upstream git@github.com:octobercms/october.git +git fetch upstream +git checkout develop +git pull upstream develop +``` + +This ensures that your local `develop` branch matches OctoberCMS. When developing a pull request, it is best to use your own development branch. For example, creating a fix to improve spelling on a language file could be made into a branch called `lang-en-spelling-fixes`, which can be branched off from the `develop` branch. + +``` +git checkout -b lang-en-spelling-fixes develop +``` + +When you wish to update your development branch with the latest changes from the `develop` branch, it is just a simple merge: + +``` +git merge develop +``` + +This will merge all the latest changes from the OctoberCMS `develop` branch into your development branch. + +#### Resolving merge conflicts + +Occassionally, you may encounter a merge conflict with your Pull Request. This most commonly occurs if another change made to the OctoberCMS repository was made to a file that your Pull Request has also changed. + +It is the responsibility of the author of the Pull Request to resolve any merge conflicts before their Pull Request is accepted. + +You should ensure that your local copy of OctoberCMS is synchronised with with the `develop` branch in the OctoberCMS repository. Please follow the [steps above](#best-practices) to synchronise the repositories. + +If Git reports that your changes have conflicts, you will need to resolve the changes in a way that includes the changes from the OctoberCMS repository as well as implementing your Pull Request's changes. See GitHub's guide to [resolving a merge conflict](https://help.github.com/en/articles/resolving-a-merge-conflict-using-the-command-line) for tips on resolving conflicts. + #### PSR Coding standards Please ensure that your Pull Request satisfies the following coding standards: @@ -69,10 +127,28 @@ Please ensure that your Pull Request satisfies the following coding standards: - [PSR 1 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) - [PSR 0 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) +To validate your changes against our coding standards, you may run `./vendor/bin/phpcs -nq --extensions="php"` in your development folder. + #### Team rules -The October team follows the [developer guidelines](https://octobercms.com/docs/help/developer-guide) as much as possible. +The OctoberCMS team follows the [developer guidelines](https://octobercms.com/docs/help/developer-guide) as much as possible. -#### Escalation process +## Testing a Pull Request -We do our best to attend to all reported issues. If you have an important issue that requires attention, consider submitting a bounty using the [OctoberCMS Bounty Program](https://www.bountysource.com/teams/october). \ No newline at end of file +Although we aim to test all pull requests made to the OctoberCMS repository, the maintainers of OctoberCMS are volunteers and may not be able to promptly attend to all pull requests. + +To help speed things up, any assistance with testing Pull Requests and fixes will be very appreciated. The best Pull Requests to test are those that are tagged as [**Testing Needed**](https://github.com/octobercms/october/pulls?q=is%3Apr+is%3Aopen+label%3A%22Testing+Needed%22) in the repository. + +To test a Pull Request, you can use the steps below in a terminal or command-line interface to create a fresh installation of OctoberCMS with the changes made in the Pull Request, ready to test. In this example, we have a user called `qwerty123` that has created a pull request with an ID of `#4509`. + +1. Check out a copy of the OctoberCMS repository to a folder that you can view in your web browser: `git clone git@github.com:octobercms/october.git`. This will add the files into a subfolder called `october`. + +2. Then, go to the `october` subfolder and check out **@qwerty123**'s changes in a branch in your local repository: `git fetch origin pull/4509/head:pr-4509`. This will pull their changes into a branch called `pr-4509`. You will then need to check out the branch: `git checkout pr-4509`. + +3. Next, get the Composer dependencies: `composer update`. + +4. Next, run `php artisan october:env` to create a `.env` file in your folder. This will contain the configuration values for the database and site. + +5. Finally, once you've populated that file with your database and site details, run `php artisan october:up` to install the necessary database tables. + +At this point, you should have a working copy of the Pull Request ready to test. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..bc7c4b4be --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,5 @@ +# These are supported funding model platforms +custom: ['https://octobercms.com/fundraising'] +open_collective: octobercms +github: LukeTowers +patreon: LukeTowers diff --git a/.github/ISSUE_TEMPLATE/0_IMMEDIATE_SUPPORT.md b/.github/ISSUE_TEMPLATE/0_IMMEDIATE_SUPPORT.md new file mode 100644 index 000000000..96219a5f4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/0_IMMEDIATE_SUPPORT.md @@ -0,0 +1,15 @@ +--- +name: "🚨 Immediate Support" +about: 'I use October and it just broke! Help me!' +--- + +This repository is only for reporting reproducible bugs or problems. If you need support, please use the following options: + +- Slack: https://octobercms.slack.com (Get an invite: https://octobercms-slack.herokuapp.com/) +- Live chat (IRC): https://octobercms.com/chat - **Note:** Not as active as Slack +- Forum: https://octobercms.com/forum +- Stack Overflow: https://stackoverflow.com/questions/tagged/octobercms + +If you rely on OctoberCMS for your business consider purchasing a paid support plan! Send an email to octobercms@luketowers.ca to get started. + +Thanks! \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/2_MARKETPLACE_SUPPORT.md b/.github/ISSUE_TEMPLATE/2_MARKETPLACE_SUPPORT.md new file mode 100644 index 000000000..a2e76acef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2_MARKETPLACE_SUPPORT.md @@ -0,0 +1,8 @@ +--- +name: "🛒 Marketplace Support" +about: 'For reporting any issues with the marketplace, send an email to hello@octobercms.com' +--- + +All marketplace support issues will be addressed through email support. + +Thanks! diff --git a/.github/ISSUE_TEMPLATE/2_SUPPORT_QUESTION.md b/.github/ISSUE_TEMPLATE/3_GENERAL_SUPPORT.md similarity index 59% rename from .github/ISSUE_TEMPLATE/2_SUPPORT_QUESTION.md rename to .github/ISSUE_TEMPLATE/3_GENERAL_SUPPORT.md index 3ea7271c1..a76dd57b1 100644 --- a/.github/ISSUE_TEMPLATE/2_SUPPORT_QUESTION.md +++ b/.github/ISSUE_TEMPLATE/3_GENERAL_SUPPORT.md @@ -1,12 +1,12 @@ --- -name: "⚠️ Support Question" -about: 'This repository is only for reporting bugs or problems. If you need help, see: https://octobercms.com/support' +name: "⚠️ General Support" +about: 'This repository is only for reporting bugs or problems. If you need help using OctoberCMS, see: https://octobercms.com/support' --- This repository is only for reporting bugs or problems. If you need support, please use the following options: - Forum: https://octobercms.com/forum -- Slack: https://octobercms.slack.com (Invite link: https://octobercms-slack.herokuapp.com/) +- Slack: https://octobercms.slack.com (Get an invite: https://octobercms-slack.herokuapp.com/) - Stack Overflow: https://stackoverflow.com/questions/tagged/octobercms Thanks! \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/3_FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/4_FEATURE_REQUEST.md similarity index 100% rename from .github/ISSUE_TEMPLATE/3_FEATURE_REQUEST.md rename to .github/ISSUE_TEMPLATE/4_FEATURE_REQUEST.md diff --git a/.github/ISSUE_TEMPLATE/4_DOCUMENTATION.md b/.github/ISSUE_TEMPLATE/5_DOCUMENTATION.md similarity index 100% rename from .github/ISSUE_TEMPLATE/4_DOCUMENTATION.md rename to .github/ISSUE_TEMPLATE/5_DOCUMENTATION.md diff --git a/.github/ISSUE_TEMPLATE/5_SECURITY_ISSUES.md b/.github/ISSUE_TEMPLATE/5_SECURITY_ISSUES.md deleted file mode 100644 index 53abbd023..000000000 --- a/.github/ISSUE_TEMPLATE/5_SECURITY_ISSUES.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: "🔒 Security Vulnerabilities" -about: 'For reporting security-related issues, send an email to hello@octobercms.com' ---- - -PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW. - -If you discover a security vulnerability within OctoberCMS or dependencies, please send an e-mail to Samuel Georges & Luke Towers via hello@octobercms.com and octobercms@luketowers.ca respectively. All security vulnerabilities will be promptly addressed. \ No newline at end of file diff --git a/.github/workflows/archive.yml b/.github/workflows/archive.yml new file mode 100644 index 000000000..a76ad0def --- /dev/null +++ b/.github/workflows/archive.yml @@ -0,0 +1,21 @@ +name: Archive + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + archive: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 30 + days-before-close: 3 + stale-issue-message: 'This issue will be closed and archived in 3 days, as there has been no activity in the last 30 days. If this issue is still relevant or you would like to see action on it, please respond and we will get the ball rolling.' + stale-pr-message: 'This pull request will be closed and archived in 3 days, as there has been no activity in the last 30 days. If this is still being worked on, please respond and we will re-open this pull request.' + stale-issue-label: 'Status: Archived' + stale-pr-label: 'Status: Archived' + exempt-issue-label: 'Status: In Progress' + exempt-pr-label: 'Status: In Progress' diff --git a/.github/workflows/code-quality-pr.yaml b/.github/workflows/code-quality-pr.yaml new file mode 100644 index 000000000..65a0f4646 --- /dev/null +++ b/.github/workflows/code-quality-pr.yaml @@ -0,0 +1,30 @@ +name: Code Quality + +on: + pull_request: + +jobs: + codeQuality: + runs-on: ubuntu-latest + name: PHP + steps: + - name: Checkout changes + uses: actions/checkout@v1 + - name: Install PHP + uses: shivammathur/setup-php@master + with: + php-version: 7.2 + - name: Install Composer dependencies + run: composer install --no-interaction --no-progress --no-suggest + - name: Reset October modules and library + run: | + git reset --hard HEAD + rm -rf ./vendor/october/rain + wget https://github.com/octobercms/library/archive/develop.zip -O ./vendor/october/develop.zip + unzip ./vendor/october/develop.zip -d ./vendor/october + mv ./vendor/october/library-develop ./vendor/october/rain + composer dump-autoload + - name: Run code quality checks + run: | + git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" && git fetch + ./vendor/bin/phpcs --colors -nq --report="full" --extensions="php" $(git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }} HEAD) diff --git a/.github/workflows/code-quality-push.yaml b/.github/workflows/code-quality-push.yaml new file mode 100644 index 000000000..3de3f6a97 --- /dev/null +++ b/.github/workflows/code-quality-push.yaml @@ -0,0 +1,31 @@ +name: Code Quality + +on: + push: + branches: + - master + - develop + +jobs: + codeQuality: + runs-on: ubuntu-latest + name: PHP + steps: + - name: Checkout changes + uses: actions/checkout@v1 + - name: Install PHP + uses: shivammathur/setup-php@master + with: + php-version: 7.2 + - name: Install Composer dependencies + run: composer install --no-interaction --no-progress --no-suggest + - name: Reset October modules and library + run: | + git reset --hard HEAD + rm -rf ./vendor/october/rain + wget https://github.com/octobercms/library/archive/develop.zip -O ./vendor/october/develop.zip + unzip ./vendor/october/develop.zip -d ./vendor/october + mv ./vendor/october/library-develop ./vendor/october/rain + composer dump-autoload + - name: Run code quality checks + run: ./vendor/bin/phpcs --colors -nq --report="full" --extensions="php" $(git show --name-only --pretty="" --diff-filter=ACMR ${{ github.sha }}) diff --git a/.github/workflows/frontend-tests.yaml b/.github/workflows/frontend-tests.yaml new file mode 100644 index 000000000..9d0068a0a --- /dev/null +++ b/.github/workflows/frontend-tests.yaml @@ -0,0 +1,24 @@ +name: Tests + +on: + push: + branches: + - master + - develop + pull_request: + +jobs: + frontendTests: + runs-on: ubuntu-latest + name: JavaScript + steps: + - name: Checkout changes + uses: actions/checkout@v1 + - name: Install Node + uses: actions/setup-node@v1 + with: + node-version: 8 + - name: Install Node dependencies + run: npm install + - name: Run tests + run: npm run test diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..b3ce5ca08 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,39 @@ +name: Tests + +on: + push: + branches: + - master + - develop + pull_request: + +jobs: + phpUnitTests: + runs-on: ubuntu-latest + strategy: + max-parallel: 6 + matrix: + phpVersions: ['7.1', '7.2', '7.3'] + fail-fast: false + name: PHP ${{ matrix.phpVersions }} + steps: + - name: Checkout changes + uses: actions/checkout@v1 + - name: Install PHP + uses: shivammathur/setup-php@master + with: + php-version: ${{ matrix.phpVersions }} + - name: Install Composer dependencies + run: composer install --no-interaction --no-progress --no-suggest + - name: Reset October modules and library + run: | + git reset --hard HEAD + rm -rf ./vendor/october/rain + wget https://github.com/octobercms/library/archive/develop.zip -O ./vendor/october/develop.zip + unzip ./vendor/october/develop.zip -d ./vendor/october + mv ./vendor/october/library-develop ./vendor/october/rain + composer dump-autoload + - name: Run Linting and Tests + run: | + ./vendor/bin/parallel-lint --exclude vendor --exclude storage --exclude tests/fixtures/plugins/testvendor/goto/Plugin.php . + ./vendor/bin/phpunit diff --git a/.github/workflows/wrong-branch-notification.yaml b/.github/workflows/wrong-branch-notification.yaml new file mode 100644 index 000000000..ab67a9113 --- /dev/null +++ b/.github/workflows/wrong-branch-notification.yaml @@ -0,0 +1,29 @@ +name: Wrong Branch + +on: + pull_request: + types: [opened] + branches: + - master + +jobs: + wrongBranch: + runs-on: ubuntu-latest + name: Fix Wrong Branch + steps: + - name: Alert submitter + run: | + export ISSUE_NUMBER=$(echo '${{ github.ref }}' | cut -d'/' -f3) + curl -s --request POST \ + --url https://api.github.com/repos/${{ github.repository }}/issues/${ISSUE_NUMBER}/comments \ + --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ + --header 'content-type: application/json' \ + --data '{"body": "This pull request has been made to the wrong branch. Please review [the contributing guidelines](https://github.com/octobercms/october/blob/master/.github/CONTRIBUTING.md#making-a-pull-request) as all PRs need to be made to the `develop` branch.\n\nWe'\''ll fix it for you this time, but please ensure you make any future PRs to the `develop` branch, not the `master` branch."}' > /dev/null + - name: Change base branch + run: | + export ISSUE_NUMBER=$(echo '${{ github.ref }}' | cut -d'/' -f3) + curl -s --request PATCH \ + --url https://api.github.com/repos/${{ github.repository }}/pulls/${ISSUE_NUMBER} \ + --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \ + --header 'content-type: application/json' \ + --data '{"base": "develop"}' > /dev/null diff --git a/.gitignore b/.gitignore index c5ebf3e67..4cd08cf9a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ sftp-config.json .ftpconfig selenium.php composer.lock +package-lock.json +/node_modules +_ide_helper.php # for netbeans nbproject diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 000000000..bb55890a9 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,5 @@ +{ + "esversion": 6, + "curly": true, + "asi": true +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bac7f1100..000000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: php - -php: - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - nightly - -matrix: - allow_failures: - - php: nightly - -sudo: false - -install: - - composer self-update - - travis_retry composer install --no-interaction --prefer-source - -before_script: git reset --hard HEAD - -script: vendor/bin/phpunit diff --git a/README.md b/README.md index ef85392f0..362c4aab1 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,14 @@ October

-[October](http://octobercms.com) is a Content Management System (CMS) and web platform whose sole purpose is to make your development workflow simple again. It was born out of frustration with existing systems. We feel building websites has become a convoluted and confusing process that leaves developers unsatisfied. We want to turn you around to the simpler side and get back to basics. +[October](https://octobercms.com) is a Content Management Framework (CMF) and web platform whose sole purpose is to make your development workflow simple again. It was born out of frustration with existing systems. We feel building websites has become a convoluted and confusing process that leaves developers unsatisfied. We want to turn you around to the simpler side and get back to basics. October's mission is to show the world that web development is not rocket science. [![Build Status](https://travis-ci.org/octobercms/october.svg?branch=develop)](https://travis-ci.org/octobercms/october) [![License](https://poser.pugx.org/october/october/license.svg)](https://packagist.org/packages/october/october) -### Learning October - -The best place to learn October is by [reading the documentation](https://octobercms.com/docs) or [following some tutorials](https://octobercms.com/support/articles/tutorials). - -You may also watch these introductory videos for [beginners](https://vimeo.com/79963873) and [advanced users](https://vimeo.com/172202661). - -### Installing October +## Installing October Instructions on how to install October can be found at the [installation guide](https://octobercms.com/docs/setup/installation). @@ -33,35 +27,54 @@ If you plan on using a database, run this command: php artisan october:install ``` -### Development Team +## Learning October -October was created by [Alexey Bobkov](http://ca.linkedin.com/pub/aleksey-bobkov/2b/ba0/232) and [Samuel Georges](https://www.linkedin.com/in/samuel-georges-0a964131/), who (along with [Luke Towers](https://luketowers.ca/)) continue to develop the platform. +The best place to learn October is by [reading the documentation](https://octobercms.com/docs) or [following some tutorials](https://octobercms.com/support/articles/tutorials). -### Foundation library +You may also watch these introductory videos for [beginners](https://vimeo.com/79963873) and [advanced users](https://vimeo.com/172202661). There is also the excellent video series by [Watch & Learn](https://watch-learn.com/series/making-websites-with-october-cms). -The CMS uses [Laravel](https://laravel.com) as a foundation PHP framework. - -### Contact - -You can communicate with us using the following mediums: - -* [Follow us on Twitter](https://twitter.com/octobercms) for announcements and updates. -* [Follow us on Facebook](https://facebook.com/octobercms) for announcements and updates. -* [Join us on IRC](https://octobercms.com/chat) to chat with us. -* [Join us on Slack](https://octobercms-slack.herokuapp.com/) to chat with us. - -### License - -The OctoberCMS platform is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). - -### Contributing +## Contributing Before sending a Pull Request, be sure to review the [Contributing Guidelines](.github/CONTRIBUTING.md) first. -### Coding standards +### Help and support this project + +You can also help the project by reviewing and testing open Pull Requests with the "**Status: Testing Needed**" tag. +[Read more...](https://github.com/octobercms/october/blob/master/.github/CONTRIBUTING.md#testing-pull-requests) + +## Coding standards Please follow the following guides and code standards: * [PSR 4 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md) * [PSR 2 Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) * [PSR 1 Coding Standards](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) + +## Code of Conduct + +In order to ensure that the OctoberCMS community is welcoming to all, please review and abide by the [Code of Conduct](CODE_OF_CONDUCT.md). + +## Security Vulnerabilities + +Please review [our security policy](https://github.com/octobercms/october/security/policy) on how to report security vulnerabilities. + +## Development Team + +October was created by [Alexey Bobkov](http://ca.linkedin.com/pub/aleksey-bobkov/2b/ba0/232) and [Samuel Georges](https://www.linkedin.com/in/samuel-georges-0a964131/). The core maintainer is [Luke Towers](https://luketowers.ca/) and other maintainers include [Ben Thomson](https://github.com/bennothommo) and [Denis Denisov](https://github.com/w20k). + +## Foundation library + +The CMS uses [Laravel](https://laravel.com) as a foundation PHP framework. + +## Contact + +You can communicate with us using the following mediums: + +* [Follow us on Twitter](https://twitter.com/octobercms) for announcements and updates. +* [Follow us on Facebook](https://facebook.com/octobercms) for announcements and updates. +* [Join us on Slack](https://octobercms-slack.herokuapp.com/) to chat with us. +* [Join us on IRC](https://octobercms.com/chat) to chat with us. + +## License + +The OctoberCMS platform is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..dc9de11e8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +**PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, [SEE BELOW](#reporting-a-vulnerability).** + +## Supported Versions + +October is evergreen, no one version is singled out for security fixes because there is no way to update just one version. Builds are continually released and security fixes will always be available in the latest build. + +## Reporting a Vulnerability + +If you discover a security vulnerability within OctoberCMS, please send an email to Samuel Georges at hello@octobercms.com and Luke Towers at octobercms@luketowers.ca. All security vulnerabilities will be promptly addressed. diff --git a/composer.json b/composer.json index 39af52963..e08f1cdde 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,8 @@ { "name": "october/october", - "description": "October CMS", + "description": "OctoberCMS", "homepage": "https://octobercms.com", + "type": "project", "keywords": ["october", "cms", "octobercms", "laravel"], "license": "MIT", "authors": [ @@ -42,12 +43,16 @@ }, "require-dev": { "fzaninotto/faker": "~1.7", - "phpunit/phpunit": "~5.7", + "phpunit/phpunit": "~6.5", "phpunit/phpunit-selenium": "~1.2", - "meyfa/phpunit-assert-gd": "1.1.0" + "meyfa/phpunit-assert-gd": "1.1.0", + "squizlabs/php_codesniffer": "3.*", + "jakub-onderka/php-parallel-lint": "^1.0" }, "autoload-dev": { "classmap": [ + "tests/concerns/InteractsWithAuthentication.php", + "tests/fixtures/backend/models/UserFixture.php", "tests/TestCase.php", "tests/UiTestCase.php", "tests/PluginTestCase.php" diff --git a/config/cms.php b/config/cms.php index 75189b297..acb98db9a 100644 --- a/config/cms.php +++ b/config/cms.php @@ -93,6 +93,21 @@ return [ 'backendSkin' => 'Backend\Skins\Standard', + /* + |-------------------------------------------------------------------------- + | Automatically run migrations on login + |-------------------------------------------------------------------------- + | + | If value is true, UpdateManager will be run on logging in to the backend. + | It's recommended to set this value to 'null' in production enviroments + | because it clears the cache every time a user logs in to the backend. + | If set to null, this setting is enabled when debug mode (app.debug) is enabled + | and disabled when debug mode is disabled. + | + */ + + 'runMigrationsOnLogin' => null, + /* |-------------------------------------------------------------------------- | Determines which modules to load @@ -359,8 +374,8 @@ return [ | Cross Site Request Forgery (CSRF) Protection |-------------------------------------------------------------------------- | - | If the CSRF protection is enabled, all "postback" requests are checked - | for a valid security token. + | If the CSRF protection is enabled, all "postback" & AJAX requests are + | checked for a valid security token. | */ @@ -413,4 +428,26 @@ return [ 'restrictBaseDir' => true, + /* + |-------------------------------------------------------------------------- + | Backend Service Worker + |-------------------------------------------------------------------------- + | + | Allow plugins to run Service Workers in the backend. + | + | WARNING: This should always be disabled for security reasons as Service + | Workers can be hijacked and used to run XSS into the backend. Turning + | this feature on can create a conflict if you have a frontend Service + | Worker running. The 'scope' needs to be correctly set and not have a + | duplicate subfolder structure on the frontend, otherwise it will run + | on both the frontend and backend of your website. + | + | true - allow service workers to run in the backend + | + | false - disallow service workers to run in the backend + | + */ + + 'enableBackendServiceWorkers' => false, + ]; diff --git a/config/develop.php b/config/develop.php new file mode 100644 index 000000000..cd4aee7d7 --- /dev/null +++ b/config/develop.php @@ -0,0 +1,24 @@ + false, + +]; diff --git a/config/filesystems.php b/config/filesystems.php index a265cbb3e..4d843013c 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -46,6 +46,7 @@ return [ 'local' => [ 'driver' => 'local', 'root' => storage_path('app'), + 'url' => '/storage/app', ], 's3' => [ diff --git a/config/services.php b/config/services.php index 5a2795a17..c2d453065 100644 --- a/config/services.php +++ b/config/services.php @@ -17,6 +17,7 @@ return [ 'mailgun' => [ 'domain' => '', 'secret' => '', + 'endpoint' => 'api.mailgun.net', // api.eu.mailgun.net for EU ], 'mandrill' => [ @@ -29,6 +30,10 @@ return [ 'region' => 'us-east-1', ], + 'sparkpost' => [ + 'secret' => '', + ], + 'stripe' => [ 'model' => 'User', 'secret' => '', diff --git a/config/testing/cms.php b/config/testing/cms.php index d29bbc3c3..8267ac3e4 100644 --- a/config/testing/cms.php +++ b/config/testing/cms.php @@ -109,4 +109,16 @@ return [ 'themesPathLocal' => base_path('tests/fixtures/themes'), + /* + |-------------------------------------------------------------------------- + | Cross Site Request Forgery (CSRF) Protection + |-------------------------------------------------------------------------- + | + | If the CSRF protection is enabled, all "postback" requests are checked + | for a valid security token. + | + */ + + 'enableCsrfProtection' => false + ]; diff --git a/modules/backend/ServiceProvider.php b/modules/backend/ServiceProvider.php index 859c61ba7..60ef1b9e0 100644 --- a/modules/backend/ServiceProvider.php +++ b/modules/backend/ServiceProvider.php @@ -76,6 +76,8 @@ class ServiceProvider extends ModuleServiceProvider $combiner->registerBundle('~/modules/backend/formwidgets/fileupload/assets/less/fileupload.less'); $combiner->registerBundle('~/modules/backend/formwidgets/nestedform/assets/less/nestedform.less'); $combiner->registerBundle('~/modules/backend/formwidgets/richeditor/assets/js/build-plugins.js'); + $combiner->registerBundle('~/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less'); + $combiner->registerBundle('~/modules/backend/formwidgets/permissioneditor/assets/less/permissioneditor.less'); /* * Rich Editor is protected by DRM diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index f995f7ae2..fb572f7c5 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -169,9 +169,9 @@ html.mobile .control-scrollbar {overflow:auto;-webkit-overflow-scrolling:touch} .control-filelist ul li.group >h4 a:after, .control-filelist ul li.group >div.group >h4 a:after {width:10px;height:10px;display:block;position:absolute;top:1px} .control-filelist ul li.group >h4 a:after, -.control-filelist ul li.group >div.group >h4 a:after {left:33px;top:9px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f07b";color:#a1aab1;font-size:16px} +.control-filelist ul li.group >div.group >h4 a:after {left:33px;top:9px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f07b";color:#a1aab1;font-size:16px} .control-filelist ul li.group >h4 a:before, -.control-filelist ul li.group >div.group >h4 a:before {left:20px;top:9px;color:#cfcfcf;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f0da";-webkit-transform:rotate(90deg) translate(5px,0);-ms-transform:rotate(90deg) translate(5px,0);transform:rotate(90deg) translate(5px,0);-webkit-transition:all 0.1s ease;transition:all 0.1s ease} +.control-filelist ul li.group >div.group >h4 a:before {left:20px;top:9px;color:#cfcfcf;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f0da";-webkit-transform:rotate(90deg) translate(5px,0);-ms-transform:rotate(90deg) translate(5px,0);transform:rotate(90deg) translate(5px,0);-webkit-transition:all 0.1s ease;transition:all 0.1s ease} .control-filelist ul li.group >ul >li >a {padding-left:52px} .control-filelist ul li.group >ul >li.group {padding-left:20px} .control-filelist ul li.group >ul >li.group >ul >li >a {padding-left:324px;margin-left:-270px} @@ -276,10 +276,10 @@ html.mobile .control-scrollbar {overflow:auto;-webkit-overflow-scrolling:touch} .control-treelist >ol >li >div.record:before {display:none} .control-treelist li {margin:0;padding:0} .control-treelist li >div.record {margin:0;font-size:12px;margin-bottom:5px;position:relative;display:block} -.control-treelist li >div.record:before {color:#bdc3c7;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f111";font-size:6px;position:absolute;left:-18px;top:11px} +.control-treelist li >div.record:before {color:#bdc3c7;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f111";font-size:6px;position:absolute;left:-18px;top:11px} .control-treelist li >div.record >a.move {display:inline-block;padding:7px 0 7px 10px;text-decoration:none;color:#bdc3c7} .control-treelist li >div.record >a.move:hover {color:#4ea5e0} -.control-treelist li >div.record >a.move:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f0c9"} +.control-treelist li >div.record >a.move:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f0c9"} .control-treelist li >div.record >span {color:#666;display:inline-block;padding:7px 15px 7px 5px} .control-treelist li.dragged {position:absolute;z-index:2000;width:auto !important;height:auto !important} .control-treelist li.dragged >div.record {opacity:0.5;filter:alpha(opacity=50);background:#4ea5e0 !important} @@ -287,7 +287,7 @@ html.mobile .control-scrollbar {overflow:auto;-webkit-overflow-scrolling:touch} .control-treelist li.dragged >div.record >span {color:white} .control-treelist li.dragged >div.record:before {display:none} .control-treelist li.placeholder {display:inline-block;position:relative;background:#4ea5e0 !important;height:25px;margin-bottom:5px} -.control-treelist li.placeholder:before {display:block;position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f053";color:#d35714;left:-10px;top:8px;z-index:2000} +.control-treelist li.placeholder:before {display:block;position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f053";color:#d35714;left:-10px;top:8px;z-index:2000} .control-treeview {margin-bottom:40px} .control-treeview ol {margin:0;padding:0;list-style:none;background:#fff} .control-treeview ol >li {-webkit-transition:width 1s;transition:width 1s} @@ -296,9 +296,9 @@ html.mobile .control-scrollbar {overflow:auto;-webkit-overflow-scrolling:touch} .control-treeview ol >li >div:before {content:' ';background-image:url(../images/treeview-icons.png);background-position:0 -28px;background-repeat:no-repeat;background-size:42px auto;position:absolute;width:21px;height:22px;left:28px;top:15px} .control-treeview ol >li >div span.comment {display:block;font-weight:400;color:#95a5a6;font-size:13px;margin-top:2px;overflow:hidden;text-overflow:ellipsis} .control-treeview ol >li >div >span.expand {font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;display:none;position:absolute;width:20px;height:20px;top:19px;left:2px;cursor:pointer;color:#bdc3c7;-webkit-transition:transform 0.1s ease;transition:transform 0.1s ease} -.control-treeview ol >li >div >span.expand:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f0da";line-height:100%;font-size:15px;position:relative;left:8px;top:2px} +.control-treeview ol >li >div >span.expand:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f0da";line-height:100%;font-size:15px;position:relative;left:8px;top:2px} .control-treeview ol >li >div >span.drag-handle {font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0;-webkit-transition:opacity 0.4s;transition:opacity 0.4s;position:absolute;right:9px;bottom:0;width:18px;height:19px;cursor:move;color:#bdc3c7;opacity:0;filter:alpha(opacity=0)} -.control-treeview ol >li >div >span.drag-handle:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f0c9";font-size:18px} +.control-treeview ol >li >div >span.drag-handle:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f0c9";font-size:18px} .control-treeview ol >li >div span.borders {font-size:0} .control-treeview ol >li >div >ul.submenu {position:absolute;left:20px;bottom:-36.9px;padding:0;list-style:none;z-index:200;height:37px;display:none;margin-left:15px;background:transparent url(../images/treeview-submenu-tabs.png) repeat-x left -39px} .control-treeview ol >li >div >ul.submenu:before, @@ -449,7 +449,7 @@ body.dragging .control-treeview.treeview-light ol.dragging ol >li >div {backgrou .sidenav-tree ul.top-level >li[data-status=collapsed] ul {display:none} .sidenav-tree ul.top-level >li >div.group {position:relative} .sidenav-tree ul.top-level >li >div.group h3 {background:rgba(0,0,0,0.15);color:#ecf0f1;text-transform:uppercase;font-size:15px;padding:15px 15px 15px 40px;margin:0;position:relative;cursor:pointer;font-weight:400} -.sidenav-tree ul.top-level >li >div.group h3:before {display:block;position:absolute;width:10px;height:10px;left:16px;top:15px;color:#cfcfcf;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105";-webkit-transform:rotate(90deg) translate(5px,-3px);-ms-transform:rotate(90deg) translate(5px,-3px);transform:rotate(90deg) translate(5px,-3px);-webkit-transition:all 0.1s ease;transition:all 0.1s ease;font-size:16px} +.sidenav-tree ul.top-level >li >div.group h3:before {display:block;position:absolute;width:10px;height:10px;left:16px;top:15px;color:#cfcfcf;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105";-webkit-transform:rotate(90deg) translate(5px,-3px);-ms-transform:rotate(90deg) translate(5px,-3px);transform:rotate(90deg) translate(5px,-3px);-webkit-transition:all 0.1s ease;transition:all 0.1s ease;font-size:16px} .sidenav-tree ul.top-level >li >div.group:before, .sidenav-tree ul.top-level >li >div.group:after {content:'';display:block;width:0;height:0;border-left:7.5px solid transparent;border-right:7.5px solid transparent;border-top:8px solid #34495e;border-bottom-width:0;position:absolute;left:15px;bottom:-8px;z-index:101} .sidenav-tree ul.top-level >li >div.group:after {content:'';display:block;width:0;height:0;border-left:7.5px solid transparent;border-right:7.5px solid transparent;border-top:8px solid rgba(0,0,0,0.15);border-bottom-width:0} @@ -495,7 +495,7 @@ div.panel >label {margin-bottom:5px} div.panel .nav.selector-group {margin:0 -20px 20px -20px} ul.tree-path {list-style:none;padding:0;margin-bottom:0} ul.tree-path li {display:inline-block;margin-right:1px;font-size:13px} -ul.tree-path li:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105";display:inline-block;font-size:13px;margin-left:5px;position:relative;top:1px;color:#95a5a6} +ul.tree-path li:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105";display:inline-block;font-size:13px;margin-left:5px;position:relative;top:1px;color:#95a5a6} ul.tree-path li:last-child a {cursor:default} ul.tree-path li:last-child:after {display:none} ul.tree-path li.go-up {font-size:12px;margin-right:7px} @@ -685,7 +685,7 @@ nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-preview a {position:relative nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-account {margin-right:0} nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-account >a {padding:0 15px 0 10px;font-size:13px;position:relative} nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-account.highlight >a {z-index:600} -nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-account img.account-avatar {width:45px} +nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-account img.account-avatar {width:45px;height:45px} nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-account .account-name {margin-right:15px} nav#layout-mainmenu ul.mainmenu-toolbar li.mainmenu-account ul {line-height:23px} html.svg nav#layout-mainmenu img.svg-icon, @@ -778,7 +778,7 @@ nav#layout-mainmenu.navbar-mode-collapse .menu-toggle {display:inline-block;colo .mainmenu-collapsed >div ul li a i {line-height:1;font-size:30px;vertical-align:middle} .mainmenu-collapsed >div ul li a img.svg-icon {height:30px;width:30px;position:relative;top:0} .mainmenu-collapsed .scroll-marker {position:absolute;left:0;width:100%;height:10px;display:none} -.mainmenu-collapsed .scroll-marker:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f141";display:block;position:absolute;left:50%;margin-left:-3px;top:0;height:9px;font-size:10px;color:rgba(255,255,255,0.6)} +.mainmenu-collapsed .scroll-marker:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f141";display:block;position:absolute;left:50%;margin-left:-3px;top:0;height:9px;font-size:10px;color:rgba(255,255,255,0.6)} .mainmenu-collapsed .scroll-marker.before {top:0} .mainmenu-collapsed .scroll-marker.after {bottom:3px} .mainmenu-collapsed .scroll-marker.after:after {top:2px} @@ -940,7 +940,7 @@ body.breadcrumb-fancy .control-breadcrumb li:last-child:before, .fancy-layout .control-tabs.master-tabs >div >div.tabs-container >ul.nav-tabs >li[data-modified] span.tab-close i, .fancy-layout.control-tabs.master-tabs >div >div.tabs-container >ul.nav-tabs >li[data-modified] span.tab-close i {top:5px;font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} .fancy-layout .control-tabs.master-tabs >div >div.tabs-container >ul.nav-tabs >li[data-modified] span.tab-close i:before, -.fancy-layout.control-tabs.master-tabs >div >div.tabs-container >ul.nav-tabs >li[data-modified] span.tab-close i:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f111";font-size:9px} +.fancy-layout.control-tabs.master-tabs >div >div.tabs-container >ul.nav-tabs >li[data-modified] span.tab-close i:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f111";font-size:9px} .fancy-layout .control-tabs.master-tabs >div >div.tabs-container >ul.nav-tabs >li:first-child, .fancy-layout.control-tabs.master-tabs >div >div.tabs-container >ul.nav-tabs >li:first-child {margin-left:0} .fancy-layout .control-tabs.master-tabs[data-closable] >div >div.tabs-container >ul.nav-tabs >li a >span.title, diff --git a/modules/backend/assets/js/auth/uninstall-sw.js b/modules/backend/assets/js/auth/uninstall-sw.js deleted file mode 100644 index eb48b3938..000000000 --- a/modules/backend/assets/js/auth/uninstall-sw.js +++ /dev/null @@ -1,10 +0,0 @@ -// Only run on HTTPS connections -if (location.protocol === 'https:') { - // Unregister all service workers before signing in to prevent cache issues - navigator.serviceWorker.getRegistrations().then( - function(registrations) { - for (let registration of registrations) { - registration.unregister(); - } - }); -} \ No newline at end of file diff --git a/modules/backend/assets/js/october-min.js b/modules/backend/assets/js/october-min.js index 3820594b4..a25834ef2 100644 --- a/modules/backend/assets/js/october-min.js +++ b/modules/backend/assets/js/october-min.js @@ -36,145 +36,211 @@ return(document.cookie=[encode(key),'=',stringifyCookieValue(value),options.expi var result=key?undefined:{};var cookies=document.cookie?document.cookie.split('; '):[];for(var i=0,l=cookies.length;i1?_len-1:0),_key=1;_key<_len;_key++){args[_key-1]=arguments[_key];} +for(var _iterator=callbacks,_isArray=true,_i=0,_iterator=_isArray?_iterator:_iterator[Symbol.iterator]();;){var _ref;if(_isArray){if(_i>=_iterator.length)break;_ref=_iterator[_i++];}else{_i=_iterator.next();if(_i.done)break;_ref=_i.value;} +var callback=_ref;callback.apply(this,args);}} +return this;}},{key:"off",value:function off(event,fn){if(!this._callbacks||arguments.length===0){this._callbacks={};return this;} +var callbacks=this._callbacks[event];if(!callbacks){return this;} if(arguments.length===1){delete this._callbacks[event];return this;} -for(i=_i=0,_len=callbacks.length;_i<_len;i=++_i){callback=callbacks[i];if(callback===fn){callbacks.splice(i,1);break;}} -return this;};return Emitter;})();Dropzone=(function(_super){var extend,resolveOption;__extends(Dropzone,_super);Dropzone.prototype.Emitter=Emitter;Dropzone.prototype.events=["drop","dragstart","dragend","dragenter","dragover","dragleave","addedfile","removedfile","thumbnail","error","errormultiple","processing","processingmultiple","uploadprogress","totaluploadprogress","sending","sendingmultiple","success","successmultiple","canceled","canceledmultiple","complete","completemultiple","reset","maxfilesexceeded","maxfilesreached","queuecomplete"];Dropzone.prototype.defaultOptions={url:null,method:"post",withCredentials:false,parallelUploads:2,uploadMultiple:false,maxFilesize:256,paramName:"file",createImageThumbnails:true,maxThumbnailFilesize:10,thumbnailWidth:120,thumbnailHeight:120,filesizeBase:1000,maxFiles:null,filesizeBase:1000,params:{},clickable:true,ignoreHiddenFiles:true,acceptedFiles:null,acceptedMimeTypes:null,autoProcessQueue:true,autoQueue:true,addRemoveLinks:false,previewsContainer:null,capture:null,dictDefaultMessage:"Drop files here to upload",dictFallbackMessage:"Your browser does not support drag'n'drop file uploads.",dictFallbackText:"Please use the fallback form below to upload your files like in the olden days.",dictFileTooBig:"File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.",dictInvalidFileType:"You can't upload files of this type.",dictResponseError:"Server responded with {{statusCode}} code.",dictCancelUpload:"Cancel upload",dictCancelUploadConfirmation:"Are you sure you want to cancel this upload?",dictRemoveFile:"Remove file",dictRemoveFileConfirmation:null,dictMaxFilesExceeded:"You can not upload any more files.",accept:function(file,done){return done();},init:function(){return noop;},forceFallback:false,fallback:function(){var child,messageElement,span,_i,_len,_ref;this.element.className=""+this.element.className+" dz-browser-not-supported";_ref=this.element.getElementsByTagName("div");for(_i=0,_len=_ref.length;_i<_len;_i++){child=_ref[_i];if(/(^| )dz-message($| )/.test(child.className)){messageElement=child;child.className="dz-message";continue;}} +for(var i=0;i=_iterator2.length)break;_ref2=_iterator2[_i2++];}else{_i2=_iterator2.next();if(_i2.done)break;_ref2=_i2.value;} +var child=_ref2;if(/(^| )dz-message($| )/.test(child.className)){messageElement=child;child.className="dz-message";break;}} if(!messageElement){messageElement=Dropzone.createElement("
");this.element.appendChild(messageElement);} -span=messageElement.getElementsByTagName("span")[0];if(span){span.textContent=this.options.dictFallbackMessage;} -return this.element.appendChild(this.getFallbackForm());},resize:function(file){var info,srcRatio,trgRatio;info={srcX:0,srcY:0,srcWidth:file.width,srcHeight:file.height};srcRatio=file.width/file.height;info.optWidth=this.options.thumbnailWidth;info.optHeight=this.options.thumbnailHeight;if((info.optWidth==null)&&(info.optHeight==null)){info.optWidth=info.srcWidth;info.optHeight=info.srcHeight;}else if(info.optWidth==null){info.optWidth=srcRatio*info.optHeight;}else if(info.optHeight==null){info.optHeight=(1/srcRatio)*info.optWidth;} -trgRatio=info.optWidth/info.optHeight;if(file.heighttrgRatio){info.srcHeight=file.height;info.srcWidth=info.srcHeight*trgRatio;}else{info.srcWidth=file.width;info.srcHeight=info.srcWidth/trgRatio;}} -info.srcX=(file.width-info.srcWidth)/2;info.srcY=(file.height-info.srcHeight)/2;return info;},drop:function(e){return this.element.classList.remove("dz-drag-hover");},dragstart:noop,dragend:function(e){return this.element.classList.remove("dz-drag-hover");},dragenter:function(e){return this.element.classList.add("dz-drag-hover");},dragover:function(e){return this.element.classList.add("dz-drag-hover");},dragleave:function(e){return this.element.classList.remove("dz-drag-hover");},paste:noop,reset:function(){return this.element.classList.remove("dz-started");},addedfile:function(file){var node,removeFileEvent,removeLink,_i,_j,_k,_len,_len1,_len2,_ref,_ref1,_ref2,_results;if(this.element===this.previewsContainer){this.element.classList.add("dz-started");} -if(this.previewsContainer){file.previewElement=Dropzone.createElement(this.options.previewTemplate.trim());file.previewTemplate=file.previewElement;this.previewsContainer.appendChild(file.previewElement);_ref=file.previewElement.querySelectorAll("[data-dz-name]");for(_i=0,_len=_ref.length;_i<_len;_i++){node=_ref[_i];node.textContent=file.name;} -_ref1=file.previewElement.querySelectorAll("[data-dz-size]");for(_j=0,_len1=_ref1.length;_j<_len1;_j++){node=_ref1[_j];node.innerHTML=this.filesize(file.size);} +var span=messageElement.getElementsByTagName("span")[0];if(span){if(span.textContent!=null){span.textContent=this.options.dictFallbackMessage;}else if(span.innerText!=null){span.innerText=this.options.dictFallbackMessage;}} +return this.element.appendChild(this.getFallbackForm());},resize:function resize(file,width,height,resizeMethod){var info={srcX:0,srcY:0,srcWidth:file.width,srcHeight:file.height};var srcRatio=file.width/file.height;if(width==null&&height==null){width=info.srcWidth;height=info.srcHeight;}else if(width==null){width=height*srcRatio;}else if(height==null){height=width/srcRatio;} +width=Math.min(width,info.srcWidth);height=Math.min(height,info.srcHeight);var trgRatio=width/height;if(info.srcWidth>width||info.srcHeight>height){if(resizeMethod==='crop'){if(srcRatio>trgRatio){info.srcHeight=file.height;info.srcWidth=info.srcHeight*trgRatio;}else{info.srcWidth=file.width;info.srcHeight=info.srcWidth/trgRatio;}}else if(resizeMethod==='contain'){if(srcRatio>trgRatio){height=width/srcRatio;}else{width=height*srcRatio;}}else{throw new Error("Unknown resizeMethod '"+resizeMethod+"'");}} +info.srcX=(file.width-info.srcWidth)/2;info.srcY=(file.height-info.srcHeight)/2;info.trgWidth=width;info.trgHeight=height;return info;},transformFile:function transformFile(file,done){if((this.options.resizeWidth||this.options.resizeHeight)&&file.type.match(/image.*/)){return this.resizeImage(file,this.options.resizeWidth,this.options.resizeHeight,this.options.resizeMethod,done);}else{return done(file);}},previewTemplate:"
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
",drop:function drop(e){return this.element.classList.remove("dz-drag-hover");},dragstart:function dragstart(e){},dragend:function dragend(e){return this.element.classList.remove("dz-drag-hover");},dragenter:function dragenter(e){return this.element.classList.add("dz-drag-hover");},dragover:function dragover(e){return this.element.classList.add("dz-drag-hover");},dragleave:function dragleave(e){return this.element.classList.remove("dz-drag-hover");},paste:function paste(e){},reset:function reset(){return this.element.classList.remove("dz-started");},addedfile:function addedfile(file){var _this2=this;if(this.element===this.previewsContainer){this.element.classList.add("dz-started");} +if(this.previewsContainer){file.previewElement=Dropzone.createElement(this.options.previewTemplate.trim());file.previewTemplate=file.previewElement;this.previewsContainer.appendChild(file.previewElement);for(var _iterator3=file.previewElement.querySelectorAll("[data-dz-name]"),_isArray3=true,_i3=0,_iterator3=_isArray3?_iterator3:_iterator3[Symbol.iterator]();;){var _ref3;if(_isArray3){if(_i3>=_iterator3.length)break;_ref3=_iterator3[_i3++];}else{_i3=_iterator3.next();if(_i3.done)break;_ref3=_i3.value;} +var node=_ref3;node.textContent=file.name;} +for(var _iterator4=file.previewElement.querySelectorAll("[data-dz-size]"),_isArray4=true,_i4=0,_iterator4=_isArray4?_iterator4:_iterator4[Symbol.iterator]();;){if(_isArray4){if(_i4>=_iterator4.length)break;node=_iterator4[_i4++];}else{_i4=_iterator4.next();if(_i4.done)break;node=_i4.value;} +node.innerHTML=this.filesize(file.size);} if(this.options.addRemoveLinks){file._removeLink=Dropzone.createElement(""+this.options.dictRemoveFile+"");file.previewElement.appendChild(file._removeLink);} -removeFileEvent=(function(_this){return function(e){e.preventDefault();e.stopPropagation();if(file.status===Dropzone.UPLOADING){return Dropzone.confirm(_this.options.dictCancelUploadConfirmation,function(){return _this.removeFile(file);});}else{if(_this.options.dictRemoveFileConfirmation){return Dropzone.confirm(_this.options.dictRemoveFileConfirmation,function(){return _this.removeFile(file);});}else{return _this.removeFile(file);}}};})(this);_ref2=file.previewElement.querySelectorAll("[data-dz-remove]");_results=[];for(_k=0,_len2=_ref2.length;_k<_len2;_k++){removeLink=_ref2[_k];_results.push(removeLink.addEventListener("click",removeFileEvent));} -return _results;}},removedfile:function(file){var _ref;if(file.previewElement){if((_ref=file.previewElement)!=null){_ref.parentNode.removeChild(file.previewElement);}} -return this._updateMaxFilesReachedClass();},thumbnail:function(file,dataUrl){var thumbnailElement,_i,_len,_ref;if(file.previewElement){file.previewElement.classList.remove("dz-file-preview");_ref=file.previewElement.querySelectorAll("[data-dz-thumbnail]");for(_i=0,_len=_ref.length;_i<_len;_i++){thumbnailElement=_ref[_i];thumbnailElement.alt=file.name;thumbnailElement.src=dataUrl;} -return setTimeout(((function(_this){return function(){return file.previewElement.classList.add("dz-image-preview");};})(this)),1);}},error:function(file,message){var node,_i,_len,_ref,_results;if(file.previewElement){file.previewElement.classList.add("dz-error");if(typeof message!=="String"&&message.error){message=message.error;} -_ref=file.previewElement.querySelectorAll("[data-dz-errormessage]");_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){node=_ref[_i];_results.push(node.textContent=message);} -return _results;}},errormultiple:noop,processing:function(file){if(file.previewElement){file.previewElement.classList.add("dz-processing");if(file._removeLink){return file._removeLink.textContent=this.options.dictCancelUpload;}}},processingmultiple:noop,uploadprogress:function(file,progress,bytesSent){var node,_i,_len,_ref,_results;if(file.previewElement){_ref=file.previewElement.querySelectorAll("[data-dz-uploadprogress]");_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){node=_ref[_i];if(node.nodeName==='PROGRESS'){_results.push(node.value=progress);}else{_results.push(node.style.width=""+progress+"%");}} -return _results;}},totaluploadprogress:noop,sending:noop,sendingmultiple:noop,success:function(file){if(file.previewElement){return file.previewElement.classList.add("dz-success");}},successmultiple:noop,canceled:function(file){return this.emit("error",file,"Upload canceled.");},canceledmultiple:noop,complete:function(file){if(file._removeLink){file._removeLink.textContent=this.options.dictRemoveFile;} -if(file.previewElement){return file.previewElement.classList.add("dz-complete");}},completemultiple:noop,maxfilesexceeded:noop,maxfilesreached:noop,queuecomplete:noop,previewTemplate:"
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
"};extend=function(){var key,object,objects,target,val,_i,_len;target=arguments[0],objects=2<=arguments.length?__slice.call(arguments,1):[];for(_i=0,_len=objects.length;_i<_len;_i++){object=objects[_i];for(key in object){val=object[key];target[key]=val;}} -return target;};function Dropzone(element,options){var elementOptions,fallback,_ref;this.element=element;this.version=Dropzone.version;this.defaultOptions.previewTemplate=this.defaultOptions.previewTemplate.replace(/\n*/g,"");this.clickableElements=[];this.listeners=[];this.files=[];if(typeof this.element==="string"){this.element=document.querySelector(this.element);} -if(!(this.element&&(this.element.nodeType!=null))){throw new Error("Invalid dropzone element.");} -if(this.element.dropzone){throw new Error("Dropzone already attached.");} -Dropzone.instances.push(this);this.element.dropzone=this;elementOptions=(_ref=Dropzone.optionsForElement(this.element))!=null?_ref:{};this.options=extend({},this.defaultOptions,elementOptions,options!=null?options:{});if(this.options.forceFallback||!Dropzone.isBrowserSupported()){return this.options.fallback.call(this);} -if(this.options.url==null){this.options.url=this.element.getAttribute("action");} -if(!this.options.url){throw new Error("No URL provided.");} -if(this.options.acceptedFiles&&this.options.acceptedMimeTypes){throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");} -if(this.options.acceptedMimeTypes){this.options.acceptedFiles=this.options.acceptedMimeTypes;delete this.options.acceptedMimeTypes;} -this.options.method=this.options.method.toUpperCase();if((fallback=this.getExistingFallback())&&fallback.parentNode){fallback.parentNode.removeChild(fallback);} -if(this.options.previewsContainer!==false){if(this.options.previewsContainer){this.previewsContainer=Dropzone.getElement(this.options.previewsContainer,"previewsContainer");}else{this.previewsContainer=this.element;}} -if(this.options.clickable){if(this.options.clickable===true){this.clickableElements=[this.element];}else{this.clickableElements=Dropzone.getElements(this.options.clickable,"clickable");}} -this.init();} -Dropzone.prototype.getAcceptedFiles=function(){var file,_i,_len,_ref,_results;_ref=this.files;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];if(file.accepted){_results.push(file);}} -return _results;};Dropzone.prototype.getRejectedFiles=function(){var file,_i,_len,_ref,_results;_ref=this.files;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];if(!file.accepted){_results.push(file);}} -return _results;};Dropzone.prototype.getFilesWithStatus=function(status){var file,_i,_len,_ref,_results;_ref=this.files;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];if(file.status===status){_results.push(file);}} -return _results;};Dropzone.prototype.getQueuedFiles=function(){return this.getFilesWithStatus(Dropzone.QUEUED);};Dropzone.prototype.getUploadingFiles=function(){return this.getFilesWithStatus(Dropzone.UPLOADING);};Dropzone.prototype.getActiveFiles=function(){var file,_i,_len,_ref,_results;_ref=this.files;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];if(file.status===Dropzone.UPLOADING||file.status===Dropzone.QUEUED){_results.push(file);}} -return _results;};Dropzone.prototype.init=function(){var eventName,noPropagation,setupHiddenFileInput,_i,_len,_ref,_ref1;if(this.element.tagName==="form"){this.element.setAttribute("enctype","multipart/form-data");} +var removeFileEvent=function removeFileEvent(e){e.preventDefault();e.stopPropagation();if(file.status===Dropzone.UPLOADING){return Dropzone.confirm(_this2.options.dictCancelUploadConfirmation,function(){return _this2.removeFile(file);});}else{if(_this2.options.dictRemoveFileConfirmation){return Dropzone.confirm(_this2.options.dictRemoveFileConfirmation,function(){return _this2.removeFile(file);});}else{return _this2.removeFile(file);}}};for(var _iterator5=file.previewElement.querySelectorAll("[data-dz-remove]"),_isArray5=true,_i5=0,_iterator5=_isArray5?_iterator5:_iterator5[Symbol.iterator]();;){var _ref4;if(_isArray5){if(_i5>=_iterator5.length)break;_ref4=_iterator5[_i5++];}else{_i5=_iterator5.next();if(_i5.done)break;_ref4=_i5.value;} +var removeLink=_ref4;removeLink.addEventListener("click",removeFileEvent);}}},removedfile:function removedfile(file){if(file.previewElement!=null&&file.previewElement.parentNode!=null){file.previewElement.parentNode.removeChild(file.previewElement);} +return this._updateMaxFilesReachedClass();},thumbnail:function thumbnail(file,dataUrl){if(file.previewElement){file.previewElement.classList.remove("dz-file-preview");for(var _iterator6=file.previewElement.querySelectorAll("[data-dz-thumbnail]"),_isArray6=true,_i6=0,_iterator6=_isArray6?_iterator6:_iterator6[Symbol.iterator]();;){var _ref5;if(_isArray6){if(_i6>=_iterator6.length)break;_ref5=_iterator6[_i6++];}else{_i6=_iterator6.next();if(_i6.done)break;_ref5=_i6.value;} +var thumbnailElement=_ref5;thumbnailElement.alt=file.name;thumbnailElement.src=dataUrl;} +return setTimeout(function(){return file.previewElement.classList.add("dz-image-preview");},1);}},error:function error(file,message){if(file.previewElement){file.previewElement.classList.add("dz-error");if(typeof message!=="String"&&message.error){message=message.error;} +for(var _iterator7=file.previewElement.querySelectorAll("[data-dz-errormessage]"),_isArray7=true,_i7=0,_iterator7=_isArray7?_iterator7:_iterator7[Symbol.iterator]();;){var _ref6;if(_isArray7){if(_i7>=_iterator7.length)break;_ref6=_iterator7[_i7++];}else{_i7=_iterator7.next();if(_i7.done)break;_ref6=_i7.value;} +var node=_ref6;node.textContent=message;}}},errormultiple:function errormultiple(){},processing:function processing(file){if(file.previewElement){file.previewElement.classList.add("dz-processing");if(file._removeLink){return file._removeLink.innerHTML=this.options.dictCancelUpload;}}},processingmultiple:function processingmultiple(){},uploadprogress:function uploadprogress(file,progress,bytesSent){if(file.previewElement){for(var _iterator8=file.previewElement.querySelectorAll("[data-dz-uploadprogress]"),_isArray8=true,_i8=0,_iterator8=_isArray8?_iterator8:_iterator8[Symbol.iterator]();;){var _ref7;if(_isArray8){if(_i8>=_iterator8.length)break;_ref7=_iterator8[_i8++];}else{_i8=_iterator8.next();if(_i8.done)break;_ref7=_i8.value;} +var node=_ref7;node.nodeName==='PROGRESS'?node.value=progress:node.style.width=progress+"%";}}},totaluploadprogress:function totaluploadprogress(){},sending:function sending(){},sendingmultiple:function sendingmultiple(){},success:function success(file){if(file.previewElement){return file.previewElement.classList.add("dz-success");}},successmultiple:function successmultiple(){},canceled:function canceled(file){return this.emit("error",file,this.options.dictUploadCanceled);},canceledmultiple:function canceledmultiple(){},complete:function complete(file){if(file._removeLink){file._removeLink.innerHTML=this.options.dictRemoveFile;} +if(file.previewElement){return file.previewElement.classList.add("dz-complete");}},completemultiple:function completemultiple(){},maxfilesexceeded:function maxfilesexceeded(){},maxfilesreached:function maxfilesreached(){},queuecomplete:function queuecomplete(){},addedfiles:function addedfiles(){}};this.prototype._thumbnailQueue=[];this.prototype._processingThumbnail=false;}},{key:"extend",value:function extend(target){for(var _len2=arguments.length,objects=Array(_len2>1?_len2-1:0),_key2=1;_key2<_len2;_key2++){objects[_key2-1]=arguments[_key2];} +for(var _iterator9=objects,_isArray9=true,_i9=0,_iterator9=_isArray9?_iterator9:_iterator9[Symbol.iterator]();;){var _ref8;if(_isArray9){if(_i9>=_iterator9.length)break;_ref8=_iterator9[_i9++];}else{_i9=_iterator9.next();if(_i9.done)break;_ref8=_i9.value;} +var object=_ref8;for(var key in object){var val=object[key];target[key]=val;}} +return target;}}]);function Dropzone(el,options){_classCallCheck(this,Dropzone);var _this=_possibleConstructorReturn(this,(Dropzone.__proto__||Object.getPrototypeOf(Dropzone)).call(this));var fallback=void 0,left=void 0;_this.element=el;_this.version=Dropzone.version;_this.defaultOptions.previewTemplate=_this.defaultOptions.previewTemplate.replace(/\n*/g,"");_this.clickableElements=[];_this.listeners=[];_this.files=[];if(typeof _this.element==="string"){_this.element=document.querySelector(_this.element);} +if(!_this.element||_this.element.nodeType==null){throw new Error("Invalid dropzone element.");} +if(_this.element.dropzone){throw new Error("Dropzone already attached.");} +Dropzone.instances.push(_this);_this.element.dropzone=_this;var elementOptions=(left=Dropzone.optionsForElement(_this.element))!=null?left:{};_this.options=Dropzone.extend({},_this.defaultOptions,elementOptions,options!=null?options:{});if(_this.options.forceFallback||!Dropzone.isBrowserSupported()){var _ret;return _ret=_this.options.fallback.call(_this),_possibleConstructorReturn(_this,_ret);} +if(_this.options.url==null){_this.options.url=_this.element.getAttribute("action");} +if(!_this.options.url){throw new Error("No URL provided.");} +if(_this.options.acceptedFiles&&_this.options.acceptedMimeTypes){throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated.");} +if(_this.options.uploadMultiple&&_this.options.chunking){throw new Error('You cannot set both: uploadMultiple and chunking.');} +if(_this.options.acceptedMimeTypes){_this.options.acceptedFiles=_this.options.acceptedMimeTypes;delete _this.options.acceptedMimeTypes;} +if(_this.options.renameFilename!=null){_this.options.renameFile=function(file){return _this.options.renameFilename.call(_this,file.name,file);};} +_this.options.method=_this.options.method.toUpperCase();if((fallback=_this.getExistingFallback())&&fallback.parentNode){fallback.parentNode.removeChild(fallback);} +if(_this.options.previewsContainer!==false){if(_this.options.previewsContainer){_this.previewsContainer=Dropzone.getElement(_this.options.previewsContainer,"previewsContainer");}else{_this.previewsContainer=_this.element;}} +if(_this.options.clickable){if(_this.options.clickable===true){_this.clickableElements=[_this.element];}else{_this.clickableElements=Dropzone.getElements(_this.options.clickable,"clickable");}} +_this.init();return _this;} +_createClass(Dropzone,[{key:"getAcceptedFiles",value:function getAcceptedFiles(){return this.files.filter(function(file){return file.accepted;}).map(function(file){return file;});}},{key:"getRejectedFiles",value:function getRejectedFiles(){return this.files.filter(function(file){return!file.accepted;}).map(function(file){return file;});}},{key:"getFilesWithStatus",value:function getFilesWithStatus(status){return this.files.filter(function(file){return file.status===status;}).map(function(file){return file;});}},{key:"getQueuedFiles",value:function getQueuedFiles(){return this.getFilesWithStatus(Dropzone.QUEUED);}},{key:"getUploadingFiles",value:function getUploadingFiles(){return this.getFilesWithStatus(Dropzone.UPLOADING);}},{key:"getAddedFiles",value:function getAddedFiles(){return this.getFilesWithStatus(Dropzone.ADDED);}},{key:"getActiveFiles",value:function getActiveFiles(){return this.files.filter(function(file){return file.status===Dropzone.UPLOADING||file.status===Dropzone.QUEUED;}).map(function(file){return file;});}},{key:"init",value:function init(){var _this3=this;if(this.element.tagName==="form"){this.element.setAttribute("enctype","multipart/form-data");} if(this.element.classList.contains("dropzone")&&!this.element.querySelector(".dz-message")){this.element.appendChild(Dropzone.createElement("
"+this.options.dictDefaultMessage+"
"));} -if(this.clickableElements.length){setupHiddenFileInput=(function(_this){return function(){if(_this.hiddenFileInput){document.body.removeChild(_this.hiddenFileInput);} -_this.hiddenFileInput=document.createElement("input");_this.hiddenFileInput.setAttribute("type","file");if((_this.options.maxFiles==null)||_this.options.maxFiles>1){_this.hiddenFileInput.setAttribute("multiple","multiple");} -_this.hiddenFileInput.className="dz-hidden-input";if(_this.options.acceptedFiles!=null){_this.hiddenFileInput.setAttribute("accept",_this.options.acceptedFiles);} -if(_this.options.capture!=null){_this.hiddenFileInput.setAttribute("capture",_this.options.capture);} -_this.hiddenFileInput.style.visibility="hidden";_this.hiddenFileInput.style.position="absolute";_this.hiddenFileInput.style.top="0";_this.hiddenFileInput.style.left="0";_this.hiddenFileInput.style.height="0";_this.hiddenFileInput.style.width="0";document.body.appendChild(_this.hiddenFileInput);return _this.hiddenFileInput.addEventListener("change",function(){var file,files,_i,_len;files=_this.hiddenFileInput.files;if(files.length){for(_i=0,_len=files.length;_i<_len;_i++){file=files[_i];_this.addFile(file);}} -return setupHiddenFileInput();});};})(this);setupHiddenFileInput();} -this.URL=(_ref=window.URL)!=null?_ref:window.webkitURL;_ref1=this.events;for(_i=0,_len=_ref1.length;_i<_len;_i++){eventName=_ref1[_i];this.on(eventName,this.options[eventName]);} -this.on("uploadprogress",(function(_this){return function(){return _this.updateTotalUploadProgress();};})(this));this.on("removedfile",(function(_this){return function(){return _this.updateTotalUploadProgress();};})(this));this.on("canceled",(function(_this){return function(file){return _this.emit("complete",file);};})(this));this.on("complete",(function(_this){return function(file){if(_this.getUploadingFiles().length===0&&_this.getQueuedFiles().length===0){return setTimeout((function(){return _this.emit("queuecomplete");}),0);}};})(this));noPropagation=function(e){e.stopPropagation();if(e.preventDefault){return e.preventDefault();}else{return e.returnValue=false;}};this.listeners=[{element:this.element,events:{"dragstart":(function(_this){return function(e){return _this.emit("dragstart",e);};})(this),"dragenter":(function(_this){return function(e){noPropagation(e);return _this.emit("dragenter",e);};})(this),"dragover":(function(_this){return function(e){var efct;try{efct=e.dataTransfer.effectAllowed;}catch(_error){} -e.dataTransfer.dropEffect='move'===efct||'linkMove'===efct?'move':'copy';noPropagation(e);return _this.emit("dragover",e);};})(this),"dragleave":(function(_this){return function(e){return _this.emit("dragleave",e);};})(this),"drop":(function(_this){return function(e){noPropagation(e);return _this.drop(e);};})(this),"dragend":(function(_this){return function(e){return _this.emit("dragend",e);};})(this)}}];this.clickableElements.forEach((function(_this){return function(clickableElement){return _this.listeners.push({element:clickableElement,events:{"click":function(evt){if((clickableElement!==_this.element)||(evt.target===_this.element||Dropzone.elementInside(evt.target,_this.element.querySelector(".dz-message")))){return _this.hiddenFileInput.click();}}}});};})(this));this.enable();return this.options.init.call(this);};Dropzone.prototype.destroy=function(){var _ref;this.disable();this.removeAllFiles(true);if((_ref=this.hiddenFileInput)!=null?_ref.parentNode:void 0){this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);this.hiddenFileInput=null;} -delete this.element.dropzone;return Dropzone.instances.splice(Dropzone.instances.indexOf(this),1);};Dropzone.prototype.updateTotalUploadProgress=function(){var activeFiles,file,totalBytes,totalBytesSent,totalUploadProgress,_i,_len,_ref;totalBytesSent=0;totalBytes=0;activeFiles=this.getActiveFiles();if(activeFiles.length){_ref=this.getActiveFiles();for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];totalBytesSent+=file.upload.bytesSent;totalBytes+=file.upload.total;} +if(this.clickableElements.length){var setupHiddenFileInput=function setupHiddenFileInput(){if(_this3.hiddenFileInput){_this3.hiddenFileInput.parentNode.removeChild(_this3.hiddenFileInput);} +_this3.hiddenFileInput=document.createElement("input");_this3.hiddenFileInput.setAttribute("type","file");if(_this3.options.maxFiles===null||_this3.options.maxFiles>1){_this3.hiddenFileInput.setAttribute("multiple","multiple");} +_this3.hiddenFileInput.className="dz-hidden-input";if(_this3.options.acceptedFiles!==null){_this3.hiddenFileInput.setAttribute("accept",_this3.options.acceptedFiles);} +if(_this3.options.capture!==null){_this3.hiddenFileInput.setAttribute("capture",_this3.options.capture);} +_this3.hiddenFileInput.style.visibility="hidden";_this3.hiddenFileInput.style.position="absolute";_this3.hiddenFileInput.style.top="0";_this3.hiddenFileInput.style.left="0";_this3.hiddenFileInput.style.height="0";_this3.hiddenFileInput.style.width="0";Dropzone.getElement(_this3.options.hiddenInputContainer,'hiddenInputContainer').appendChild(_this3.hiddenFileInput);return _this3.hiddenFileInput.addEventListener("change",function(){var files=_this3.hiddenFileInput.files;if(files.length){for(var _iterator10=files,_isArray10=true,_i10=0,_iterator10=_isArray10?_iterator10:_iterator10[Symbol.iterator]();;){var _ref9;if(_isArray10){if(_i10>=_iterator10.length)break;_ref9=_iterator10[_i10++];}else{_i10=_iterator10.next();if(_i10.done)break;_ref9=_i10.value;} +var file=_ref9;_this3.addFile(file);}} +_this3.emit("addedfiles",files);return setupHiddenFileInput();});};setupHiddenFileInput();} +this.URL=window.URL!==null?window.URL:window.webkitURL;for(var _iterator11=this.events,_isArray11=true,_i11=0,_iterator11=_isArray11?_iterator11:_iterator11[Symbol.iterator]();;){var _ref10;if(_isArray11){if(_i11>=_iterator11.length)break;_ref10=_iterator11[_i11++];}else{_i11=_iterator11.next();if(_i11.done)break;_ref10=_i11.value;} +var eventName=_ref10;this.on(eventName,this.options[eventName]);} +this.on("uploadprogress",function(){return _this3.updateTotalUploadProgress();});this.on("removedfile",function(){return _this3.updateTotalUploadProgress();});this.on("canceled",function(file){return _this3.emit("complete",file);});this.on("complete",function(file){if(_this3.getAddedFiles().length===0&&_this3.getUploadingFiles().length===0&&_this3.getQueuedFiles().length===0){return setTimeout(function(){return _this3.emit("queuecomplete");},0);}});var noPropagation=function noPropagation(e){e.stopPropagation();if(e.preventDefault){return e.preventDefault();}else{return e.returnValue=false;}};this.listeners=[{element:this.element,events:{"dragstart":function dragstart(e){return _this3.emit("dragstart",e);},"dragenter":function dragenter(e){noPropagation(e);return _this3.emit("dragenter",e);},"dragover":function dragover(e){var efct=void 0;try{efct=e.dataTransfer.effectAllowed;}catch(error){} +e.dataTransfer.dropEffect='move'===efct||'linkMove'===efct?'move':'copy';noPropagation(e);return _this3.emit("dragover",e);},"dragleave":function dragleave(e){return _this3.emit("dragleave",e);},"drop":function drop(e){noPropagation(e);return _this3.drop(e);},"dragend":function dragend(e){return _this3.emit("dragend",e);}}}];this.clickableElements.forEach(function(clickableElement){return _this3.listeners.push({element:clickableElement,events:{"click":function click(evt){if(clickableElement!==_this3.element||evt.target===_this3.element||Dropzone.elementInside(evt.target,_this3.element.querySelector(".dz-message"))){_this3.hiddenFileInput.click();} +return true;}}});});this.enable();return this.options.init.call(this);}},{key:"destroy",value:function destroy(){this.disable();this.removeAllFiles(true);if(this.hiddenFileInput!=null?this.hiddenFileInput.parentNode:undefined){this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput);this.hiddenFileInput=null;} +delete this.element.dropzone;return Dropzone.instances.splice(Dropzone.instances.indexOf(this),1);}},{key:"updateTotalUploadProgress",value:function updateTotalUploadProgress(){var totalUploadProgress=void 0;var totalBytesSent=0;var totalBytes=0;var activeFiles=this.getActiveFiles();if(activeFiles.length){for(var _iterator12=this.getActiveFiles(),_isArray12=true,_i12=0,_iterator12=_isArray12?_iterator12:_iterator12[Symbol.iterator]();;){var _ref11;if(_isArray12){if(_i12>=_iterator12.length)break;_ref11=_iterator12[_i12++];}else{_i12=_iterator12.next();if(_i12.done)break;_ref11=_i12.value;} +var file=_ref11;totalBytesSent+=file.upload.bytesSent;totalBytes+=file.upload.total;} totalUploadProgress=100*totalBytesSent/totalBytes;}else{totalUploadProgress=100;} -return this.emit("totaluploadprogress",totalUploadProgress,totalBytes,totalBytesSent);};Dropzone.prototype._getParamName=function(n){if(typeof this.options.paramName==="function"){return this.options.paramName(n);}else{return""+this.options.paramName+(this.options.uploadMultiple?"["+n+"]":"");}};Dropzone.prototype.getFallbackForm=function(){var existingFallback,fields,fieldsString,form;if(existingFallback=this.getExistingFallback()){return existingFallback;} -fieldsString="
";if(this.options.dictFallbackText){fieldsString+="

"+this.options.dictFallbackText+"

";} -fieldsString+="
";fields=Dropzone.createElement(fieldsString);if(this.element.tagName!=="FORM"){form=Dropzone.createElement("
");form.appendChild(fields);}else{this.element.setAttribute("enctype","multipart/form-data");this.element.setAttribute("method",this.options.method);} -return form!=null?form:fields;};Dropzone.prototype.getExistingFallback=function(){var fallback,getFallback,tagName,_i,_len,_ref;getFallback=function(elements){var el,_i,_len;for(_i=0,_len=elements.length;_i<_len;_i++){el=elements[_i];if(/(^| )fallback($| )/.test(el.className)){return el;}}};_ref=["div","form"];for(_i=0,_len=_ref.length;_i<_len;_i++){tagName=_ref[_i];if(fallback=getFallback(this.element.getElementsByTagName(tagName))){return fallback;}}};Dropzone.prototype.setupEventListeners=function(){var elementListeners,event,listener,_i,_len,_ref,_results;_ref=this.listeners;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){elementListeners=_ref[_i];_results.push((function(){var _ref1,_results1;_ref1=elementListeners.events;_results1=[];for(event in _ref1){listener=_ref1[event];_results1.push(elementListeners.element.addEventListener(event,listener,false));} -return _results1;})());} -return _results;};Dropzone.prototype.removeEventListeners=function(){var elementListeners,event,listener,_i,_len,_ref,_results;_ref=this.listeners;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){elementListeners=_ref[_i];_results.push((function(){var _ref1,_results1;_ref1=elementListeners.events;_results1=[];for(event in _ref1){listener=_ref1[event];_results1.push(elementListeners.element.removeEventListener(event,listener,false));} -return _results1;})());} -return _results;};Dropzone.prototype.disable=function(){var file,_i,_len,_ref,_results;this.clickableElements.forEach(function(element){return element.classList.remove("dz-clickable");});this.removeEventListeners();_ref=this.files;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];_results.push(this.cancelUpload(file));} -return _results;};Dropzone.prototype.enable=function(){this.clickableElements.forEach(function(element){return element.classList.add("dz-clickable");});return this.setupEventListeners();};Dropzone.prototype.filesize=function(size){var cutoff,i,selectedSize,selectedUnit,unit,units,_i,_len;units=['TB','GB','MB','KB','b'];selectedSize=selectedUnit=null;for(i=_i=0,_len=units.length;_i<_len;i=++_i){unit=units[i];cutoff=Math.pow(this.options.filesizeBase,4-i)/10;if(size>=cutoff){selectedSize=size/Math.pow(this.options.filesizeBase,4-i);selectedUnit=unit;break;}} -selectedSize=Math.round(10*selectedSize)/10;return""+selectedSize+" "+selectedUnit;};Dropzone.prototype._updateMaxFilesReachedClass=function(){if((this.options.maxFiles!=null)&&this.getAcceptedFiles().length>=this.options.maxFiles){if(this.getAcceptedFiles().length===this.options.maxFiles){this.emit('maxfilesreached',this.files);} -return this.element.classList.add("dz-max-files-reached");}else{return this.element.classList.remove("dz-max-files-reached");}};Dropzone.prototype.drop=function(e){var files,items;if(!e.dataTransfer){return;} -this.emit("drop",e);files=e.dataTransfer.files;if(files.length){items=e.dataTransfer.items;if(items&&items.length&&(items[0].webkitGetAsEntry!=null)){this._addFilesFromItems(items);}else{this.handleFiles(files);}}};Dropzone.prototype.paste=function(e){var items,_ref;if((e!=null?(_ref=e.clipboardData)!=null?_ref.items:void 0:void 0)==null){return;} -this.emit("paste",e);items=e.clipboardData.items;if(items.length){return this._addFilesFromItems(items);}};Dropzone.prototype.handleFiles=function(files){var file,_i,_len,_results;_results=[];for(_i=0,_len=files.length;_i<_len;_i++){file=files[_i];_results.push(this.addFile(file));} -return _results;};Dropzone.prototype._addFilesFromItems=function(items){var entry,item,_i,_len,_results;_results=[];for(_i=0,_len=items.length;_i<_len;_i++){item=items[_i];if((item.webkitGetAsEntry!=null)&&(entry=item.webkitGetAsEntry())){if(entry.isFile){_results.push(this.addFile(item.getAsFile()));}else if(entry.isDirectory){_results.push(this._addFilesFromDirectory(entry,entry.name));}else{_results.push(void 0);}}else if(item.getAsFile!=null){if((item.kind==null)||item.kind==="file"){_results.push(this.addFile(item.getAsFile()));}else{_results.push(void 0);}}else{_results.push(void 0);}} -return _results;};Dropzone.prototype._addFilesFromDirectory=function(directory,path){var dirReader,entriesReader;dirReader=directory.createReader();entriesReader=(function(_this){return function(entries){var entry,_i,_len;for(_i=0,_len=entries.length;_i<_len;_i++){entry=entries[_i];if(entry.isFile){entry.file(function(file){if(_this.options.ignoreHiddenFiles&&file.name.substring(0,1)==='.'){return;} -file.fullPath=""+path+"/"+file.name;return _this.addFile(file);});}else if(entry.isDirectory){_this._addFilesFromDirectory(entry,""+path+"/"+entry.name);}}};})(this);return dirReader.readEntries(entriesReader,function(error){return typeof console!=="undefined"&&console!==null?typeof console.log==="function"?console.log(error):void 0:void 0;});};Dropzone.prototype.accept=function(file,done){if(file.size>this.options.maxFilesize*1024*1024){return done(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(file.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize));}else if(!Dropzone.isValidFile(file,this.options.acceptedFiles)){return done(this.options.dictInvalidFileType);}else if((this.options.maxFiles!=null)&&this.getAcceptedFiles().length>=this.options.maxFiles){done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles));return this.emit("maxfilesexceeded",file);}else{return this.options.accept.call(this,file,done);}};Dropzone.prototype.addFile=function(file){file.upload={progress:0,total:file.size,bytesSent:0};this.files.push(file);file.status=Dropzone.ADDED;this.emit("addedfile",file);this._enqueueThumbnail(file);return this.accept(file,(function(_this){return function(error){if(error){file.accepted=false;_this._errorProcessing([file],error);}else{file.accepted=true;if(_this.options.autoQueue){_this.enqueueFile(file);}} -return _this._updateMaxFilesReachedClass();};})(this));};Dropzone.prototype.enqueueFiles=function(files){var file,_i,_len;for(_i=0,_len=files.length;_i<_len;_i++){file=files[_i];this.enqueueFile(file);} -return null;};Dropzone.prototype.enqueueFile=function(file){if(file.status===Dropzone.ADDED&&file.accepted===true){file.status=Dropzone.QUEUED;if(this.options.autoProcessQueue){return setTimeout(((function(_this){return function(){return _this.processQueue();};})(this)),0);}}else{throw new Error("This file can't be queued because it has already been processed or was rejected.");}};Dropzone.prototype._thumbnailQueue=[];Dropzone.prototype._processingThumbnail=false;Dropzone.prototype._enqueueThumbnail=function(file){if(this.options.createImageThumbnails&&file.type.match(/image.*/)&&file.size<=this.options.maxThumbnailFilesize*1024*1024){this._thumbnailQueue.push(file);return setTimeout(((function(_this){return function(){return _this._processThumbnailQueue();};})(this)),0);}};Dropzone.prototype._processThumbnailQueue=function(){if(this._processingThumbnail||this._thumbnailQueue.length===0){return;} -this._processingThumbnail=true;return this.createThumbnail(this._thumbnailQueue.shift(),(function(_this){return function(){_this._processingThumbnail=false;return _this._processThumbnailQueue();};})(this));};Dropzone.prototype.removeFile=function(file){if(file.status===Dropzone.UPLOADING){this.cancelUpload(file);} -this.files=without(this.files,file);this.emit("removedfile",file);if(this.files.length===0){return this.emit("reset");}};Dropzone.prototype.removeAllFiles=function(cancelIfNecessary){var file,_i,_len,_ref;if(cancelIfNecessary==null){cancelIfNecessary=false;} -_ref=this.files.slice();for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];if(file.status!==Dropzone.UPLOADING||cancelIfNecessary){this.removeFile(file);}} -return null;};Dropzone.prototype.createThumbnail=function(file,callback){var fileReader;fileReader=new FileReader;fileReader.onload=(function(_this){return function(){if(file.type==="image/svg+xml"){_this.emit("thumbnail",file,fileReader.result);if(callback!=null){callback();} +return this.emit("totaluploadprogress",totalUploadProgress,totalBytes,totalBytesSent);}},{key:"_getParamName",value:function _getParamName(n){if(typeof this.options.paramName==="function"){return this.options.paramName(n);}else{return""+this.options.paramName+(this.options.uploadMultiple?"["+n+"]":"");}}},{key:"_renameFile",value:function _renameFile(file){if(typeof this.options.renameFile!=="function"){return file.name;} +return this.options.renameFile(file);}},{key:"getFallbackForm",value:function getFallbackForm(){var existingFallback=void 0,form=void 0;if(existingFallback=this.getExistingFallback()){return existingFallback;} +var fieldsString="
";if(this.options.dictFallbackText){fieldsString+="

"+this.options.dictFallbackText+"

";} +fieldsString+="
";var fields=Dropzone.createElement(fieldsString);if(this.element.tagName!=="FORM"){form=Dropzone.createElement("
");form.appendChild(fields);}else{this.element.setAttribute("enctype","multipart/form-data");this.element.setAttribute("method",this.options.method);} +return form!=null?form:fields;}},{key:"getExistingFallback",value:function getExistingFallback(){var getFallback=function getFallback(elements){for(var _iterator13=elements,_isArray13=true,_i13=0,_iterator13=_isArray13?_iterator13:_iterator13[Symbol.iterator]();;){var _ref12;if(_isArray13){if(_i13>=_iterator13.length)break;_ref12=_iterator13[_i13++];}else{_i13=_iterator13.next();if(_i13.done)break;_ref12=_i13.value;} +var el=_ref12;if(/(^| )fallback($| )/.test(el.className)){return el;}}};var _arr=["div","form"];for(var _i14=0;_i14<_arr.length;_i14++){var tagName=_arr[_i14];var fallback;if(fallback=getFallback(this.element.getElementsByTagName(tagName))){return fallback;}}}},{key:"setupEventListeners",value:function setupEventListeners(){return this.listeners.map(function(elementListeners){return function(){var result=[];for(var event in elementListeners.events){var listener=elementListeners.events[event];result.push(elementListeners.element.addEventListener(event,listener,false));} +return result;}();});}},{key:"removeEventListeners",value:function removeEventListeners(){return this.listeners.map(function(elementListeners){return function(){var result=[];for(var event in elementListeners.events){var listener=elementListeners.events[event];result.push(elementListeners.element.removeEventListener(event,listener,false));} +return result;}();});}},{key:"disable",value:function disable(){var _this4=this;this.clickableElements.forEach(function(element){return element.classList.remove("dz-clickable");});this.removeEventListeners();this.disabled=true;return this.files.map(function(file){return _this4.cancelUpload(file);});}},{key:"enable",value:function enable(){delete this.disabled;this.clickableElements.forEach(function(element){return element.classList.add("dz-clickable");});return this.setupEventListeners();}},{key:"filesize",value:function filesize(size){var selectedSize=0;var selectedUnit="b";if(size>0){var units=['tb','gb','mb','kb','b'];for(var i=0;i=cutoff){selectedSize=size/Math.pow(this.options.filesizeBase,4-i);selectedUnit=unit;break;}} +selectedSize=Math.round(10*selectedSize)/10;} +return""+selectedSize+" "+this.options.dictFileSizeUnits[selectedUnit];}},{key:"_updateMaxFilesReachedClass",value:function _updateMaxFilesReachedClass(){if(this.options.maxFiles!=null&&this.getAcceptedFiles().length>=this.options.maxFiles){if(this.getAcceptedFiles().length===this.options.maxFiles){this.emit('maxfilesreached',this.files);} +return this.element.classList.add("dz-max-files-reached");}else{return this.element.classList.remove("dz-max-files-reached");}}},{key:"drop",value:function drop(e){if(!e.dataTransfer){return;} +this.emit("drop",e);var files=[];for(var i=0;i=_iterator14.length)break;_ref13=_iterator14[_i15++];}else{_i15=_iterator14.next();if(_i15.done)break;_ref13=_i15.value;} +var file=_ref13;this.addFile(file);}}},{key:"_addFilesFromItems",value:function _addFilesFromItems(items){var _this5=this;return function(){var result=[];for(var _iterator15=items,_isArray15=true,_i16=0,_iterator15=_isArray15?_iterator15:_iterator15[Symbol.iterator]();;){var _ref14;if(_isArray15){if(_i16>=_iterator15.length)break;_ref14=_iterator15[_i16++];}else{_i16=_iterator15.next();if(_i16.done)break;_ref14=_i16.value;} +var item=_ref14;var entry;if(item.webkitGetAsEntry!=null&&(entry=item.webkitGetAsEntry())){if(entry.isFile){result.push(_this5.addFile(item.getAsFile()));}else if(entry.isDirectory){result.push(_this5._addFilesFromDirectory(entry,entry.name));}else{result.push(undefined);}}else if(item.getAsFile!=null){if(item.kind==null||item.kind==="file"){result.push(_this5.addFile(item.getAsFile()));}else{result.push(undefined);}}else{result.push(undefined);}} +return result;}();}},{key:"_addFilesFromDirectory",value:function _addFilesFromDirectory(directory,path){var _this6=this;var dirReader=directory.createReader();var errorHandler=function errorHandler(error){return __guardMethod__(console,'log',function(o){return o.log(error);});};var readEntries=function readEntries(){return dirReader.readEntries(function(entries){if(entries.length>0){for(var _iterator16=entries,_isArray16=true,_i17=0,_iterator16=_isArray16?_iterator16:_iterator16[Symbol.iterator]();;){var _ref15;if(_isArray16){if(_i17>=_iterator16.length)break;_ref15=_iterator16[_i17++];}else{_i17=_iterator16.next();if(_i17.done)break;_ref15=_i17.value;} +var entry=_ref15;if(entry.isFile){entry.file(function(file){if(_this6.options.ignoreHiddenFiles&&file.name.substring(0,1)==='.'){return;} +file.fullPath=path+"/"+file.name;return _this6.addFile(file);});}else if(entry.isDirectory){_this6._addFilesFromDirectory(entry,path+"/"+entry.name);}} +readEntries();} +return null;},errorHandler);};return readEntries();}},{key:"accept",value:function accept(file,done){if(this.options.maxFilesize&&file.size>this.options.maxFilesize*1024*1024){return done(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(file.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize));}else if(!Dropzone.isValidFile(file,this.options.acceptedFiles)){return done(this.options.dictInvalidFileType);}else if(this.options.maxFiles!=null&&this.getAcceptedFiles().length>=this.options.maxFiles){done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles));return this.emit("maxfilesexceeded",file);}else{return this.options.accept.call(this,file,done);}}},{key:"addFile",value:function addFile(file){var _this7=this;file.upload={uuid:Dropzone.uuidv4(),progress:0,total:file.size,bytesSent:0,filename:this._renameFile(file),chunked:this.options.chunking&&(this.options.forceChunking||file.size>this.options.chunkSize),totalChunkCount:Math.ceil(file.size/this.options.chunkSize)};this.files.push(file);file.status=Dropzone.ADDED;this.emit("addedfile",file);this._enqueueThumbnail(file);return this.accept(file,function(error){if(error){file.accepted=false;_this7._errorProcessing([file],error);}else{file.accepted=true;if(_this7.options.autoQueue){_this7.enqueueFile(file);}} +return _this7._updateMaxFilesReachedClass();});}},{key:"enqueueFiles",value:function enqueueFiles(files){for(var _iterator17=files,_isArray17=true,_i18=0,_iterator17=_isArray17?_iterator17:_iterator17[Symbol.iterator]();;){var _ref16;if(_isArray17){if(_i18>=_iterator17.length)break;_ref16=_iterator17[_i18++];}else{_i18=_iterator17.next();if(_i18.done)break;_ref16=_i18.value;} +var file=_ref16;this.enqueueFile(file);} +return null;}},{key:"enqueueFile",value:function enqueueFile(file){var _this8=this;if(file.status===Dropzone.ADDED&&file.accepted===true){file.status=Dropzone.QUEUED;if(this.options.autoProcessQueue){return setTimeout(function(){return _this8.processQueue();},0);}}else{throw new Error("This file can't be queued because it has already been processed or was rejected.");}}},{key:"_enqueueThumbnail",value:function _enqueueThumbnail(file){var _this9=this;if(this.options.createImageThumbnails&&file.type.match(/image.*/)&&file.size<=this.options.maxThumbnailFilesize*1024*1024){this._thumbnailQueue.push(file);return setTimeout(function(){return _this9._processThumbnailQueue();},0);}}},{key:"_processThumbnailQueue",value:function _processThumbnailQueue(){var _this10=this;if(this._processingThumbnail||this._thumbnailQueue.length===0){return;} +this._processingThumbnail=true;var file=this._thumbnailQueue.shift();return this.createThumbnail(file,this.options.thumbnailWidth,this.options.thumbnailHeight,this.options.thumbnailMethod,true,function(dataUrl){_this10.emit("thumbnail",file,dataUrl);_this10._processingThumbnail=false;return _this10._processThumbnailQueue();});}},{key:"removeFile",value:function removeFile(file){if(file.status===Dropzone.UPLOADING){this.cancelUpload(file);} +this.files=without(this.files,file);this.emit("removedfile",file);if(this.files.length===0){return this.emit("reset");}}},{key:"removeAllFiles",value:function removeAllFiles(cancelIfNecessary){if(cancelIfNecessary==null){cancelIfNecessary=false;} +for(var _iterator18=this.files.slice(),_isArray18=true,_i19=0,_iterator18=_isArray18?_iterator18:_iterator18[Symbol.iterator]();;){var _ref17;if(_isArray18){if(_i19>=_iterator18.length)break;_ref17=_iterator18[_i19++];}else{_i19=_iterator18.next();if(_i19.done)break;_ref17=_i19.value;} +var file=_ref17;if(file.status!==Dropzone.UPLOADING||cancelIfNecessary){this.removeFile(file);}} +return null;}},{key:"resizeImage",value:function resizeImage(file,width,height,resizeMethod,callback){var _this11=this;return this.createThumbnail(file,width,height,resizeMethod,true,function(dataUrl,canvas){if(canvas==null){return callback(file);}else{var resizeMimeType=_this11.options.resizeMimeType;if(resizeMimeType==null){resizeMimeType=file.type;} +var resizedDataURL=canvas.toDataURL(resizeMimeType,_this11.options.resizeQuality);if(resizeMimeType==='image/jpeg'||resizeMimeType==='image/jpg'){resizedDataURL=ExifRestore.restore(file.dataURL,resizedDataURL);} +return callback(Dropzone.dataURItoBlob(resizedDataURL));}});}},{key:"createThumbnail",value:function createThumbnail(file,width,height,resizeMethod,fixOrientation,callback){var _this12=this;var fileReader=new FileReader();fileReader.onload=function(){file.dataURL=fileReader.result;if(file.type==="image/svg+xml"){if(callback!=null){callback(fileReader.result);} return;} -return _this.createThumbnailFromUrl(file,fileReader.result,callback);};})(this);return fileReader.readAsDataURL(file);};Dropzone.prototype.createThumbnailFromUrl=function(file,imageUrl,callback){var img;img=document.createElement("img");img.onload=(function(_this){return function(){var canvas,ctx,resizeInfo,thumbnail,_ref,_ref1,_ref2,_ref3;file.width=img.width;file.height=img.height;resizeInfo=_this.options.resize.call(_this,file);if(resizeInfo.trgWidth==null){resizeInfo.trgWidth=resizeInfo.optWidth;} -if(resizeInfo.trgHeight==null){resizeInfo.trgHeight=resizeInfo.optHeight;} -canvas=document.createElement("canvas");ctx=canvas.getContext("2d");canvas.width=resizeInfo.trgWidth;canvas.height=resizeInfo.trgHeight;drawImageIOSFix(ctx,img,(_ref=resizeInfo.srcX)!=null?_ref:0,(_ref1=resizeInfo.srcY)!=null?_ref1:0,resizeInfo.srcWidth,resizeInfo.srcHeight,(_ref2=resizeInfo.trgX)!=null?_ref2:0,(_ref3=resizeInfo.trgY)!=null?_ref3:0,resizeInfo.trgWidth,resizeInfo.trgHeight);thumbnail=canvas.toDataURL("image/png");_this.emit("thumbnail",file,thumbnail);if(callback!=null){return callback();}};})(this);if(callback!=null){img.onerror=callback;} -return img.src=imageUrl;};Dropzone.prototype.processQueue=function(){var i,parallelUploads,processingLength,queuedFiles;parallelUploads=this.options.parallelUploads;processingLength=this.getUploadingFiles().length;i=processingLength;if(processingLength>=parallelUploads){return;} -queuedFiles=this.getQueuedFiles();if(!(queuedFiles.length>0)){return;} +return _this12.createThumbnailFromUrl(file,width,height,resizeMethod,fixOrientation,callback);};return fileReader.readAsDataURL(file);}},{key:"createThumbnailFromUrl",value:function createThumbnailFromUrl(file,width,height,resizeMethod,fixOrientation,callback,crossOrigin){var _this13=this;var img=document.createElement("img");if(crossOrigin){img.crossOrigin=crossOrigin;} +img.onload=function(){var loadExif=function loadExif(callback){return callback(1);};if(typeof EXIF!=='undefined'&&EXIF!==null&&fixOrientation){loadExif=function loadExif(callback){return EXIF.getData(img,function(){return callback(EXIF.getTag(this,'Orientation'));});};} +return loadExif(function(orientation){file.width=img.width;file.height=img.height;var resizeInfo=_this13.options.resize.call(_this13,file,width,height,resizeMethod);var canvas=document.createElement("canvas");var ctx=canvas.getContext("2d");canvas.width=resizeInfo.trgWidth;canvas.height=resizeInfo.trgHeight;if(orientation>4){canvas.width=resizeInfo.trgHeight;canvas.height=resizeInfo.trgWidth;} +switch(orientation){case 2:ctx.translate(canvas.width,0);ctx.scale(-1,1);break;case 3:ctx.translate(canvas.width,canvas.height);ctx.rotate(Math.PI);break;case 4:ctx.translate(0,canvas.height);ctx.scale(1,-1);break;case 5:ctx.rotate(0.5*Math.PI);ctx.scale(1,-1);break;case 6:ctx.rotate(0.5*Math.PI);ctx.translate(0,-canvas.width);break;case 7:ctx.rotate(0.5*Math.PI);ctx.translate(canvas.height,-canvas.width);ctx.scale(-1,1);break;case 8:ctx.rotate(-0.5*Math.PI);ctx.translate(-canvas.height,0);break;} +drawImageIOSFix(ctx,img,resizeInfo.srcX!=null?resizeInfo.srcX:0,resizeInfo.srcY!=null?resizeInfo.srcY:0,resizeInfo.srcWidth,resizeInfo.srcHeight,resizeInfo.trgX!=null?resizeInfo.trgX:0,resizeInfo.trgY!=null?resizeInfo.trgY:0,resizeInfo.trgWidth,resizeInfo.trgHeight);var thumbnail=canvas.toDataURL("image/png");if(callback!=null){return callback(thumbnail,canvas);}});};if(callback!=null){img.onerror=callback;} +return img.src=file.dataURL;}},{key:"processQueue",value:function processQueue(){var parallelUploads=this.options.parallelUploads;var processingLength=this.getUploadingFiles().length;var i=processingLength;if(processingLength>=parallelUploads){return;} +var queuedFiles=this.getQueuedFiles();if(!(queuedFiles.length>0)){return;} if(this.options.uploadMultiple){return this.processFiles(queuedFiles.slice(0,parallelUploads-processingLength));}else{while(i=_iterator19.length)break;_ref18=_iterator19[_i20++];}else{_i20=_iterator19.next();if(_i20.done)break;_ref18=_i20.value;} +var file=_ref18;file.processing=true;file.status=Dropzone.UPLOADING;this.emit("processing",file);} if(this.options.uploadMultiple){this.emit("processingmultiple",files);} -return this.uploadFiles(files);};Dropzone.prototype._getFilesWithXhr=function(xhr){var file,files;return files=(function(){var _i,_len,_ref,_results;_ref=this.files;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){file=_ref[_i];if(file.xhr===xhr){_results.push(file);}} -return _results;}).call(this);};Dropzone.prototype.cancelUpload=function(file){var groupedFile,groupedFiles,_i,_j,_len,_len1,_ref;if(file.status===Dropzone.UPLOADING){groupedFiles=this._getFilesWithXhr(file.xhr);for(_i=0,_len=groupedFiles.length;_i<_len;_i++){groupedFile=groupedFiles[_i];groupedFile.status=Dropzone.CANCELED;} -file.xhr.abort();for(_j=0,_len1=groupedFiles.length;_j<_len1;_j++){groupedFile=groupedFiles[_j];this.emit("canceled",groupedFile);} -if(this.options.uploadMultiple){this.emit("canceledmultiple",groupedFiles);}}else if((_ref=file.status)===Dropzone.ADDED||_ref===Dropzone.QUEUED){file.status=Dropzone.CANCELED;this.emit("canceled",file);if(this.options.uploadMultiple){this.emit("canceledmultiple",[file]);}} -if(this.options.autoProcessQueue){return this.processQueue();}};resolveOption=function(){var args,option;option=arguments[0],args=2<=arguments.length?__slice.call(arguments,1):[];if(typeof option==='function'){return option.apply(this,args);} -return option;};Dropzone.prototype.uploadFile=function(file){return this.uploadFiles([file]);};Dropzone.prototype.uploadFiles=function(files){var file,formData,handleError,headerName,headerValue,headers,i,input,inputName,inputType,key,method,option,progressObj,response,updateProgress,url,value,xhr,_i,_j,_k,_l,_len,_len1,_len2,_len3,_m,_ref,_ref1,_ref2,_ref3,_ref4,_ref5;xhr=new XMLHttpRequest();for(_i=0,_len=files.length;_i<_len;_i++){file=files[_i];file.xhr=xhr;} -method=resolveOption(this.options.method,files);url=resolveOption(this.options.url,files);xhr.open(method,url,true);xhr.withCredentials=!!this.options.withCredentials;response=null;handleError=(function(_this){return function(){var _j,_len1,_results;_results=[];for(_j=0,_len1=files.length;_j<_len1;_j++){file=files[_j];_results.push(_this._errorProcessing(files,response||_this.options.dictResponseError.replace("{{statusCode}}",xhr.status),xhr));} -return _results;};})(this);updateProgress=(function(_this){return function(e){var allFilesFinished,progress,_j,_k,_l,_len1,_len2,_len3,_results;if(e!=null){progress=100*e.loaded/e.total;for(_j=0,_len1=files.length;_j<_len1;_j++){file=files[_j];file.upload={progress:progress,total:e.total,bytesSent:e.loaded};}}else{allFilesFinished=true;progress=100;for(_k=0,_len2=files.length;_k<_len2;_k++){file=files[_k];if(!(file.upload.progress===100&&file.upload.bytesSent===file.upload.total)){allFilesFinished=false;} -file.upload.progress=progress;file.upload.bytesSent=file.upload.total;} -if(allFilesFinished){return;}} -_results=[];for(_l=0,_len3=files.length;_l<_len3;_l++){file=files[_l];_results.push(_this.emit("uploadprogress",file,progress,file.upload.bytesSent));} -return _results;};})(this);xhr.onload=(function(_this){return function(e){var _ref;if(files[0].status===Dropzone.CANCELED){return;} -if(xhr.readyState!==4){return;} -response=xhr.responseText;if(xhr.getResponseHeader("content-type")&&~xhr.getResponseHeader("content-type").indexOf("application/json")){try{response=JSON.parse(response);}catch(_error){e=_error;response="Invalid JSON response from server.";}} -updateProgress();if(!((200<=(_ref=xhr.status)&&_ref<300))){return handleError();}else{return _this._finished(files,response,e);}};})(this);xhr.onerror=(function(_this){return function(){if(files[0].status===Dropzone.CANCELED){return;} -return handleError();};})(this);progressObj=(_ref=xhr.upload)!=null?_ref:xhr;progressObj.onprogress=updateProgress;headers={"Accept":"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"};if(this.options.headers){extend(headers,this.options.headers);} -for(headerName in headers){headerValue=headers[headerName];xhr.setRequestHeader(headerName,headerValue);} -formData=new FormData();if(this.options.params){_ref1=this.options.params;for(key in _ref1){value=_ref1[key];formData.append(key,value);}} -for(_j=0,_len1=files.length;_j<_len1;_j++){file=files[_j];this.emit("sending",file,xhr,formData);} +return this.uploadFiles(files);}},{key:"_getFilesWithXhr",value:function _getFilesWithXhr(xhr){var files=void 0;return files=this.files.filter(function(file){return file.xhr===xhr;}).map(function(file){return file;});}},{key:"cancelUpload",value:function cancelUpload(file){if(file.status===Dropzone.UPLOADING){var groupedFiles=this._getFilesWithXhr(file.xhr);for(var _iterator20=groupedFiles,_isArray20=true,_i21=0,_iterator20=_isArray20?_iterator20:_iterator20[Symbol.iterator]();;){var _ref19;if(_isArray20){if(_i21>=_iterator20.length)break;_ref19=_iterator20[_i21++];}else{_i21=_iterator20.next();if(_i21.done)break;_ref19=_i21.value;} +var groupedFile=_ref19;groupedFile.status=Dropzone.CANCELED;} +if(typeof file.xhr!=='undefined'){file.xhr.abort();} +for(var _iterator21=groupedFiles,_isArray21=true,_i22=0,_iterator21=_isArray21?_iterator21:_iterator21[Symbol.iterator]();;){var _ref20;if(_isArray21){if(_i22>=_iterator21.length)break;_ref20=_iterator21[_i22++];}else{_i22=_iterator21.next();if(_i22.done)break;_ref20=_i22.value;} +var _groupedFile=_ref20;this.emit("canceled",_groupedFile);} +if(this.options.uploadMultiple){this.emit("canceledmultiple",groupedFiles);}}else if(file.status===Dropzone.ADDED||file.status===Dropzone.QUEUED){file.status=Dropzone.CANCELED;this.emit("canceled",file);if(this.options.uploadMultiple){this.emit("canceledmultiple",[file]);}} +if(this.options.autoProcessQueue){return this.processQueue();}}},{key:"resolveOption",value:function resolveOption(option){if(typeof option==='function'){for(var _len3=arguments.length,args=Array(_len3>1?_len3-1:0),_key3=1;_key3<_len3;_key3++){args[_key3-1]=arguments[_key3];} +return option.apply(this,args);} +return option;}},{key:"uploadFile",value:function uploadFile(file){return this.uploadFiles([file]);}},{key:"uploadFiles",value:function uploadFiles(files){var _this14=this;this._transformFiles(files,function(transformedFiles){if(files[0].upload.chunked){var file=files[0];var transformedFile=transformedFiles[0];var startedChunkCount=0;file.upload.chunks=[];var handleNextChunk=function handleNextChunk(){var chunkIndex=0;while(file.upload.chunks[chunkIndex]!==undefined){chunkIndex++;} +if(chunkIndex>=file.upload.totalChunkCount)return;startedChunkCount++;var start=chunkIndex*_this14.options.chunkSize;var end=Math.min(start+_this14.options.chunkSize,file.size);var dataBlock={name:_this14._getParamName(0),data:transformedFile.webkitSlice?transformedFile.webkitSlice(start,end):transformedFile.slice(start,end),filename:file.upload.filename,chunkIndex:chunkIndex};file.upload.chunks[chunkIndex]={file:file,index:chunkIndex,dataBlock:dataBlock,status:Dropzone.UPLOADING,progress:0,retries:0};_this14._uploadData(files,[dataBlock]);};file.upload.finishedChunkUpload=function(chunk){var allFinished=true;chunk.status=Dropzone.SUCCESS;chunk.dataBlock=null;chunk.xhr=null;for(var i=0;i=_iterator22.length)break;_ref21=_iterator22[_i24++];}else{_i24=_iterator22.next();if(_i24.done)break;_ref21=_i24.value;} +var file=_ref21;file.xhr=xhr;} +if(files[0].upload.chunked){files[0].upload.chunks[dataBlocks[0].chunkIndex].xhr=xhr;} +var method=this.resolveOption(this.options.method,files);var url=this.resolveOption(this.options.url,files);xhr.open(method,url,true);xhr.timeout=this.resolveOption(this.options.timeout,files);xhr.withCredentials=!!this.options.withCredentials;xhr.onload=function(e){_this15._finishedUploading(files,xhr,e);};xhr.onerror=function(){_this15._handleUploadError(files,xhr);};var progressObj=xhr.upload!=null?xhr.upload:xhr;progressObj.onprogress=function(e){return _this15._updateFilesUploadProgress(files,xhr,e);};var headers={"Accept":"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"};if(this.options.headers){Dropzone.extend(headers,this.options.headers);} +for(var headerName in headers){var headerValue=headers[headerName];if(headerValue){xhr.setRequestHeader(headerName,headerValue);}} +var formData=new FormData();if(this.options.params){var additionalParams=this.options.params;if(typeof additionalParams==='function'){additionalParams=additionalParams.call(this,files,xhr,files[0].upload.chunked?this._getChunk(files[0],xhr):null);} +for(var key in additionalParams){var value=additionalParams[key];formData.append(key,value);}} +for(var _iterator23=files,_isArray23=true,_i25=0,_iterator23=_isArray23?_iterator23:_iterator23[Symbol.iterator]();;){var _ref22;if(_isArray23){if(_i25>=_iterator23.length)break;_ref22=_iterator23[_i25++];}else{_i25=_iterator23.next();if(_i25.done)break;_ref22=_i25.value;} +var _file=_ref22;this.emit("sending",_file,xhr,formData);} if(this.options.uploadMultiple){this.emit("sendingmultiple",files,xhr,formData);} -if(this.element.tagName==="FORM"){_ref2=this.element.querySelectorAll("input, textarea, select, button");for(_k=0,_len2=_ref2.length;_k<_len2;_k++){input=_ref2[_k];inputName=input.getAttribute("name");inputType=input.getAttribute("type");if(input.tagName==="SELECT"&&input.hasAttribute("multiple")){_ref3=input.options;for(_l=0,_len3=_ref3.length;_l<_len3;_l++){option=_ref3[_l];if(option.selected){formData.append(inputName,option.value);}}}else if(!inputType||((_ref4=inputType.toLowerCase())!=="checkbox"&&_ref4!=="radio")||input.checked){formData.append(inputName,input.value);}}} -for(i=_m=0,_ref5=files.length-1;0<=_ref5?_m<=_ref5:_m>=_ref5;i=0<=_ref5?++_m:--_m){formData.append(this._getParamName(i),files[i],files[i].name);} -return xhr.send(formData);};Dropzone.prototype._finished=function(files,responseText,e){var file,_i,_len;for(_i=0,_len=files.length;_i<_len;_i++){file=files[_i];file.status=Dropzone.SUCCESS;this.emit("success",file,responseText,e);this.emit("complete",file);} +this._addFormElementData(formData);for(var i=0;i=_iterator24.length)break;_ref23=_iterator24[_i26++];}else{_i26=_iterator24.next();if(_i26.done)break;_ref23=_i26.value;} +var input=_ref23;var inputName=input.getAttribute("name");var inputType=input.getAttribute("type");if(inputType)inputType=inputType.toLowerCase();if(typeof inputName==='undefined'||inputName===null)continue;if(input.tagName==="SELECT"&&input.hasAttribute("multiple")){for(var _iterator25=input.options,_isArray25=true,_i27=0,_iterator25=_isArray25?_iterator25:_iterator25[Symbol.iterator]();;){var _ref24;if(_isArray25){if(_i27>=_iterator25.length)break;_ref24=_iterator25[_i27++];}else{_i27=_iterator25.next();if(_i27.done)break;_ref24=_i27.value;} +var option=_ref24;if(option.selected){formData.append(inputName,option.value);}}}else if(!inputType||inputType!=="checkbox"&&inputType!=="radio"||input.checked){formData.append(inputName,input.value);}}}}},{key:"_updateFilesUploadProgress",value:function _updateFilesUploadProgress(files,xhr,e){var progress=void 0;if(typeof e!=='undefined'){progress=100*e.loaded/e.total;if(files[0].upload.chunked){var file=files[0];var chunk=this._getChunk(file,xhr);chunk.progress=progress;chunk.total=e.total;chunk.bytesSent=e.loaded;var fileProgress=0,fileTotal=void 0,fileBytesSent=void 0;file.upload.progress=0;file.upload.total=0;file.upload.bytesSent=0;for(var i=0;i=_iterator26.length)break;_ref25=_iterator26[_i28++];}else{_i28=_iterator26.next();if(_i28.done)break;_ref25=_i28.value;} +var _file2=_ref25;_file2.upload.progress=progress;_file2.upload.total=e.total;_file2.upload.bytesSent=e.loaded;}} +for(var _iterator27=files,_isArray27=true,_i29=0,_iterator27=_isArray27?_iterator27:_iterator27[Symbol.iterator]();;){var _ref26;if(_isArray27){if(_i29>=_iterator27.length)break;_ref26=_iterator27[_i29++];}else{_i29=_iterator27.next();if(_i29.done)break;_ref26=_i29.value;} +var _file3=_ref26;this.emit("uploadprogress",_file3,_file3.upload.progress,_file3.upload.bytesSent);}}else{var allFilesFinished=true;progress=100;for(var _iterator28=files,_isArray28=true,_i30=0,_iterator28=_isArray28?_iterator28:_iterator28[Symbol.iterator]();;){var _ref27;if(_isArray28){if(_i30>=_iterator28.length)break;_ref27=_iterator28[_i30++];}else{_i30=_iterator28.next();if(_i30.done)break;_ref27=_i30.value;} +var _file4=_ref27;if(_file4.upload.progress!==100||_file4.upload.bytesSent!==_file4.upload.total){allFilesFinished=false;} +_file4.upload.progress=progress;_file4.upload.bytesSent=_file4.upload.total;} +if(allFilesFinished){return;} +for(var _iterator29=files,_isArray29=true,_i31=0,_iterator29=_isArray29?_iterator29:_iterator29[Symbol.iterator]();;){var _ref28;if(_isArray29){if(_i31>=_iterator29.length)break;_ref28=_iterator29[_i31++];}else{_i31=_iterator29.next();if(_i31.done)break;_ref28=_i31.value;} +var _file5=_ref28;this.emit("uploadprogress",_file5,progress,_file5.upload.bytesSent);}}}},{key:"_finishedUploading",value:function _finishedUploading(files,xhr,e){var response=void 0;if(files[0].status===Dropzone.CANCELED){return;} +if(xhr.readyState!==4){return;} +if(xhr.responseType!=='arraybuffer'&&xhr.responseType!=='blob'){response=xhr.responseText;if(xhr.getResponseHeader("content-type")&&~xhr.getResponseHeader("content-type").indexOf("application/json")){try{response=JSON.parse(response);}catch(error){e=error;response="Invalid JSON response from server.";}}} +this._updateFilesUploadProgress(files);if(!(200<=xhr.status&&xhr.status<300)){this._handleUploadError(files,xhr,response);}else{if(files[0].upload.chunked){files[0].upload.finishedChunkUpload(this._getChunk(files[0],xhr));}else{this._finished(files,response,e);}}}},{key:"_handleUploadError",value:function _handleUploadError(files,xhr,response){if(files[0].status===Dropzone.CANCELED){return;} +if(files[0].upload.chunked&&this.options.retryChunks){var chunk=this._getChunk(files[0],xhr);if(chunk.retries++=_iterator30.length)break;_ref29=_iterator30[_i32++];}else{_i32=_iterator30.next();if(_i32.done)break;_ref29=_i32.value;} +var file=_ref29;this._errorProcessing(files,response||this.options.dictResponseError.replace("{{statusCode}}",xhr.status),xhr);}}},{key:"submitRequest",value:function submitRequest(xhr,formData,files){xhr.send(formData);}},{key:"_finished",value:function _finished(files,responseText,e){for(var _iterator31=files,_isArray31=true,_i33=0,_iterator31=_isArray31?_iterator31:_iterator31[Symbol.iterator]();;){var _ref30;if(_isArray31){if(_i33>=_iterator31.length)break;_ref30=_iterator31[_i33++];}else{_i33=_iterator31.next();if(_i33.done)break;_ref30=_i33.value;} +var file=_ref30;file.status=Dropzone.SUCCESS;this.emit("success",file,responseText,e);this.emit("complete",file);} if(this.options.uploadMultiple){this.emit("successmultiple",files,responseText,e);this.emit("completemultiple",files);} -if(this.options.autoProcessQueue){return this.processQueue();}};Dropzone.prototype._errorProcessing=function(files,message,xhr){var file,_i,_len;for(_i=0,_len=files.length;_i<_len;_i++){file=files[_i];file.status=Dropzone.ERROR;this.emit("error",file,message,xhr);this.emit("complete",file);} +if(this.options.autoProcessQueue){return this.processQueue();}}},{key:"_errorProcessing",value:function _errorProcessing(files,message,xhr){for(var _iterator32=files,_isArray32=true,_i34=0,_iterator32=_isArray32?_iterator32:_iterator32[Symbol.iterator]();;){var _ref31;if(_isArray32){if(_i34>=_iterator32.length)break;_ref31=_iterator32[_i34++];}else{_i34=_iterator32.next();if(_i34.done)break;_ref31=_i34.value;} +var file=_ref31;file.status=Dropzone.ERROR;this.emit("error",file,message,xhr);this.emit("complete",file);} if(this.options.uploadMultiple){this.emit("errormultiple",files,message,xhr);this.emit("completemultiple",files);} -if(this.options.autoProcessQueue){return this.processQueue();}};return Dropzone;})(Emitter);Dropzone.version="4.0.1";Dropzone.options={};Dropzone.optionsForElement=function(element){if(element.getAttribute("id")){return Dropzone.options[camelize(element.getAttribute("id"))];}else{return void 0;}};Dropzone.instances=[];Dropzone.forElement=function(element){if(typeof element==="string"){element=document.querySelector(element);} -if((element!=null?element.dropzone:void 0)==null){throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");} -return element.dropzone;};Dropzone.autoDiscover=true;Dropzone.discover=function(){var checkElements,dropzone,dropzones,_i,_len,_results;if(document.querySelectorAll){dropzones=document.querySelectorAll(".dropzone");}else{dropzones=[];checkElements=function(elements){var el,_i,_len,_results;_results=[];for(_i=0,_len=elements.length;_i<_len;_i++){el=elements[_i];if(/(^| )dropzone($| )/.test(el.className)){_results.push(dropzones.push(el));}else{_results.push(void 0);}} -return _results;};checkElements(document.getElementsByTagName("div"));checkElements(document.getElementsByTagName("form"));} -_results=[];for(_i=0,_len=dropzones.length;_i<_len;_i++){dropzone=dropzones[_i];if(Dropzone.optionsForElement(dropzone)!==false){_results.push(new Dropzone(dropzone));}else{_results.push(void 0);}} -return _results;};Dropzone.blacklistedBrowsers=[/opera.*Macintosh.*version\/12/i];Dropzone.isBrowserSupported=function(){var capableBrowser,regex,_i,_len,_ref;capableBrowser=true;if(window.File&&window.FileReader&&window.FileList&&window.Blob&&window.FormData&&document.querySelector){if(!("classList"in document.createElement("a"))){capableBrowser=false;}else{_ref=Dropzone.blacklistedBrowsers;for(_i=0,_len=_ref.length;_i<_len;_i++){regex=_ref[_i];if(regex.test(navigator.userAgent)){capableBrowser=false;continue;}}}}else{capableBrowser=false;} -return capableBrowser;};without=function(list,rejectedItem){var item,_i,_len,_results;_results=[];for(_i=0,_len=list.length;_i<_len;_i++){item=list[_i];if(item!==rejectedItem){_results.push(item);}} -return _results;};camelize=function(str){return str.replace(/[\-_](\w)/g,function(match){return match.charAt(1).toUpperCase();});};Dropzone.createElement=function(string){var div;div=document.createElement("div");div.innerHTML=string;return div.childNodes[0];};Dropzone.elementInside=function(element,container){if(element===container){return true;} +if(this.options.autoProcessQueue){return this.processQueue();}}}],[{key:"uuidv4",value:function uuidv4(){return'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(c){var r=Math.random()*16|0,v=c==='x'?r:r&0x3|0x8;return v.toString(16);});}}]);return Dropzone;}(Emitter);Dropzone.initClass();Dropzone.version="5.5.1";Dropzone.options={};Dropzone.optionsForElement=function(element){if(element.getAttribute("id")){return Dropzone.options[camelize(element.getAttribute("id"))];}else{return undefined;}};Dropzone.instances=[];Dropzone.forElement=function(element){if(typeof element==="string"){element=document.querySelector(element);} +if((element!=null?element.dropzone:undefined)==null){throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone.");} +return element.dropzone;};Dropzone.autoDiscover=true;Dropzone.discover=function(){var dropzones=void 0;if(document.querySelectorAll){dropzones=document.querySelectorAll(".dropzone");}else{dropzones=[];var checkElements=function checkElements(elements){return function(){var result=[];for(var _iterator33=elements,_isArray33=true,_i35=0,_iterator33=_isArray33?_iterator33:_iterator33[Symbol.iterator]();;){var _ref32;if(_isArray33){if(_i35>=_iterator33.length)break;_ref32=_iterator33[_i35++];}else{_i35=_iterator33.next();if(_i35.done)break;_ref32=_i35.value;} +var el=_ref32;if(/(^| )dropzone($| )/.test(el.className)){result.push(dropzones.push(el));}else{result.push(undefined);}} +return result;}();};checkElements(document.getElementsByTagName("div"));checkElements(document.getElementsByTagName("form"));} +return function(){var result=[];for(var _iterator34=dropzones,_isArray34=true,_i36=0,_iterator34=_isArray34?_iterator34:_iterator34[Symbol.iterator]();;){var _ref33;if(_isArray34){if(_i36>=_iterator34.length)break;_ref33=_iterator34[_i36++];}else{_i36=_iterator34.next();if(_i36.done)break;_ref33=_i36.value;} +var dropzone=_ref33;if(Dropzone.optionsForElement(dropzone)!==false){result.push(new Dropzone(dropzone));}else{result.push(undefined);}} +return result;}();};Dropzone.blacklistedBrowsers=[/opera.*(Macintosh|Windows Phone).*version\/12/i];Dropzone.isBrowserSupported=function(){var capableBrowser=true;if(window.File&&window.FileReader&&window.FileList&&window.Blob&&window.FormData&&document.querySelector){if(!("classList"in document.createElement("a"))){capableBrowser=false;}else{for(var _iterator35=Dropzone.blacklistedBrowsers,_isArray35=true,_i37=0,_iterator35=_isArray35?_iterator35:_iterator35[Symbol.iterator]();;){var _ref34;if(_isArray35){if(_i37>=_iterator35.length)break;_ref34=_iterator35[_i37++];}else{_i37=_iterator35.next();if(_i37.done)break;_ref34=_i37.value;} +var regex=_ref34;if(regex.test(navigator.userAgent)){capableBrowser=false;continue;}}}}else{capableBrowser=false;} +return capableBrowser;};Dropzone.dataURItoBlob=function(dataURI){var byteString=atob(dataURI.split(',')[1]);var mimeString=dataURI.split(',')[0].split(':')[1].split(';')[0];var ab=new ArrayBuffer(byteString.length);var ia=new Uint8Array(ab);for(var i=0,end=byteString.length,asc=0<=end;asc?i<=end:i>=end;asc?i++:i--){ia[i]=byteString.charCodeAt(i);} +return new Blob([ab],{type:mimeString});};var without=function without(list,rejectedItem){return list.filter(function(item){return item!==rejectedItem;}).map(function(item){return item;});};var camelize=function camelize(str){return str.replace(/[\-_](\w)/g,function(match){return match.charAt(1).toUpperCase();});};Dropzone.createElement=function(string){var div=document.createElement("div");div.innerHTML=string;return div.childNodes[0];};Dropzone.elementInside=function(element,container){if(element===container){return true;} while(element=element.parentNode){if(element===container){return true;}} -return false;};Dropzone.getElement=function(el,name){var element;if(typeof el==="string"){element=document.querySelector(el);}else if(el.nodeType!=null){element=el;} +return false;};Dropzone.getElement=function(el,name){var element=void 0;if(typeof el==="string"){element=document.querySelector(el);}else if(el.nodeType!=null){element=el;} if(element==null){throw new Error("Invalid `"+name+"` option provided. Please provide a CSS selector or a plain HTML element.");} -return element;};Dropzone.getElements=function(els,name){var e,el,elements,_i,_j,_len,_len1,_ref;if(els instanceof Array){elements=[];try{for(_i=0,_len=els.length;_i<_len;_i++){el=els[_i];elements.push(this.getElement(el,name));}}catch(_error){e=_error;elements=null;}}else if(typeof els==="string"){elements=[];_ref=document.querySelectorAll(els);for(_j=0,_len1=_ref.length;_j<_len1;_j++){el=_ref[_j];elements.push(el);}}else if(els.nodeType!=null){elements=[els];} -if(!((elements!=null)&&elements.length)){throw new Error("Invalid `"+name+"` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");} -return elements;};Dropzone.confirm=function(question,accepted,rejected){if(window.confirm(question)){return accepted();}else if(rejected!=null){return rejected();}};Dropzone.isValidFile=function(file,acceptedFiles){var baseMimeType,mimeType,validType,_i,_len;if(!acceptedFiles){return true;} -acceptedFiles=acceptedFiles.split(",");mimeType=file.type;baseMimeType=mimeType.replace(/\/.*$/,"");for(_i=0,_len=acceptedFiles.length;_i<_len;_i++){validType=acceptedFiles[_i];validType=validType.trim();if(validType.charAt(0)==="."){if(file.name.toLowerCase().indexOf(validType.toLowerCase(),file.name.length-validType.length)!==-1){return true;}}else if(/\/\*$/.test(validType)){if(baseMimeType===validType.replace(/\/.*$/,"")){return true;}}else{if(mimeType===validType){return true;}}} -return false;};if(typeof jQuery!=="undefined"&&jQuery!==null){jQuery.fn.dropzone=function(options){return this.each(function(){return new Dropzone(this,options);});};} -if(typeof module!=="undefined"&&module!==null){module.exports=Dropzone;}else{window.Dropzone=Dropzone;} -Dropzone.ADDED="added";Dropzone.QUEUED="queued";Dropzone.ACCEPTED=Dropzone.QUEUED;Dropzone.UPLOADING="uploading";Dropzone.PROCESSING=Dropzone.UPLOADING;Dropzone.CANCELED="canceled";Dropzone.ERROR="error";Dropzone.SUCCESS="success";detectVerticalSquash=function(img){var alpha,canvas,ctx,data,ey,ih,iw,py,ratio,sy;iw=img.naturalWidth;ih=img.naturalHeight;canvas=document.createElement("canvas");canvas.width=1;canvas.height=ih;ctx=canvas.getContext("2d");ctx.drawImage(img,0,0);data=ctx.getImageData(0,0,1,ih).data;sy=0;ey=ih;py=ih;while(py>sy){alpha=data[(py-1)*4+3];if(alpha===0){ey=py;}else{sy=py;} -py=(ey+sy)>>1;} -ratio=py/ih;if(ratio===0){return 1;}else{return ratio;}};drawImageIOSFix=function(ctx,img,sx,sy,sw,sh,dx,dy,dw,dh){var vertSquashRatio;vertSquashRatio=detectVerticalSquash(img);return ctx.drawImage(img,sx,sy,sw,sh,dx,dy,dw,dh/vertSquashRatio);};contentLoaded=function(win,fn){var add,doc,done,init,poll,pre,rem,root,top;done=false;top=true;doc=win.document;root=doc.documentElement;add=(doc.addEventListener?"addEventListener":"attachEvent");rem=(doc.addEventListener?"removeEventListener":"detachEvent");pre=(doc.addEventListener?"":"on");init=function(e){if(e.type==="readystatechange"&&doc.readyState!=="complete"){return;} -(e.type==="load"?win:doc)[rem](pre+e.type,init,false);if(!done&&(done=true)){return fn.call(win,e.type||e);}};poll=function(){var e;try{root.doScroll("left");}catch(_error){e=_error;setTimeout(poll,50);return;} -return init("poll");};if(doc.readyState!=="complete"){if(doc.createEventObject&&root.doScroll){try{top=!win.frameElement;}catch(_error){} +return element;};Dropzone.getElements=function(els,name){var el=void 0,elements=void 0;if(els instanceof Array){elements=[];try{for(var _iterator36=els,_isArray36=true,_i38=0,_iterator36=_isArray36?_iterator36:_iterator36[Symbol.iterator]();;){if(_isArray36){if(_i38>=_iterator36.length)break;el=_iterator36[_i38++];}else{_i38=_iterator36.next();if(_i38.done)break;el=_i38.value;} +elements.push(this.getElement(el,name));}}catch(e){elements=null;}}else if(typeof els==="string"){elements=[];for(var _iterator37=document.querySelectorAll(els),_isArray37=true,_i39=0,_iterator37=_isArray37?_iterator37:_iterator37[Symbol.iterator]();;){if(_isArray37){if(_i39>=_iterator37.length)break;el=_iterator37[_i39++];}else{_i39=_iterator37.next();if(_i39.done)break;el=_i39.value;} +elements.push(el);}}else if(els.nodeType!=null){elements=[els];} +if(elements==null||!elements.length){throw new Error("Invalid `"+name+"` option provided. Please provide a CSS selector, a plain HTML element or a list of those.");} +return elements;};Dropzone.confirm=function(question,accepted,rejected){if(window.confirm(question)){return accepted();}else if(rejected!=null){return rejected();}};Dropzone.isValidFile=function(file,acceptedFiles){if(!acceptedFiles){return true;} +acceptedFiles=acceptedFiles.split(",");var mimeType=file.type;var baseMimeType=mimeType.replace(/\/.*$/,"");for(var _iterator38=acceptedFiles,_isArray38=true,_i40=0,_iterator38=_isArray38?_iterator38:_iterator38[Symbol.iterator]();;){var _ref35;if(_isArray38){if(_i40>=_iterator38.length)break;_ref35=_iterator38[_i40++];}else{_i40=_iterator38.next();if(_i40.done)break;_ref35=_i40.value;} +var validType=_ref35;validType=validType.trim();if(validType.charAt(0)==="."){if(file.name.toLowerCase().indexOf(validType.toLowerCase(),file.name.length-validType.length)!==-1){return true;}}else if(/\/\*$/.test(validType)){if(baseMimeType===validType.replace(/\/.*$/,"")){return true;}}else{if(mimeType===validType){return true;}}} +return false;};if(typeof jQuery!=='undefined'&&jQuery!==null){jQuery.fn.dropzone=function(options){return this.each(function(){return new Dropzone(this,options);});};} +if(typeof module!=='undefined'&&module!==null){module.exports=Dropzone;}else{window.Dropzone=Dropzone;} +Dropzone.ADDED="added";Dropzone.QUEUED="queued";Dropzone.ACCEPTED=Dropzone.QUEUED;Dropzone.UPLOADING="uploading";Dropzone.PROCESSING=Dropzone.UPLOADING;Dropzone.CANCELED="canceled";Dropzone.ERROR="error";Dropzone.SUCCESS="success";var detectVerticalSquash=function detectVerticalSquash(img){var iw=img.naturalWidth;var ih=img.naturalHeight;var canvas=document.createElement("canvas");canvas.width=1;canvas.height=ih;var ctx=canvas.getContext("2d");ctx.drawImage(img,0,0);var _ctx$getImageData=ctx.getImageData(1,0,1,ih),data=_ctx$getImageData.data;var sy=0;var ey=ih;var py=ih;while(py>sy){var alpha=data[(py-1)*4+3];if(alpha===0){ey=py;}else{sy=py;} +py=ey+sy>>1;} +var ratio=py/ih;if(ratio===0){return 1;}else{return ratio;}};var drawImageIOSFix=function drawImageIOSFix(ctx,img,sx,sy,sw,sh,dx,dy,dw,dh){var vertSquashRatio=detectVerticalSquash(img);return ctx.drawImage(img,sx,sy,sw,sh,dx,dy,dw,dh/vertSquashRatio);};var ExifRestore=function(){function ExifRestore(){_classCallCheck(this,ExifRestore);} +_createClass(ExifRestore,null,[{key:"initClass",value:function initClass(){this.KEY_STR='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';}},{key:"encode64",value:function encode64(input){var output='';var chr1=undefined;var chr2=undefined;var chr3='';var enc1=undefined;var enc2=undefined;var enc3=undefined;var enc4='';var i=0;while(true){chr1=input[i++];chr2=input[i++];chr3=input[i++];enc1=chr1>>2;enc2=(chr1&3)<<4|chr2>>4;enc3=(chr2&15)<<2|chr3>>6;enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64;}else if(isNaN(chr3)){enc4=64;} +output=output+this.KEY_STR.charAt(enc1)+this.KEY_STR.charAt(enc2)+this.KEY_STR.charAt(enc3)+this.KEY_STR.charAt(enc4);chr1=chr2=chr3='';enc1=enc2=enc3=enc4='';if(!(irawImageArray.length){break;}} +return segments;}},{key:"decode64",value:function decode64(input){var output='';var chr1=undefined;var chr2=undefined;var chr3='';var enc1=undefined;var enc2=undefined;var enc3=undefined;var enc4='';var i=0;var buf=[];var base64test=/[^A-Za-z0-9\+\/\=]/g;if(base64test.exec(input)){console.warn('There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, \'+\', \'/\',and \'=\'\nExpect errors in decoding.');} +input=input.replace(/[^A-Za-z0-9\+\/\=]/g,'');while(true){enc1=this.KEY_STR.indexOf(input.charAt(i++));enc2=this.KEY_STR.indexOf(input.charAt(i++));enc3=this.KEY_STR.indexOf(input.charAt(i++));enc4=this.KEY_STR.indexOf(input.charAt(i++));chr1=enc1<<2|enc2>>4;chr2=(enc2&15)<<4|enc3>>2;chr3=(enc3&3)<<6|enc4;buf.push(chr1);if(enc3!==64){buf.push(chr2);} +if(enc4!==64){buf.push(chr3);} +chr1=chr2=chr3='';enc1=enc2=enc3=enc4='';if(!(i=0){newClass=newClass.replace(' '+className+' ',' ');} +doc[add](pre+"DOMContentLoaded",init,false);doc[add](pre+"readystatechange",init,false);return win[add](pre+"load",init,false);}};Dropzone._autoDiscoverFunction=function(){if(Dropzone.autoDiscover){return Dropzone.discover();}};contentLoaded(window,Dropzone._autoDiscoverFunction);function __guard__(value,transform){return typeof value!=='undefined'&&value!==null?transform(value):undefined;} +function __guardMethod__(obj,methodName,transform){if(typeof obj!=='undefined'&&obj!==null&&typeof obj[methodName]==='function'){return transform(obj,methodName);}else{return undefined;}} +(function(window,document){var modalClass='.sweet-alert',overlayClass='.sweet-overlay',alertTypes=['error','warning','info','success'],defaultParams={title:'',text:'',type:null,allowOutsideClick:false,showCancelButton:false,showConfirmButton:true,closeOnConfirm:true,closeOnCancel:true,confirmButtonText:'OK',confirmButtonClass:'btn-primary',cancelButtonText:'Cancel',cancelButtonClass:'btn-default',containerClass:'',titleClass:'',textClass:'',imageUrl:null,imageSize:null,timer:null};var getModal=function(){return document.querySelector(modalClass);},getOverlay=function(){return document.querySelector(overlayClass);},hasClass=function(elem,className){return new RegExp(' '+className+' ').test(' '+elem.className+' ');},addClass=function(elem,className){if(className&&!hasClass(elem,className)){elem.className+=' '+className;}},removeClass=function(elem,className){var newClass=' '+elem.className.replace(/[\t\r\n]/g,' ')+' ';if(hasClass(elem,className)){while(newClass.indexOf(' '+className+' ')>=0){newClass=newClass.replace(' '+className+' ',' ');} elem.className=newClass.replace(/^\s+|\s+$/g,'');}},escapeHtml=function(str){var div=document.createElement('div');div.appendChild(document.createTextNode(str));return div.innerHTML;},_show=function(elem){elem.style.opacity='';elem.style.display='block';},show=function(elems){if(elems&&!elems.length){return _show(elems);} for(var i=0;i0){setTimeout(tick,interval);}else{elem.style.display='none';}};tick();},fireClick=function(node){if(MouseEvent){var mevt=new MouseEvent('click',{view:window,bubbles:false,cancelable:true});node.dispatchEvent(mevt);}else if(document.createEvent){var evt=document.createEvent('MouseEvents');evt.initEvent('click',false,false);node.dispatchEvent(evt);}else if(document.createEventObject){node.fireEvent('onclick');}else if(typeof node.onclick==='function'){node.onclick();}},stopEventPropagation=function(e){if(typeof e.stopPropagation==='function'){e.stopPropagation();e.preventDefault();}else if(window.event&&window.event.hasOwnProperty('cancelBubble')){window.event.cancelBubble=true;}};var previousActiveElement,previousDocumentClick,previousWindowKeyDown,lastFocusedButton;window.sweetAlertInitialize=function(){var sweetHTML='

Title

Text

',sweetWrap=document.createElement('div');sweetWrap.innerHTML=sweetHTML;document.body.appendChild(sweetWrap);} +return false;},getTopMargin=function(elem){elem.style.left='-9999px';elem.style.display='block';var height=elem.clientHeight;var padding=parseInt(getComputedStyle(elem).getPropertyValue('padding'),10);elem.style.left='';elem.style.display='none';return('-'+parseInt(height/2+padding)+'px');},fadeIn=function(elem,interval){if(+elem.style.opacity<1){interval=interval||16;elem.style.opacity=0;elem.style.display='block';var last=+new Date();var tick=function(){elem.style.opacity=+elem.style.opacity+(new Date()-last)/100;last=+new Date();if(+elem.style.opacity<1){setTimeout(tick,interval);}};tick();}},fadeOut=function(elem,interval){interval=interval||16;elem.style.opacity=1;var last=+new Date();var tick=function(){elem.style.opacity=+elem.style.opacity-(new Date()-last)/100;last=+new Date();if(+elem.style.opacity>0){setTimeout(tick,interval);}else{elem.style.display='none';}};tick();},fireClick=function(node){if(MouseEvent){var mevt=new MouseEvent('click',{view:window,bubbles:false,cancelable:true});node.dispatchEvent(mevt);}else if(document.createEvent){var evt=document.createEvent('MouseEvents');evt.initEvent('click',false,false);node.dispatchEvent(evt);}else if(document.createEventObject){node.fireEvent('onclick');}else if(typeof node.onclick==='function'){node.onclick();}},stopEventPropagation=function(e){if(typeof e.stopPropagation==='function'){e.stopPropagation();e.preventDefault();}else if(window.event&&window.event.hasOwnProperty('cancelBubble')){window.event.cancelBubble=true;}};var previousActiveElement,previousDocumentClick,previousWindowKeyDown,lastFocusedButton;window.sweetAlertInitialize=function(){var sweetHTML='

Title

Text

',sweetWrap=document.createElement('div');sweetWrap.innerHTML=sweetHTML;document.body.appendChild(sweetWrap);} window.sweetAlert=window.swal=function(){if(arguments[0]===undefined){window.console.error('sweetAlert expects at least 1 attribute!');return false;} var params=extend({},defaultParams);switch(typeof arguments[0]){case'string':params.title=arguments[0];params.text=arguments[1]||'';params.type=arguments[2]||'';break;case'object':if(arguments[0].title===undefined){window.console.error('Missing "title" argument!');return false;} params.title=arguments[0].title;params.text=arguments[0].text||defaultParams.text;params.type=arguments[0].type||defaultParams.type;params.allowOutsideClick=arguments[0].allowOutsideClick||defaultParams.allowOutsideClick;params.showCancelButton=arguments[0].showCancelButton!==undefined?arguments[0].showCancelButton:defaultParams.showCancelButton;params.showConfirmButton=arguments[0].showConfirmButton!==undefined?arguments[0].showConfirmButton:defaultParams.showConfirmButton;params.closeOnConfirm=arguments[0].closeOnConfirm!==undefined?arguments[0].closeOnConfirm:defaultParams.closeOnConfirm;params.closeOnCancel=arguments[0].closeOnCancel!==undefined?arguments[0].closeOnCancel:defaultParams.closeOnCancel;params.timer=arguments[0].timer||defaultParams.timer;params.confirmButtonText=(defaultParams.showCancelButton)?'Confirm':defaultParams.confirmButtonText;params.confirmButtonText=arguments[0].confirmButtonText||defaultParams.confirmButtonText;params.confirmButtonClass=arguments[0].confirmButtonClass||(arguments[0].type?'btn-'+arguments[0].type:null)||defaultParams.confirmButtonClass;params.cancelButtonText=arguments[0].cancelButtonText||defaultParams.cancelButtonText;params.cancelButtonClass=arguments[0].cancelButtonClass||defaultParams.cancelButtonClass;params.containerClass=arguments[0].containerClass||defaultParams.containerClass;params.titleClass=arguments[0].titleClass||defaultParams.titleClass;params.textClass=arguments[0].textClass||defaultParams.textClass;params.imageUrl=arguments[0].imageUrl||defaultParams.imageUrl;params.imageSize=arguments[0].imageSize||defaultParams.imageSize;params.doneFunction=arguments[1]||null;break;default:window.console.error('Unexpected type of argument! Expected "string" or "object", got '+typeof arguments[0]);return false;} diff --git a/modules/backend/assets/js/october.alert.js b/modules/backend/assets/js/october.alert.js index 71220d327..11d9e64c0 100644 --- a/modules/backend/assets/js/october.alert.js +++ b/modules/backend/assets/js/october.alert.js @@ -9,6 +9,7 @@ * * Dependences: * - Sweet Alert + * - Translations (october.lang.js) */ (function($){ diff --git a/modules/backend/assets/js/october.flyout.js b/modules/backend/assets/js/october.flyout.js index 2f4992572..1d5c9b15a 100644 --- a/modules/backend/assets/js/october.flyout.js +++ b/modules/backend/assets/js/october.flyout.js @@ -172,7 +172,7 @@ } Flyout.prototype.onDocumentKeydown = function(ev) { - if (ev.which == 27) { + if (ev.key === 'Escape') { this.hide(); } } diff --git a/modules/backend/assets/less/layout/mainmenu.less b/modules/backend/assets/less/layout/mainmenu.less index 27b125c35..a1af75cc9 100644 --- a/modules/backend/assets/less/layout/mainmenu.less +++ b/modules/backend/assets/less/layout/mainmenu.less @@ -214,6 +214,7 @@ nav#layout-mainmenu { img.account-avatar { width: 45px; + height: 45px; } .account-name { diff --git a/modules/backend/assets/vendor/dropzone/dropzone.js b/modules/backend/assets/vendor/dropzone/dropzone.js index babbdd450..476c0550d 100644 --- a/modules/backend/assets/vendor/dropzone/dropzone.js +++ b/modules/backend/assets/vendor/dropzone/dropzone.js @@ -1,3 +1,16 @@ +/** + * DropZone V5.5.1 (non-minified) for testing on October CMS + */ + +"use strict"; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /* * @@ -25,581 +38,1270 @@ * */ -(function() { - var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, - __slice = [].slice, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; +// The Emitter class provides the ability to call `.on()` on Dropzone to listen +// to events. +// It is strongly based on component's emitter class, and I removed the +// functionality because of the dependency hell with different frameworks. +var Emitter = function () { + function Emitter() { + _classCallCheck(this, Emitter); + } - noop = function() {}; + _createClass(Emitter, [{ + key: "on", - Emitter = (function() { - function Emitter() {} - - Emitter.prototype.addEventListener = Emitter.prototype.on; - - Emitter.prototype.on = function(event, fn) { + // Add an event listener for given event + value: function on(event, fn) { this._callbacks = this._callbacks || {}; + // Create namespace for this event if (!this._callbacks[event]) { this._callbacks[event] = []; } this._callbacks[event].push(fn); return this; - }; - - Emitter.prototype.emit = function() { - var args, callback, callbacks, event, _i, _len; - event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + } + }, { + key: "emit", + value: function emit(event) { this._callbacks = this._callbacks || {}; - callbacks = this._callbacks[event]; + var callbacks = this._callbacks[event]; + if (callbacks) { - for (_i = 0, _len = callbacks.length; _i < _len; _i++) { - callback = callbacks[_i]; + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + for (var _iterator = callbacks, _isArray = true, _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + var callback = _ref; + callback.apply(this, args); } } + return this; - }; + } - Emitter.prototype.removeListener = Emitter.prototype.off; + // Remove event listener for given event. If fn is not provided, all event + // listeners for that event will be removed. If neither is provided, all + // event listeners will be removed. - Emitter.prototype.removeAllListeners = Emitter.prototype.off; - - Emitter.prototype.removeEventListener = Emitter.prototype.off; - - Emitter.prototype.off = function(event, fn) { - var callback, callbacks, i, _i, _len; + }, { + key: "off", + value: function off(event, fn) { if (!this._callbacks || arguments.length === 0) { this._callbacks = {}; return this; } - callbacks = this._callbacks[event]; + + // specific event + var callbacks = this._callbacks[event]; if (!callbacks) { return this; } + + // remove all handlers if (arguments.length === 1) { delete this._callbacks[event]; return this; } - for (i = _i = 0, _len = callbacks.length; _i < _len; i = ++_i) { - callback = callbacks[i]; + + // remove specific handler + for (var i = 0; i < callbacks.length; i++) { + var callback = callbacks[i]; if (callback === fn) { callbacks.splice(i, 1); break; } } + return this; - }; + } + }]); - return Emitter; + return Emitter; +}(); - })(); +var Dropzone = function (_Emitter) { + _inherits(Dropzone, _Emitter); - Dropzone = (function(_super) { - var extend, resolveOption; + _createClass(Dropzone, null, [{ + key: "initClass", + value: function initClass() { - __extends(Dropzone, _super); - - Dropzone.prototype.Emitter = Emitter; - - - /* - This is a list of all available events you can register on a dropzone object. - - You can register an event handler like this: - - dropzone.on("dragEnter", function() { }); - */ - - Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"]; - - Dropzone.prototype.defaultOptions = { - url: null, - method: "post", - withCredentials: false, - parallelUploads: 2, - uploadMultiple: false, - maxFilesize: 256, - paramName: "file", - createImageThumbnails: true, - maxThumbnailFilesize: 10, - thumbnailWidth: 120, - thumbnailHeight: 120, - filesizeBase: 1000, - maxFiles: null, - filesizeBase: 1000, - params: {}, - clickable: true, - ignoreHiddenFiles: true, - acceptedFiles: null, - acceptedMimeTypes: null, - autoProcessQueue: true, - autoQueue: true, - addRemoveLinks: false, - previewsContainer: null, - capture: null, - dictDefaultMessage: "Drop files here to upload", - dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.", - dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.", - dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", - dictInvalidFileType: "You can't upload files of this type.", - dictResponseError: "Server responded with {{statusCode}} code.", - dictCancelUpload: "Cancel upload", - dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", - dictRemoveFile: "Remove file", - dictRemoveFileConfirmation: null, - dictMaxFilesExceeded: "You can not upload any more files.", - accept: function(file, done) { - return done(); - }, - init: function() { - return noop; - }, - forceFallback: false, - fallback: function() { - var child, messageElement, span, _i, _len, _ref; - this.element.className = "" + this.element.className + " dz-browser-not-supported"; - _ref = this.element.getElementsByTagName("div"); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - child = _ref[_i]; - if (/(^| )dz-message($| )/.test(child.className)) { - messageElement = child; - child.className = "dz-message"; - continue; - } - } - if (!messageElement) { - messageElement = Dropzone.createElement("
"); - this.element.appendChild(messageElement); - } - span = messageElement.getElementsByTagName("span")[0]; - if (span) { - span.textContent = this.options.dictFallbackMessage; - } - return this.element.appendChild(this.getFallbackForm()); - }, - resize: function(file) { - var info, srcRatio, trgRatio; - info = { - srcX: 0, - srcY: 0, - srcWidth: file.width, - srcHeight: file.height - }; - srcRatio = file.width / file.height; - info.optWidth = this.options.thumbnailWidth; - info.optHeight = this.options.thumbnailHeight; - if ((info.optWidth == null) && (info.optHeight == null)) { - info.optWidth = info.srcWidth; - info.optHeight = info.srcHeight; - } else if (info.optWidth == null) { - info.optWidth = srcRatio * info.optHeight; - } else if (info.optHeight == null) { - info.optHeight = (1 / srcRatio) * info.optWidth; - } - trgRatio = info.optWidth / info.optHeight; - if (file.height < info.optHeight || file.width < info.optWidth) { - info.trgHeight = info.srcHeight; - info.trgWidth = info.srcWidth; - } else { - if (srcRatio > trgRatio) { - info.srcHeight = file.height; - info.srcWidth = info.srcHeight * trgRatio; - } else { - info.srcWidth = file.width; - info.srcHeight = info.srcWidth / trgRatio; - } - } - info.srcX = (file.width - info.srcWidth) / 2; - info.srcY = (file.height - info.srcHeight) / 2; - return info; - }, + // Exposing the emitter class, mainly for tests + this.prototype.Emitter = Emitter; /* - Those functions register themselves to the events on init and handle all - the user interface specific stuff. Overwriting them won't break the upload - but can break the way it's displayed. - You can overwrite them if you don't like the default behavior. If you just - want to add an additional event handler, register it on the dropzone object - and don't overwrite those options. - */ - drop: function(e) { - return this.element.classList.remove("dz-drag-hover"); - }, - dragstart: noop, - dragend: function(e) { - return this.element.classList.remove("dz-drag-hover"); - }, - dragenter: function(e) { - return this.element.classList.add("dz-drag-hover"); - }, - dragover: function(e) { - return this.element.classList.add("dz-drag-hover"); - }, - dragleave: function(e) { - return this.element.classList.remove("dz-drag-hover"); - }, - paste: noop, - reset: function() { - return this.element.classList.remove("dz-started"); - }, - addedfile: function(file) { - var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results; - if (this.element === this.previewsContainer) { - this.element.classList.add("dz-started"); - } - if (this.previewsContainer) { - file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim()); - file.previewTemplate = file.previewElement; - this.previewsContainer.appendChild(file.previewElement); - _ref = file.previewElement.querySelectorAll("[data-dz-name]"); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; - node.textContent = file.name; + This is a list of all available events you can register on a dropzone object. + You can register an event handler like this: + dropzone.on("dragEnter", function() { }); + */ + this.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "addedfiles", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"]; + + this.prototype.defaultOptions = { + /** + * Has to be specified on elements other than form (or when the form + * doesn't have an `action` attribute). You can also + * provide a function that will be called with `files` and + * must return the url (since `v3.12.0`) + */ + url: null, + + /** + * Can be changed to `"put"` if necessary. You can also provide a function + * that will be called with `files` and must return the method (since `v3.12.0`). + */ + method: "post", + + /** + * Will be set on the XHRequest. + */ + withCredentials: false, + + /** + * The timeout for the XHR requests in milliseconds (since `v4.4.0`). + */ + timeout: 30000, + + /** + * How many file uploads to process in parallel (See the + * Enqueuing file uploads* documentation section for more info) + */ + parallelUploads: 2, + + /** + * Whether to send multiple files in one request. If + * this it set to true, then the fallback file input element will + * have the `multiple` attribute as well. This option will + * also trigger additional events (like `processingmultiple`). See the events + * documentation section for more information. + */ + uploadMultiple: false, + + /** + * Whether you want files to be uploaded in chunks to your server. This can't be + * used in combination with `uploadMultiple`. + * + * See [chunksUploaded](#config-chunksUploaded) for the callback to finalise an upload. + */ + chunking: false, + + /** + * If `chunking` is enabled, this defines whether **every** file should be chunked, + * even if the file size is below chunkSize. This means, that the additional chunk + * form data will be submitted and the `chunksUploaded` callback will be invoked. + */ + forceChunking: false, + + /** + * If `chunking` is `true`, then this defines the chunk size in bytes. + */ + chunkSize: 2000000, + + /** + * If `true`, the individual chunks of a file are being uploaded simultaneously. + */ + parallelChunkUploads: false, + + /** + * Whether a chunk should be retried if it fails. + */ + retryChunks: false, + + /** + * If `retryChunks` is true, how many times should it be retried. + */ + retryChunksLimit: 3, + + /** + * If not `null` defines how many files this Dropzone handles. If it exceeds, + * the event `maxfilesexceeded` will be called. The dropzone element gets the + * class `dz-max-files-reached` accordingly so you can provide visual feedback. + */ + maxFilesize: 256, + + /** + * The name of the file param that gets transferred. + * **NOTE**: If you have the option `uploadMultiple` set to `true`, then + * Dropzone will append `[]` to the name. + */ + paramName: "file", + + /** + * Whether thumbnails for images should be generated + */ + createImageThumbnails: true, + + /** + * In MB. When the filename exceeds this limit, the thumbnail will not be generated. + */ + maxThumbnailFilesize: 10, + + /** + * If `null`, the ratio of the image will be used to calculate it. + */ + thumbnailWidth: 120, + + /** + * The same as `thumbnailWidth`. If both are null, images will not be resized. + */ + thumbnailHeight: 120, + + /** + * How the images should be scaled down in case both, `thumbnailWidth` and `thumbnailHeight` are provided. + * Can be either `contain` or `crop`. + */ + thumbnailMethod: 'crop', + + /** + * If set, images will be resized to these dimensions before being **uploaded**. + * If only one, `resizeWidth` **or** `resizeHeight` is provided, the original aspect + * ratio of the file will be preserved. + * + * The `options.transformFile` function uses these options, so if the `transformFile` function + * is overridden, these options don't do anything. + */ + resizeWidth: null, + + /** + * See `resizeWidth`. + */ + resizeHeight: null, + + /** + * The mime type of the resized image (before it gets uploaded to the server). + * If `null` the original mime type will be used. To force jpeg, for example, use `image/jpeg`. + * See `resizeWidth` for more information. + */ + resizeMimeType: null, + + /** + * The quality of the resized images. See `resizeWidth`. + */ + resizeQuality: 0.8, + + /** + * How the images should be scaled down in case both, `resizeWidth` and `resizeHeight` are provided. + * Can be either `contain` or `crop`. + */ + resizeMethod: 'contain', + + /** + * The base that is used to calculate the filesize. You can change this to + * 1024 if you would rather display kibibytes, mebibytes, etc... + * 1024 is technically incorrect, because `1024 bytes` are `1 kibibyte` not `1 kilobyte`. + * You can change this to `1024` if you don't care about validity. + */ + filesizeBase: 1000, + + /** + * Can be used to limit the maximum number of files that will be handled by this Dropzone + */ + maxFiles: null, + + /** + * An optional object to send additional headers to the server. Eg: + * `{ "My-Awesome-Header": "header value" }` + */ + headers: null, + + /** + * If `true`, the dropzone element itself will be clickable, if `false` + * nothing will be clickable. + * + * You can also pass an HTML element, a CSS selector (for multiple elements) + * or an array of those. In that case, all of those elements will trigger an + * upload when clicked. + */ + clickable: true, + + /** + * Whether hidden files in directories should be ignored. + */ + ignoreHiddenFiles: true, + + /** + * The default implementation of `accept` checks the file's mime type or + * extension against this list. This is a comma separated list of mime + * types or file extensions. + * + * Eg.: `image/*,application/pdf,.psd` + * + * If the Dropzone is `clickable` this option will also be used as + * [`accept`](https://developer.mozilla.org/en-US/docs/HTML/Element/input#attr-accept) + * parameter on the hidden file input as well. + */ + acceptedFiles: null, + + /** + * **Deprecated!** + * Use acceptedFiles instead. + */ + acceptedMimeTypes: null, + + /** + * If false, files will be added to the queue but the queue will not be + * processed automatically. + * This can be useful if you need some additional user input before sending + * files (or if you want want all files sent at once). + * If you're ready to send the file simply call `myDropzone.processQueue()`. + * + * See the [enqueuing file uploads](#enqueuing-file-uploads) documentation + * section for more information. + */ + autoProcessQueue: true, + + /** + * If false, files added to the dropzone will not be queued by default. + * You'll have to call `enqueueFile(file)` manually. + */ + autoQueue: true, + + /** + * If `true`, this will add a link to every file preview to remove or cancel (if + * already uploading) the file. The `dictCancelUpload`, `dictCancelUploadConfirmation` + * and `dictRemoveFile` options are used for the wording. + */ + addRemoveLinks: false, + + /** + * Defines where to display the file previews – if `null` the + * Dropzone element itself is used. Can be a plain `HTMLElement` or a CSS + * selector. The element should have the `dropzone-previews` class so + * the previews are displayed properly. + */ + previewsContainer: null, + + /** + * This is the element the hidden input field (which is used when clicking on the + * dropzone to trigger file selection) will be appended to. This might + * be important in case you use frameworks to switch the content of your page. + * + * Can be a selector string, or an element directly. + */ + hiddenInputContainer: "body", + + /** + * If null, no capture type will be specified + * If camera, mobile devices will skip the file selection and choose camera + * If microphone, mobile devices will skip the file selection and choose the microphone + * If camcorder, mobile devices will skip the file selection and choose the camera in video mode + * On apple devices multiple must be set to false. AcceptedFiles may need to + * be set to an appropriate mime type (e.g. "image/*", "audio/*", or "video/*"). + */ + capture: null, + + /** + * **Deprecated**. Use `renameFile` instead. + */ + renameFilename: null, + + /** + * A function that is invoked before the file is uploaded to the server and renames the file. + * This function gets the `File` as argument and can use the `file.name`. The actual name of the + * file that gets used during the upload can be accessed through `file.upload.filename`. + */ + renameFile: null, + + /** + * If `true` the fallback will be forced. This is very useful to test your server + * implementations first and make sure that everything works as + * expected without dropzone if you experience problems, and to test + * how your fallbacks will look. + */ + forceFallback: false, + + /** + * The text used before any files are dropped. + */ + dictDefaultMessage: "Drop files here to upload", + + /** + * The text that replaces the default message text it the browser is not supported. + */ + dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.", + + /** + * The text that will be added before the fallback form. + * If you provide a fallback element yourself, or if this option is `null` this will + * be ignored. + */ + dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.", + + /** + * If the filesize is too big. + * `{{filesize}}` and `{{maxFilesize}}` will be replaced with the respective configuration values. + */ + dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", + + /** + * If the file doesn't match the file type. + */ + dictInvalidFileType: "You can't upload files of this type.", + + /** + * If the server response was invalid. + * `{{statusCode}}` will be replaced with the servers status code. + */ + dictResponseError: "Server responded with {{statusCode}} code.", + + /** + * If `addRemoveLinks` is true, the text to be used for the cancel upload link. + */ + dictCancelUpload: "Cancel upload", + + /** + * The text that is displayed if an upload was manually canceled + */ + dictUploadCanceled: "Upload canceled.", + + /** + * If `addRemoveLinks` is true, the text to be used for confirmation when cancelling upload. + */ + dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", + + /** + * If `addRemoveLinks` is true, the text to be used to remove a file. + */ + dictRemoveFile: "Remove file", + + /** + * If this is not null, then the user will be prompted before removing a file. + */ + dictRemoveFileConfirmation: null, + + /** + * Displayed if `maxFiles` is st and exceeded. + * The string `{{maxFiles}}` will be replaced by the configuration value. + */ + dictMaxFilesExceeded: "You can not upload any more files.", + + /** + * Allows you to translate the different units. Starting with `tb` for terabytes and going down to + * `b` for bytes. + */ + dictFileSizeUnits: { tb: "TB", gb: "GB", mb: "MB", kb: "KB", b: "b" }, + /** + * Called when dropzone initialized + * You can add event listeners here + */ + init: function init() {}, + + + /** + * Can be an **object** of additional parameters to transfer to the server, **or** a `Function` + * that gets invoked with the `files`, `xhr` and, if it's a chunked upload, `chunk` arguments. In case + * of a function, this needs to return a map. + * + * The default implementation does nothing for normal uploads, but adds relevant information for + * chunked uploads. + * + * This is the same as adding hidden input fields in the form element. + */ + params: function params(files, xhr, chunk) { + if (chunk) { + return { + dzuuid: chunk.file.upload.uuid, + dzchunkindex: chunk.index, + dztotalfilesize: chunk.file.size, + dzchunksize: this.options.chunkSize, + dztotalchunkcount: chunk.file.upload.totalChunkCount, + dzchunkbyteoffset: chunk.index * this.options.chunkSize + }; } - _ref1 = file.previewElement.querySelectorAll("[data-dz-size]"); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - node = _ref1[_j]; - node.innerHTML = this.filesize(file.size); + }, + + + /** + * A function that gets a [file](https://developer.mozilla.org/en-US/docs/DOM/File) + * and a `done` function as parameters. + * + * If the done function is invoked without arguments, the file is "accepted" and will + * be processed. If you pass an error message, the file is rejected, and the error + * message will be displayed. + * This function will not be called if the file is too big or doesn't match the mime types. + */ + accept: function accept(file, done) { + return done(); + }, + + + /** + * The callback that will be invoked when all chunks have been uploaded for a file. + * It gets the file for which the chunks have been uploaded as the first parameter, + * and the `done` function as second. `done()` needs to be invoked when everything + * needed to finish the upload process is done. + */ + chunksUploaded: function chunksUploaded(file, done) { + done(); + }, + + /** + * Gets called when the browser is not supported. + * The default implementation shows the fallback input field and adds + * a text. + */ + fallback: function fallback() { + // This code should pass in IE7... :( + var messageElement = void 0; + this.element.className = this.element.className + " dz-browser-not-supported"; + + for (var _iterator2 = this.element.getElementsByTagName("div"), _isArray2 = true, _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref2; + + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref2 = _iterator2[_i2++]; + } else { + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref2 = _i2.value; + } + + var child = _ref2; + + if (/(^| )dz-message($| )/.test(child.className)) { + messageElement = child; + child.className = "dz-message"; // Removes the 'dz-default' class + break; + } } - if (this.options.addRemoveLinks) { - file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + ""); - file.previewElement.appendChild(file._removeLink); + if (!messageElement) { + messageElement = Dropzone.createElement("
"); + this.element.appendChild(messageElement); } - removeFileEvent = (function(_this) { - return function(e) { + + var span = messageElement.getElementsByTagName("span")[0]; + if (span) { + if (span.textContent != null) { + span.textContent = this.options.dictFallbackMessage; + } else if (span.innerText != null) { + span.innerText = this.options.dictFallbackMessage; + } + } + + return this.element.appendChild(this.getFallbackForm()); + }, + + + /** + * Gets called to calculate the thumbnail dimensions. + * + * It gets `file`, `width` and `height` (both may be `null`) as parameters and must return an object containing: + * + * - `srcWidth` & `srcHeight` (required) + * - `trgWidth` & `trgHeight` (required) + * - `srcX` & `srcY` (optional, default `0`) + * - `trgX` & `trgY` (optional, default `0`) + * + * Those values are going to be used by `ctx.drawImage()`. + */ + resize: function resize(file, width, height, resizeMethod) { + var info = { + srcX: 0, + srcY: 0, + srcWidth: file.width, + srcHeight: file.height + }; + + var srcRatio = file.width / file.height; + + // Automatically calculate dimensions if not specified + if (width == null && height == null) { + width = info.srcWidth; + height = info.srcHeight; + } else if (width == null) { + width = height * srcRatio; + } else if (height == null) { + height = width / srcRatio; + } + + // Make sure images aren't upscaled + width = Math.min(width, info.srcWidth); + height = Math.min(height, info.srcHeight); + + var trgRatio = width / height; + + if (info.srcWidth > width || info.srcHeight > height) { + // Image is bigger and needs rescaling + if (resizeMethod === 'crop') { + if (srcRatio > trgRatio) { + info.srcHeight = file.height; + info.srcWidth = info.srcHeight * trgRatio; + } else { + info.srcWidth = file.width; + info.srcHeight = info.srcWidth / trgRatio; + } + } else if (resizeMethod === 'contain') { + // Method 'contain' + if (srcRatio > trgRatio) { + height = width / srcRatio; + } else { + width = height * srcRatio; + } + } else { + throw new Error("Unknown resizeMethod '" + resizeMethod + "'"); + } + } + + info.srcX = (file.width - info.srcWidth) / 2; + info.srcY = (file.height - info.srcHeight) / 2; + + info.trgWidth = width; + info.trgHeight = height; + + return info; + }, + + + /** + * Can be used to transform the file (for example, resize an image if necessary). + * + * The default implementation uses `resizeWidth` and `resizeHeight` (if provided) and resizes + * images according to those dimensions. + * + * Gets the `file` as the first parameter, and a `done()` function as the second, that needs + * to be invoked with the file when the transformation is done. + */ + transformFile: function transformFile(file, done) { + if ((this.options.resizeWidth || this.options.resizeHeight) && file.type.match(/image.*/)) { + return this.resizeImage(file, this.options.resizeWidth, this.options.resizeHeight, this.options.resizeMethod, done); + } else { + return done(file); + } + }, + + + /** + * A string that contains the template used for each dropped + * file. Change it to fulfill your needs but make sure to properly + * provide all elements. + * + * If you want to use an actual HTML element instead of providing a String + * as a config option, you could create a div with the id `tpl`, + * put the template inside it and provide the element like this: + * + * document + * .querySelector('#tpl') + * .innerHTML + * + */ + previewTemplate: "
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
", + + // END OPTIONS + // (Required by the dropzone documentation parser) + + + /* + Those functions register themselves to the events on init and handle all + the user interface specific stuff. Overwriting them won't break the upload + but can break the way it's displayed. + You can overwrite them if you don't like the default behavior. If you just + want to add an additional event handler, register it on the dropzone object + and don't overwrite those options. + */ + + // Those are self explanatory and simply concern the DragnDrop. + drop: function drop(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragstart: function dragstart(e) {}, + dragend: function dragend(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragenter: function dragenter(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragover: function dragover(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragleave: function dragleave(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + paste: function paste(e) {}, + + + // Called whenever there are no files left in the dropzone anymore, and the + // dropzone should be displayed as if in the initial state. + reset: function reset() { + return this.element.classList.remove("dz-started"); + }, + + + // Called when a file is added to the queue + // Receives `file` + addedfile: function addedfile(file) { + var _this2 = this; + + if (this.element === this.previewsContainer) { + this.element.classList.add("dz-started"); + } + + if (this.previewsContainer) { + file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim()); + file.previewTemplate = file.previewElement; // Backwards compatibility + + this.previewsContainer.appendChild(file.previewElement); + for (var _iterator3 = file.previewElement.querySelectorAll("[data-dz-name]"), _isArray3 = true, _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { + var _ref3; + + if (_isArray3) { + if (_i3 >= _iterator3.length) break; + _ref3 = _iterator3[_i3++]; + } else { + _i3 = _iterator3.next(); + if (_i3.done) break; + _ref3 = _i3.value; + } + + var node = _ref3; + + node.textContent = file.name; + } + for (var _iterator4 = file.previewElement.querySelectorAll("[data-dz-size]"), _isArray4 = true, _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { + if (_isArray4) { + if (_i4 >= _iterator4.length) break; + node = _iterator4[_i4++]; + } else { + _i4 = _iterator4.next(); + if (_i4.done) break; + node = _i4.value; + } + + node.innerHTML = this.filesize(file.size); + } + + if (this.options.addRemoveLinks) { + file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + ""); + file.previewElement.appendChild(file._removeLink); + } + + var removeFileEvent = function removeFileEvent(e) { e.preventDefault(); e.stopPropagation(); if (file.status === Dropzone.UPLOADING) { - return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() { - return _this.removeFile(file); + return Dropzone.confirm(_this2.options.dictCancelUploadConfirmation, function () { + return _this2.removeFile(file); }); } else { - if (_this.options.dictRemoveFileConfirmation) { - return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() { - return _this.removeFile(file); + if (_this2.options.dictRemoveFileConfirmation) { + return Dropzone.confirm(_this2.options.dictRemoveFileConfirmation, function () { + return _this2.removeFile(file); }); } else { - return _this.removeFile(file); + return _this2.removeFile(file); } } }; - })(this); - _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]"); - _results = []; - for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { - removeLink = _ref2[_k]; - _results.push(removeLink.addEventListener("click", removeFileEvent)); - } - return _results; - } - }, - removedfile: function(file) { - var _ref; - if (file.previewElement) { - if ((_ref = file.previewElement) != null) { - _ref.parentNode.removeChild(file.previewElement); - } - } - return this._updateMaxFilesReachedClass(); - }, - thumbnail: function(file, dataUrl) { - var thumbnailElement, _i, _len, _ref; - if (file.previewElement) { - file.previewElement.classList.remove("dz-file-preview"); - _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]"); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - thumbnailElement = _ref[_i]; - thumbnailElement.alt = file.name; - thumbnailElement.src = dataUrl; - } - return setTimeout(((function(_this) { - return function() { - return file.previewElement.classList.add("dz-image-preview"); - }; - })(this)), 1); - } - }, - error: function(file, message) { - var node, _i, _len, _ref, _results; - if (file.previewElement) { - file.previewElement.classList.add("dz-error"); - if (typeof message !== "String" && message.error) { - message = message.error; - } - _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]"); - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; - _results.push(node.textContent = message); - } - return _results; - } - }, - errormultiple: noop, - processing: function(file) { - if (file.previewElement) { - file.previewElement.classList.add("dz-processing"); - if (file._removeLink) { - return file._removeLink.textContent = this.options.dictCancelUpload; - } - } - }, - processingmultiple: noop, - uploadprogress: function(file, progress, bytesSent) { - var node, _i, _len, _ref, _results; - if (file.previewElement) { - _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]"); - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - node = _ref[_i]; - if (node.nodeName === 'PROGRESS') { - _results.push(node.value = progress); - } else { - _results.push(node.style.width = "" + progress + "%"); + + for (var _iterator5 = file.previewElement.querySelectorAll("[data-dz-remove]"), _isArray5 = true, _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { + var _ref4; + + if (_isArray5) { + if (_i5 >= _iterator5.length) break; + _ref4 = _iterator5[_i5++]; + } else { + _i5 = _iterator5.next(); + if (_i5.done) break; + _ref4 = _i5.value; + } + + var removeLink = _ref4; + + removeLink.addEventListener("click", removeFileEvent); } } - return _results; - } - }, - totaluploadprogress: noop, - sending: noop, - sendingmultiple: noop, - success: function(file) { - if (file.previewElement) { - return file.previewElement.classList.add("dz-success"); - } - }, - successmultiple: noop, - canceled: function(file) { - return this.emit("error", file, "Upload canceled."); - }, - canceledmultiple: noop, - complete: function(file) { - if (file._removeLink) { - file._removeLink.textContent = this.options.dictRemoveFile; - } - if (file.previewElement) { - return file.previewElement.classList.add("dz-complete"); - } - }, - completemultiple: noop, - maxfilesexceeded: noop, - maxfilesreached: noop, - queuecomplete: noop, - previewTemplate: "
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
" - }; + }, - extend = function() { - var key, object, objects, target, val, _i, _len; - target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : []; - for (_i = 0, _len = objects.length; _i < _len; _i++) { - object = objects[_i]; - for (key in object) { - val = object[key]; + + // Called whenever a file is removed. + removedfile: function removedfile(file) { + if (file.previewElement != null && file.previewElement.parentNode != null) { + file.previewElement.parentNode.removeChild(file.previewElement); + } + return this._updateMaxFilesReachedClass(); + }, + + + // Called when a thumbnail has been generated + // Receives `file` and `dataUrl` + thumbnail: function thumbnail(file, dataUrl) { + if (file.previewElement) { + file.previewElement.classList.remove("dz-file-preview"); + for (var _iterator6 = file.previewElement.querySelectorAll("[data-dz-thumbnail]"), _isArray6 = true, _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { + var _ref5; + + if (_isArray6) { + if (_i6 >= _iterator6.length) break; + _ref5 = _iterator6[_i6++]; + } else { + _i6 = _iterator6.next(); + if (_i6.done) break; + _ref5 = _i6.value; + } + + var thumbnailElement = _ref5; + + thumbnailElement.alt = file.name; + thumbnailElement.src = dataUrl; + } + + return setTimeout(function () { + return file.previewElement.classList.add("dz-image-preview"); + }, 1); + } + }, + + + // Called whenever an error occurs + // Receives `file` and `message` + error: function error(file, message) { + if (file.previewElement) { + file.previewElement.classList.add("dz-error"); + if (typeof message !== "String" && message.error) { + message = message.error; + } + for (var _iterator7 = file.previewElement.querySelectorAll("[data-dz-errormessage]"), _isArray7 = true, _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { + var _ref6; + + if (_isArray7) { + if (_i7 >= _iterator7.length) break; + _ref6 = _iterator7[_i7++]; + } else { + _i7 = _iterator7.next(); + if (_i7.done) break; + _ref6 = _i7.value; + } + + var node = _ref6; + + node.textContent = message; + } + } + }, + errormultiple: function errormultiple() {}, + + + // Called when a file gets processed. Since there is a cue, not all added + // files are processed immediately. + // Receives `file` + processing: function processing(file) { + if (file.previewElement) { + file.previewElement.classList.add("dz-processing"); + if (file._removeLink) { + return file._removeLink.innerHTML = this.options.dictCancelUpload; + } + } + }, + processingmultiple: function processingmultiple() {}, + + + // Called whenever the upload progress gets updated. + // Receives `file`, `progress` (percentage 0-100) and `bytesSent`. + // To get the total number of bytes of the file, use `file.size` + uploadprogress: function uploadprogress(file, progress, bytesSent) { + if (file.previewElement) { + for (var _iterator8 = file.previewElement.querySelectorAll("[data-dz-uploadprogress]"), _isArray8 = true, _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { + var _ref7; + + if (_isArray8) { + if (_i8 >= _iterator8.length) break; + _ref7 = _iterator8[_i8++]; + } else { + _i8 = _iterator8.next(); + if (_i8.done) break; + _ref7 = _i8.value; + } + + var node = _ref7; + + node.nodeName === 'PROGRESS' ? node.value = progress : node.style.width = progress + "%"; + } + } + }, + + + // Called whenever the total upload progress gets updated. + // Called with totalUploadProgress (0-100), totalBytes and totalBytesSent + totaluploadprogress: function totaluploadprogress() {}, + + + // Called just before the file is sent. Gets the `xhr` object as second + // parameter, so you can modify it (for example to add a CSRF token) and a + // `formData` object to add additional information. + sending: function sending() {}, + sendingmultiple: function sendingmultiple() {}, + + + // When the complete upload is finished and successful + // Receives `file` + success: function success(file) { + if (file.previewElement) { + return file.previewElement.classList.add("dz-success"); + } + }, + successmultiple: function successmultiple() {}, + + + // When the upload is canceled. + canceled: function canceled(file) { + return this.emit("error", file, this.options.dictUploadCanceled); + }, + canceledmultiple: function canceledmultiple() {}, + + + // When the upload is finished, either with success or an error. + // Receives `file` + complete: function complete(file) { + if (file._removeLink) { + file._removeLink.innerHTML = this.options.dictRemoveFile; + } + if (file.previewElement) { + return file.previewElement.classList.add("dz-complete"); + } + }, + completemultiple: function completemultiple() {}, + maxfilesexceeded: function maxfilesexceeded() {}, + maxfilesreached: function maxfilesreached() {}, + queuecomplete: function queuecomplete() {}, + addedfiles: function addedfiles() {} + }; + + this.prototype._thumbnailQueue = []; + this.prototype._processingThumbnail = false; + } + + // global utility + + }, { + key: "extend", + value: function extend(target) { + for (var _len2 = arguments.length, objects = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + objects[_key2 - 1] = arguments[_key2]; + } + + for (var _iterator9 = objects, _isArray9 = true, _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref8; + + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref8 = _iterator9[_i9++]; + } else { + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref8 = _i9.value; + } + + var object = _ref8; + + for (var key in object) { + var val = object[key]; target[key] = val; } } return target; - }; + } + }]); - function Dropzone(element, options) { - var elementOptions, fallback, _ref; - this.element = element; - this.version = Dropzone.version; - this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, ""); - this.clickableElements = []; - this.listeners = []; - this.files = []; - if (typeof this.element === "string") { - this.element = document.querySelector(this.element); - } - if (!(this.element && (this.element.nodeType != null))) { - throw new Error("Invalid dropzone element."); - } - if (this.element.dropzone) { - throw new Error("Dropzone already attached."); - } - Dropzone.instances.push(this); - this.element.dropzone = this; - elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {}; - this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {}); - if (this.options.forceFallback || !Dropzone.isBrowserSupported()) { - return this.options.fallback.call(this); - } - if (this.options.url == null) { - this.options.url = this.element.getAttribute("action"); - } - if (!this.options.url) { - throw new Error("No URL provided."); - } - if (this.options.acceptedFiles && this.options.acceptedMimeTypes) { - throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); - } - if (this.options.acceptedMimeTypes) { - this.options.acceptedFiles = this.options.acceptedMimeTypes; - delete this.options.acceptedMimeTypes; - } - this.options.method = this.options.method.toUpperCase(); - if ((fallback = this.getExistingFallback()) && fallback.parentNode) { - fallback.parentNode.removeChild(fallback); - } - if (this.options.previewsContainer !== false) { - if (this.options.previewsContainer) { - this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer"); - } else { - this.previewsContainer = this.element; - } - } - if (this.options.clickable) { - if (this.options.clickable === true) { - this.clickableElements = [this.element]; - } else { - this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable"); - } - } - this.init(); + function Dropzone(el, options) { + _classCallCheck(this, Dropzone); + + var _this = _possibleConstructorReturn(this, (Dropzone.__proto__ || Object.getPrototypeOf(Dropzone)).call(this)); + + var fallback = void 0, + left = void 0; + _this.element = el; + // For backwards compatibility since the version was in the prototype previously + _this.version = Dropzone.version; + + _this.defaultOptions.previewTemplate = _this.defaultOptions.previewTemplate.replace(/\n*/g, ""); + + _this.clickableElements = []; + _this.listeners = []; + _this.files = []; // All files + + if (typeof _this.element === "string") { + _this.element = document.querySelector(_this.element); } - Dropzone.prototype.getAcceptedFiles = function() { - var file, _i, _len, _ref, _results; - _ref = this.files; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; - if (file.accepted) { - _results.push(file); - } - } - return _results; - }; + // Not checking if instance of HTMLElement or Element since IE9 is extremely weird. + if (!_this.element || _this.element.nodeType == null) { + throw new Error("Invalid dropzone element."); + } - Dropzone.prototype.getRejectedFiles = function() { - var file, _i, _len, _ref, _results; - _ref = this.files; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; - if (!file.accepted) { - _results.push(file); - } - } - return _results; - }; + if (_this.element.dropzone) { + throw new Error("Dropzone already attached."); + } - Dropzone.prototype.getFilesWithStatus = function(status) { - var file, _i, _len, _ref, _results; - _ref = this.files; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; - if (file.status === status) { - _results.push(file); - } - } - return _results; - }; + // Now add this dropzone to the instances. + Dropzone.instances.push(_this); - Dropzone.prototype.getQueuedFiles = function() { + // Put the dropzone inside the element itself. + _this.element.dropzone = _this; + + var elementOptions = (left = Dropzone.optionsForElement(_this.element)) != null ? left : {}; + + _this.options = Dropzone.extend({}, _this.defaultOptions, elementOptions, options != null ? options : {}); + + // If the browser failed, just call the fallback and leave + if (_this.options.forceFallback || !Dropzone.isBrowserSupported()) { + var _ret; + + return _ret = _this.options.fallback.call(_this), _possibleConstructorReturn(_this, _ret); + } + + // @options.url = @element.getAttribute "action" unless @options.url? + if (_this.options.url == null) { + _this.options.url = _this.element.getAttribute("action"); + } + + if (!_this.options.url) { + throw new Error("No URL provided."); + } + + if (_this.options.acceptedFiles && _this.options.acceptedMimeTypes) { + throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); + } + + if (_this.options.uploadMultiple && _this.options.chunking) { + throw new Error('You cannot set both: uploadMultiple and chunking.'); + } + + // Backwards compatibility + if (_this.options.acceptedMimeTypes) { + _this.options.acceptedFiles = _this.options.acceptedMimeTypes; + delete _this.options.acceptedMimeTypes; + } + + // Backwards compatibility + if (_this.options.renameFilename != null) { + _this.options.renameFile = function (file) { + return _this.options.renameFilename.call(_this, file.name, file); + }; + } + + _this.options.method = _this.options.method.toUpperCase(); + + if ((fallback = _this.getExistingFallback()) && fallback.parentNode) { + // Remove the fallback + fallback.parentNode.removeChild(fallback); + } + + // Display previews in the previewsContainer element or the Dropzone element unless explicitly set to false + if (_this.options.previewsContainer !== false) { + if (_this.options.previewsContainer) { + _this.previewsContainer = Dropzone.getElement(_this.options.previewsContainer, "previewsContainer"); + } else { + _this.previewsContainer = _this.element; + } + } + + if (_this.options.clickable) { + if (_this.options.clickable === true) { + _this.clickableElements = [_this.element]; + } else { + _this.clickableElements = Dropzone.getElements(_this.options.clickable, "clickable"); + } + } + + _this.init(); + return _this; + } + + // Returns all files that have been accepted + + + _createClass(Dropzone, [{ + key: "getAcceptedFiles", + value: function getAcceptedFiles() { + return this.files.filter(function (file) { + return file.accepted; + }).map(function (file) { + return file; + }); + } + + // Returns all files that have been rejected + // Not sure when that's going to be useful, but added for completeness. + + }, { + key: "getRejectedFiles", + value: function getRejectedFiles() { + return this.files.filter(function (file) { + return !file.accepted; + }).map(function (file) { + return file; + }); + } + }, { + key: "getFilesWithStatus", + value: function getFilesWithStatus(status) { + return this.files.filter(function (file) { + return file.status === status; + }).map(function (file) { + return file; + }); + } + + // Returns all files that are in the queue + + }, { + key: "getQueuedFiles", + value: function getQueuedFiles() { return this.getFilesWithStatus(Dropzone.QUEUED); - }; - - Dropzone.prototype.getUploadingFiles = function() { + } + }, { + key: "getUploadingFiles", + value: function getUploadingFiles() { return this.getFilesWithStatus(Dropzone.UPLOADING); - }; + } + }, { + key: "getAddedFiles", + value: function getAddedFiles() { + return this.getFilesWithStatus(Dropzone.ADDED); + } - Dropzone.prototype.getActiveFiles = function() { - var file, _i, _len, _ref, _results; - _ref = this.files; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; - if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) { - _results.push(file); - } - } - return _results; - }; + // Files that are either queued or uploading - Dropzone.prototype.init = function() { - var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1; + }, { + key: "getActiveFiles", + value: function getActiveFiles() { + return this.files.filter(function (file) { + return file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED; + }).map(function (file) { + return file; + }); + } + + // The function that gets called when Dropzone is initialized. You + // can (and should) setup event listeners inside this function. + + }, { + key: "init", + value: function init() { + var _this3 = this; + + // In case it isn't set already if (this.element.tagName === "form") { this.element.setAttribute("enctype", "multipart/form-data"); } + if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) { this.element.appendChild(Dropzone.createElement("
" + this.options.dictDefaultMessage + "
")); } + if (this.clickableElements.length) { - setupHiddenFileInput = (function(_this) { - return function() { - if (_this.hiddenFileInput) { - document.body.removeChild(_this.hiddenFileInput); - } - _this.hiddenFileInput = document.createElement("input"); - _this.hiddenFileInput.setAttribute("type", "file"); - if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) { - _this.hiddenFileInput.setAttribute("multiple", "multiple"); - } - _this.hiddenFileInput.className = "dz-hidden-input"; - if (_this.options.acceptedFiles != null) { - _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles); - } - if (_this.options.capture != null) { - _this.hiddenFileInput.setAttribute("capture", _this.options.capture); - } - _this.hiddenFileInput.style.visibility = "hidden"; - _this.hiddenFileInput.style.position = "absolute"; - _this.hiddenFileInput.style.top = "0"; - _this.hiddenFileInput.style.left = "0"; - _this.hiddenFileInput.style.height = "0"; - _this.hiddenFileInput.style.width = "0"; - document.body.appendChild(_this.hiddenFileInput); - return _this.hiddenFileInput.addEventListener("change", function() { - var file, files, _i, _len; - files = _this.hiddenFileInput.files; - if (files.length) { - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; - _this.addFile(file); + var setupHiddenFileInput = function setupHiddenFileInput() { + if (_this3.hiddenFileInput) { + _this3.hiddenFileInput.parentNode.removeChild(_this3.hiddenFileInput); + } + _this3.hiddenFileInput = document.createElement("input"); + _this3.hiddenFileInput.setAttribute("type", "file"); + if (_this3.options.maxFiles === null || _this3.options.maxFiles > 1) { + _this3.hiddenFileInput.setAttribute("multiple", "multiple"); + } + _this3.hiddenFileInput.className = "dz-hidden-input"; + + if (_this3.options.acceptedFiles !== null) { + _this3.hiddenFileInput.setAttribute("accept", _this3.options.acceptedFiles); + } + if (_this3.options.capture !== null) { + _this3.hiddenFileInput.setAttribute("capture", _this3.options.capture); + } + + // Not setting `display="none"` because some browsers don't accept clicks + // on elements that aren't displayed. + _this3.hiddenFileInput.style.visibility = "hidden"; + _this3.hiddenFileInput.style.position = "absolute"; + _this3.hiddenFileInput.style.top = "0"; + _this3.hiddenFileInput.style.left = "0"; + _this3.hiddenFileInput.style.height = "0"; + _this3.hiddenFileInput.style.width = "0"; + Dropzone.getElement(_this3.options.hiddenInputContainer, 'hiddenInputContainer').appendChild(_this3.hiddenFileInput); + return _this3.hiddenFileInput.addEventListener("change", function () { + var files = _this3.hiddenFileInput.files; + + if (files.length) { + for (var _iterator10 = files, _isArray10 = true, _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref9; + + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref9 = _iterator10[_i10++]; + } else { + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref9 = _i10.value; } + + var file = _ref9; + + _this3.addFile(file); } - return setupHiddenFileInput(); - }); - }; - })(this); + } + _this3.emit("addedfiles", files); + return setupHiddenFileInput(); + }); + }; setupHiddenFileInput(); } - this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL; - _ref1 = this.events; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - eventName = _ref1[_i]; + + this.URL = window.URL !== null ? window.URL : window.webkitURL; + + // Setup all event listeners on the Dropzone object itself. + // They're not in @setupEventListeners() because they shouldn't be removed + // again when the dropzone gets disabled. + for (var _iterator11 = this.events, _isArray11 = true, _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { + var _ref10; + + if (_isArray11) { + if (_i11 >= _iterator11.length) break; + _ref10 = _iterator11[_i11++]; + } else { + _i11 = _iterator11.next(); + if (_i11.done) break; + _ref10 = _i11.value; + } + + var eventName = _ref10; + this.on(eventName, this.options[eventName]); } - this.on("uploadprogress", (function(_this) { - return function() { - return _this.updateTotalUploadProgress(); - }; - })(this)); - this.on("removedfile", (function(_this) { - return function() { - return _this.updateTotalUploadProgress(); - }; - })(this)); - this.on("canceled", (function(_this) { - return function(file) { - return _this.emit("complete", file); - }; - })(this)); - this.on("complete", (function(_this) { - return function(file) { - if (_this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) { - return setTimeout((function() { - return _this.emit("queuecomplete"); - }), 0); - } - }; - })(this)); - noPropagation = function(e) { + + this.on("uploadprogress", function () { + return _this3.updateTotalUploadProgress(); + }); + + this.on("removedfile", function () { + return _this3.updateTotalUploadProgress(); + }); + + this.on("canceled", function (file) { + return _this3.emit("complete", file); + }); + + // Emit a `queuecomplete` event if all files finished uploading. + this.on("complete", function (file) { + if (_this3.getAddedFiles().length === 0 && _this3.getUploadingFiles().length === 0 && _this3.getQueuedFiles().length === 0) { + // This needs to be deferred so that `queuecomplete` really triggers after `complete` + return setTimeout(function () { + return _this3.emit("queuecomplete"); + }, 0); + } + }); + + var noPropagation = function noPropagation(e) { e.stopPropagation(); if (e.preventDefault) { return e.preventDefault(); @@ -607,90 +1309,106 @@ return e.returnValue = false; } }; - this.listeners = [ - { - element: this.element, - events: { - "dragstart": (function(_this) { - return function(e) { - return _this.emit("dragstart", e); - }; - })(this), - "dragenter": (function(_this) { - return function(e) { - noPropagation(e); - return _this.emit("dragenter", e); - }; - })(this), - "dragover": (function(_this) { - return function(e) { - var efct; - try { - efct = e.dataTransfer.effectAllowed; - } catch (_error) {} - e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy'; - noPropagation(e); - return _this.emit("dragover", e); - }; - })(this), - "dragleave": (function(_this) { - return function(e) { - return _this.emit("dragleave", e); - }; - })(this), - "drop": (function(_this) { - return function(e) { - noPropagation(e); - return _this.drop(e); - }; - })(this), - "dragend": (function(_this) { - return function(e) { - return _this.emit("dragend", e); - }; - })(this) - } - } - ]; - this.clickableElements.forEach((function(_this) { - return function(clickableElement) { - return _this.listeners.push({ - element: clickableElement, - events: { - "click": function(evt) { - if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) { - return _this.hiddenFileInput.click(); - } - } - } - }); - }; - })(this)); - this.enable(); - return this.options.init.call(this); - }; - Dropzone.prototype.destroy = function() { - var _ref; + // Create the listeners + this.listeners = [{ + element: this.element, + events: { + "dragstart": function dragstart(e) { + return _this3.emit("dragstart", e); + }, + "dragenter": function dragenter(e) { + noPropagation(e); + return _this3.emit("dragenter", e); + }, + "dragover": function dragover(e) { + // Makes it possible to drag files from chrome's download bar + // http://stackoverflow.com/questions/19526430/drag-and-drop-file-uploads-from-chrome-downloads-bar + // Try is required to prevent bug in Internet Explorer 11 (SCRIPT65535 exception) + var efct = void 0; + try { + efct = e.dataTransfer.effectAllowed; + } catch (error) {} + e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy'; + + noPropagation(e); + return _this3.emit("dragover", e); + }, + "dragleave": function dragleave(e) { + return _this3.emit("dragleave", e); + }, + "drop": function drop(e) { + noPropagation(e); + return _this3.drop(e); + }, + "dragend": function dragend(e) { + return _this3.emit("dragend", e); + } + + // This is disabled right now, because the browsers don't implement it properly. + // "paste": (e) => + // noPropagation e + // @paste e + } }]; + + this.clickableElements.forEach(function (clickableElement) { + return _this3.listeners.push({ + element: clickableElement, + events: { + "click": function click(evt) { + // Only the actual dropzone or the message element should trigger file selection + if (clickableElement !== _this3.element || evt.target === _this3.element || Dropzone.elementInside(evt.target, _this3.element.querySelector(".dz-message"))) { + _this3.hiddenFileInput.click(); // Forward the click + } + return true; + } + } + }); + }); + + this.enable(); + + return this.options.init.call(this); + } + + // Not fully tested yet + + }, { + key: "destroy", + value: function destroy() { this.disable(); this.removeAllFiles(true); - if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) { + if (this.hiddenFileInput != null ? this.hiddenFileInput.parentNode : undefined) { this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); this.hiddenFileInput = null; } delete this.element.dropzone; return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); - }; + } + }, { + key: "updateTotalUploadProgress", + value: function updateTotalUploadProgress() { + var totalUploadProgress = void 0; + var totalBytesSent = 0; + var totalBytes = 0; + + var activeFiles = this.getActiveFiles(); - Dropzone.prototype.updateTotalUploadProgress = function() { - var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref; - totalBytesSent = 0; - totalBytes = 0; - activeFiles = this.getActiveFiles(); if (activeFiles.length) { - _ref = this.getActiveFiles(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; + for (var _iterator12 = this.getActiveFiles(), _isArray12 = true, _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { + var _ref11; + + if (_isArray12) { + if (_i12 >= _iterator12.length) break; + _ref11 = _iterator12[_i12++]; + } else { + _i12 = _iterator12.next(); + if (_i12.done) break; + _ref11 = _i12.value; + } + + var file = _ref11; + totalBytesSent += file.upload.bytesSent; totalBytes += file.upload.total; } @@ -698,139 +1416,200 @@ } else { totalUploadProgress = 100; } - return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent); - }; - Dropzone.prototype._getParamName = function(n) { + return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent); + } + + // @options.paramName can be a function taking one parameter rather than a string. + // A parameter name for a file is obtained simply by calling this with an index number. + + }, { + key: "_getParamName", + value: function _getParamName(n) { if (typeof this.options.paramName === "function") { return this.options.paramName(n); } else { return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : ""); } - }; + } - Dropzone.prototype.getFallbackForm = function() { - var existingFallback, fields, fieldsString, form; + // If @options.renameFile is a function, + // the function will be used to rename the file.name before appending it to the formData + + }, { + key: "_renameFile", + value: function _renameFile(file) { + if (typeof this.options.renameFile !== "function") { + return file.name; + } + return this.options.renameFile(file); + } + + // Returns a form that can be used as fallback if the browser does not support DragnDrop + // + // If the dropzone is already a form, only the input field and button are returned. Otherwise a complete form element is provided. + // This code has to pass in IE7 :( + + }, { + key: "getFallbackForm", + value: function getFallbackForm() { + var existingFallback = void 0, + form = void 0; if (existingFallback = this.getExistingFallback()) { return existingFallback; } - fieldsString = "
"; + + var fieldsString = "
"; if (this.options.dictFallbackText) { fieldsString += "

" + this.options.dictFallbackText + "

"; } - fieldsString += "
"; - fields = Dropzone.createElement(fieldsString); + fieldsString += "
"; + + var fields = Dropzone.createElement(fieldsString); if (this.element.tagName !== "FORM") { form = Dropzone.createElement("
"); form.appendChild(fields); } else { + // Make sure that the enctype and method attributes are set properly this.element.setAttribute("enctype", "multipart/form-data"); this.element.setAttribute("method", this.options.method); } return form != null ? form : fields; - }; + } + + // Returns the fallback elements if they exist already + // + // This code has to pass in IE7 :( + + }, { + key: "getExistingFallback", + value: function getExistingFallback() { + var getFallback = function getFallback(elements) { + for (var _iterator13 = elements, _isArray13 = true, _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { + var _ref12; + + if (_isArray13) { + if (_i13 >= _iterator13.length) break; + _ref12 = _iterator13[_i13++]; + } else { + _i13 = _iterator13.next(); + if (_i13.done) break; + _ref12 = _i13.value; + } + + var el = _ref12; - Dropzone.prototype.getExistingFallback = function() { - var fallback, getFallback, tagName, _i, _len, _ref; - getFallback = function(elements) { - var el, _i, _len; - for (_i = 0, _len = elements.length; _i < _len; _i++) { - el = elements[_i]; if (/(^| )fallback($| )/.test(el.className)) { return el; } } }; - _ref = ["div", "form"]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - tagName = _ref[_i]; + + var _arr = ["div", "form"]; + for (var _i14 = 0; _i14 < _arr.length; _i14++) { + var tagName = _arr[_i14]; + var fallback; if (fallback = getFallback(this.element.getElementsByTagName(tagName))) { return fallback; } } - }; + } - Dropzone.prototype.setupEventListeners = function() { - var elementListeners, event, listener, _i, _len, _ref, _results; - _ref = this.listeners; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - elementListeners = _ref[_i]; - _results.push((function() { - var _ref1, _results1; - _ref1 = elementListeners.events; - _results1 = []; - for (event in _ref1) { - listener = _ref1[event]; - _results1.push(elementListeners.element.addEventListener(event, listener, false)); + // Activates all listeners stored in @listeners + + }, { + key: "setupEventListeners", + value: function setupEventListeners() { + return this.listeners.map(function (elementListeners) { + return function () { + var result = []; + for (var event in elementListeners.events) { + var listener = elementListeners.events[event]; + result.push(elementListeners.element.addEventListener(event, listener, false)); } - return _results1; - })()); - } - return _results; - }; + return result; + }(); + }); + } - Dropzone.prototype.removeEventListeners = function() { - var elementListeners, event, listener, _i, _len, _ref, _results; - _ref = this.listeners; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - elementListeners = _ref[_i]; - _results.push((function() { - var _ref1, _results1; - _ref1 = elementListeners.events; - _results1 = []; - for (event in _ref1) { - listener = _ref1[event]; - _results1.push(elementListeners.element.removeEventListener(event, listener, false)); + // Deactivates all listeners stored in @listeners + + }, { + key: "removeEventListeners", + value: function removeEventListeners() { + return this.listeners.map(function (elementListeners) { + return function () { + var result = []; + for (var event in elementListeners.events) { + var listener = elementListeners.events[event]; + result.push(elementListeners.element.removeEventListener(event, listener, false)); } - return _results1; - })()); - } - return _results; - }; + return result; + }(); + }); + } - Dropzone.prototype.disable = function() { - var file, _i, _len, _ref, _results; - this.clickableElements.forEach(function(element) { + // Removes all event listeners and cancels all files in the queue or being processed. + + }, { + key: "disable", + value: function disable() { + var _this4 = this; + + this.clickableElements.forEach(function (element) { return element.classList.remove("dz-clickable"); }); this.removeEventListeners(); - _ref = this.files; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; - _results.push(this.cancelUpload(file)); - } - return _results; - }; + this.disabled = true; - Dropzone.prototype.enable = function() { - this.clickableElements.forEach(function(element) { + return this.files.map(function (file) { + return _this4.cancelUpload(file); + }); + } + }, { + key: "enable", + value: function enable() { + delete this.disabled; + this.clickableElements.forEach(function (element) { return element.classList.add("dz-clickable"); }); return this.setupEventListeners(); - }; + } - Dropzone.prototype.filesize = function(size) { - var cutoff, i, selectedSize, selectedUnit, unit, units, _i, _len; - units = ['TB', 'GB', 'MB', 'KB', 'b']; - selectedSize = selectedUnit = null; - for (i = _i = 0, _len = units.length; _i < _len; i = ++_i) { - unit = units[i]; - cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; - if (size >= cutoff) { - selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); - selectedUnit = unit; - break; + // Returns a nicely formatted filesize + + }, { + key: "filesize", + value: function filesize(size) { + var selectedSize = 0; + var selectedUnit = "b"; + + if (size > 0) { + var units = ['tb', 'gb', 'mb', 'kb', 'b']; + + for (var i = 0; i < units.length; i++) { + var unit = units[i]; + var cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; + + if (size >= cutoff) { + selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); + selectedUnit = unit; + break; + } } - } - selectedSize = Math.round(10 * selectedSize) / 10; - return "" + selectedSize + " " + selectedUnit; - }; - Dropzone.prototype._updateMaxFilesReachedClass = function() { - if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + selectedSize = Math.round(10 * selectedSize) / 10; // Cutting of digits + } + + return "" + selectedSize + " " + this.options.dictFileSizeUnits[selectedUnit]; + } + + // Adds or removes the `dz-max-files-reached` class from the form. + + }, { + key: "_updateMaxFilesReachedClass", + value: function _updateMaxFilesReachedClass() { + if (this.options.maxFiles != null && this.getAcceptedFiles().length >= this.options.maxFiles) { if (this.getAcceptedFiles().length === this.options.maxFiles) { this.emit('maxfilesreached', this.files); } @@ -838,519 +1617,1195 @@ } else { return this.element.classList.remove("dz-max-files-reached"); } - }; - - Dropzone.prototype.drop = function(e) { - var files, items; + } + }, { + key: "drop", + value: function drop(e) { if (!e.dataTransfer) { return; } this.emit("drop", e); - files = e.dataTransfer.files; + + // Convert the FileList to an Array + // This is necessary for IE11 + var files = []; + for (var i = 0; i < e.dataTransfer.files.length; i++) { + files[i] = e.dataTransfer.files[i]; + } + + this.emit("addedfiles", files); + + // Even if it's a folder, files.length will contain the folders. if (files.length) { - items = e.dataTransfer.items; - if (items && items.length && (items[0].webkitGetAsEntry != null)) { + var items = e.dataTransfer.items; + + if (items && items.length && items[0].webkitGetAsEntry != null) { + // The browser supports dropping of folders, so handle items instead of files this._addFilesFromItems(items); } else { this.handleFiles(files); } } - }; - - Dropzone.prototype.paste = function(e) { - var items, _ref; - if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) { + } + }, { + key: "paste", + value: function paste(e) { + if (__guard__(e != null ? e.clipboardData : undefined, function (x) { + return x.items; + }) == null) { return; } + this.emit("paste", e); - items = e.clipboardData.items; + var items = e.clipboardData.items; + + if (items.length) { return this._addFilesFromItems(items); } - }; + } + }, { + key: "handleFiles", + value: function handleFiles(files) { + for (var _iterator14 = files, _isArray14 = true, _i15 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { + var _ref13; - Dropzone.prototype.handleFiles = function(files) { - var file, _i, _len, _results; - _results = []; - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; - _results.push(this.addFile(file)); - } - return _results; - }; - - Dropzone.prototype._addFilesFromItems = function(items) { - var entry, item, _i, _len, _results; - _results = []; - for (_i = 0, _len = items.length; _i < _len; _i++) { - item = items[_i]; - if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) { - if (entry.isFile) { - _results.push(this.addFile(item.getAsFile())); - } else if (entry.isDirectory) { - _results.push(this._addFilesFromDirectory(entry, entry.name)); - } else { - _results.push(void 0); - } - } else if (item.getAsFile != null) { - if ((item.kind == null) || item.kind === "file") { - _results.push(this.addFile(item.getAsFile())); - } else { - _results.push(void 0); - } + if (_isArray14) { + if (_i15 >= _iterator14.length) break; + _ref13 = _iterator14[_i15++]; } else { - _results.push(void 0); + _i15 = _iterator14.next(); + if (_i15.done) break; + _ref13 = _i15.value; } + + var file = _ref13; + + this.addFile(file); } - return _results; - }; + } - Dropzone.prototype._addFilesFromDirectory = function(directory, path) { - var dirReader, entriesReader; - dirReader = directory.createReader(); - entriesReader = (function(_this) { - return function(entries) { - var entry, _i, _len; - for (_i = 0, _len = entries.length; _i < _len; _i++) { - entry = entries[_i]; - if (entry.isFile) { - entry.file(function(file) { - if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') { - return; - } - file.fullPath = "" + path + "/" + file.name; - return _this.addFile(file); - }); - } else if (entry.isDirectory) { - _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name); - } + // When a folder is dropped (or files are pasted), items must be handled + // instead of files. + + }, { + key: "_addFilesFromItems", + value: function _addFilesFromItems(items) { + var _this5 = this; + + return function () { + var result = []; + for (var _iterator15 = items, _isArray15 = true, _i16 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { + var _ref14; + + if (_isArray15) { + if (_i16 >= _iterator15.length) break; + _ref14 = _iterator15[_i16++]; + } else { + _i16 = _iterator15.next(); + if (_i16.done) break; + _ref14 = _i16.value; } - }; - })(this); - return dirReader.readEntries(entriesReader, function(error) { - return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0; - }); - }; - Dropzone.prototype.accept = function(file, done) { - if (file.size > this.options.maxFilesize * 1024 * 1024) { + var item = _ref14; + + var entry; + if (item.webkitGetAsEntry != null && (entry = item.webkitGetAsEntry())) { + if (entry.isFile) { + result.push(_this5.addFile(item.getAsFile())); + } else if (entry.isDirectory) { + // Append all files from that directory to files + result.push(_this5._addFilesFromDirectory(entry, entry.name)); + } else { + result.push(undefined); + } + } else if (item.getAsFile != null) { + if (item.kind == null || item.kind === "file") { + result.push(_this5.addFile(item.getAsFile())); + } else { + result.push(undefined); + } + } else { + result.push(undefined); + } + } + return result; + }(); + } + + // Goes through the directory, and adds each file it finds recursively + + }, { + key: "_addFilesFromDirectory", + value: function _addFilesFromDirectory(directory, path) { + var _this6 = this; + + var dirReader = directory.createReader(); + + var errorHandler = function errorHandler(error) { + return __guardMethod__(console, 'log', function (o) { + return o.log(error); + }); + }; + + var readEntries = function readEntries() { + return dirReader.readEntries(function (entries) { + if (entries.length > 0) { + for (var _iterator16 = entries, _isArray16 = true, _i17 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { + var _ref15; + + if (_isArray16) { + if (_i17 >= _iterator16.length) break; + _ref15 = _iterator16[_i17++]; + } else { + _i17 = _iterator16.next(); + if (_i17.done) break; + _ref15 = _i17.value; + } + + var entry = _ref15; + + if (entry.isFile) { + entry.file(function (file) { + if (_this6.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') { + return; + } + file.fullPath = path + "/" + file.name; + return _this6.addFile(file); + }); + } else if (entry.isDirectory) { + _this6._addFilesFromDirectory(entry, path + "/" + entry.name); + } + } + + // Recursively call readEntries() again, since browser only handle + // the first 100 entries. + // See: https://developer.mozilla.org/en-US/docs/Web/API/DirectoryReader#readEntries + readEntries(); + } + return null; + }, errorHandler); + }; + + return readEntries(); + } + + // If `done()` is called without argument the file is accepted + // If you call it with an error message, the file is rejected + // (This allows for asynchronous validation) + // + // This function checks the filesize, and if the file.type passes the + // `acceptedFiles` check. + + }, { + key: "accept", + value: function accept(file, done) { + if (this.options.maxFilesize && file.size > this.options.maxFilesize * 1024 * 1024) { return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize)); } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) { return done(this.options.dictInvalidFileType); - } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + } else if (this.options.maxFiles != null && this.getAcceptedFiles().length >= this.options.maxFiles) { done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles)); return this.emit("maxfilesexceeded", file); } else { return this.options.accept.call(this, file, done); } - }; + } + + }, { + key: "addFile", + value: function addFile(file) { + var _this7 = this; - Dropzone.prototype.addFile = function(file) { file.upload = { + uuid: Dropzone.uuidv4(), progress: 0, + // Setting the total upload size to file.size for the beginning + // It's actual different than the size to be transmitted. total: file.size, - bytesSent: 0 + bytesSent: 0, + filename: this._renameFile(file), + chunked: this.options.chunking && (this.options.forceChunking || file.size > this.options.chunkSize), + totalChunkCount: Math.ceil(file.size / this.options.chunkSize) }; this.files.push(file); - file.status = Dropzone.ADDED; - this.emit("addedfile", file); - this._enqueueThumbnail(file); - return this.accept(file, (function(_this) { - return function(error) { - if (error) { - file.accepted = false; - _this._errorProcessing([file], error); - } else { - file.accepted = true; - if (_this.options.autoQueue) { - _this.enqueueFile(file); - } - } - return _this._updateMaxFilesReachedClass(); - }; - })(this)); - }; - Dropzone.prototype.enqueueFiles = function(files) { - var file, _i, _len; - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; + file.status = Dropzone.ADDED; + + this.emit("addedfile", file); + + this._enqueueThumbnail(file); + + return this.accept(file, function (error) { + if (error) { + file.accepted = false; + _this7._errorProcessing([file], error); // Will set the file.status + } else { + file.accepted = true; + if (_this7.options.autoQueue) { + _this7.enqueueFile(file); + } // Will set .accepted = true + } + return _this7._updateMaxFilesReachedClass(); + }); + } + + // Wrapper for enqueueFile + + }, { + key: "enqueueFiles", + value: function enqueueFiles(files) { + for (var _iterator17 = files, _isArray17 = true, _i18 = 0, _iterator17 = _isArray17 ? _iterator17 : _iterator17[Symbol.iterator]();;) { + var _ref16; + + if (_isArray17) { + if (_i18 >= _iterator17.length) break; + _ref16 = _iterator17[_i18++]; + } else { + _i18 = _iterator17.next(); + if (_i18.done) break; + _ref16 = _i18.value; + } + + var file = _ref16; + this.enqueueFile(file); } return null; - }; + } + }, { + key: "enqueueFile", + value: function enqueueFile(file) { + var _this8 = this; - Dropzone.prototype.enqueueFile = function(file) { if (file.status === Dropzone.ADDED && file.accepted === true) { file.status = Dropzone.QUEUED; if (this.options.autoProcessQueue) { - return setTimeout(((function(_this) { - return function() { - return _this.processQueue(); - }; - })(this)), 0); + return setTimeout(function () { + return _this8.processQueue(); + }, 0); // Deferring the call } } else { throw new Error("This file can't be queued because it has already been processed or was rejected."); } - }; + } + }, { + key: "_enqueueThumbnail", + value: function _enqueueThumbnail(file) { + var _this9 = this; - Dropzone.prototype._thumbnailQueue = []; - - Dropzone.prototype._processingThumbnail = false; - - Dropzone.prototype._enqueueThumbnail = function(file) { if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) { this._thumbnailQueue.push(file); - return setTimeout(((function(_this) { - return function() { - return _this._processThumbnailQueue(); - }; - })(this)), 0); + return setTimeout(function () { + return _this9._processThumbnailQueue(); + }, 0); // Deferring the call } - }; + } + }, { + key: "_processThumbnailQueue", + value: function _processThumbnailQueue() { + var _this10 = this; - Dropzone.prototype._processThumbnailQueue = function() { if (this._processingThumbnail || this._thumbnailQueue.length === 0) { return; } - this._processingThumbnail = true; - return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) { - return function() { - _this._processingThumbnail = false; - return _this._processThumbnailQueue(); - }; - })(this)); - }; - Dropzone.prototype.removeFile = function(file) { + this._processingThumbnail = true; + var file = this._thumbnailQueue.shift(); + return this.createThumbnail(file, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, true, function (dataUrl) { + _this10.emit("thumbnail", file, dataUrl); + _this10._processingThumbnail = false; + return _this10._processThumbnailQueue(); + }); + } + + // Can be called by the user to remove a file + + }, { + key: "removeFile", + value: function removeFile(file) { if (file.status === Dropzone.UPLOADING) { this.cancelUpload(file); } this.files = without(this.files, file); + this.emit("removedfile", file); if (this.files.length === 0) { return this.emit("reset"); } - }; + } - Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) { - var file, _i, _len, _ref; + // Removes all files that aren't currently processed from the list + + }, { + key: "removeAllFiles", + value: function removeAllFiles(cancelIfNecessary) { + // Create a copy of files since removeFile() changes the @files array. if (cancelIfNecessary == null) { cancelIfNecessary = false; } - _ref = this.files.slice(); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; + for (var _iterator18 = this.files.slice(), _isArray18 = true, _i19 = 0, _iterator18 = _isArray18 ? _iterator18 : _iterator18[Symbol.iterator]();;) { + var _ref17; + + if (_isArray18) { + if (_i19 >= _iterator18.length) break; + _ref17 = _iterator18[_i19++]; + } else { + _i19 = _iterator18.next(); + if (_i19.done) break; + _ref17 = _i19.value; + } + + var file = _ref17; + if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) { this.removeFile(file); } } return null; - }; + } - Dropzone.prototype.createThumbnail = function(file, callback) { - var fileReader; - fileReader = new FileReader; - fileReader.onload = (function(_this) { - return function() { - if (file.type === "image/svg+xml") { - _this.emit("thumbnail", file, fileReader.result); - if (callback != null) { - callback(); - } - return; + // Resizes an image before it gets sent to the server. This function is the default behavior of + // `options.transformFile` if `resizeWidth` or `resizeHeight` are set. The callback is invoked with + // the resized blob. + + }, { + key: "resizeImage", + value: function resizeImage(file, width, height, resizeMethod, callback) { + var _this11 = this; + + return this.createThumbnail(file, width, height, resizeMethod, true, function (dataUrl, canvas) { + if (canvas == null) { + // The image has not been resized + return callback(file); + } else { + var resizeMimeType = _this11.options.resizeMimeType; + + if (resizeMimeType == null) { + resizeMimeType = file.type; } - return _this.createThumbnailFromUrl(file, fileReader.result, callback); - }; - })(this); - return fileReader.readAsDataURL(file); - }; + var resizedDataURL = canvas.toDataURL(resizeMimeType, _this11.options.resizeQuality); + if (resizeMimeType === 'image/jpeg' || resizeMimeType === 'image/jpg') { + // Now add the original EXIF information + resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL); + } + return callback(Dropzone.dataURItoBlob(resizedDataURL)); + } + }); + } + }, { + key: "createThumbnail", + value: function createThumbnail(file, width, height, resizeMethod, fixOrientation, callback) { + var _this12 = this; - Dropzone.prototype.createThumbnailFromUrl = function(file, imageUrl, callback) { - var img; - img = document.createElement("img"); - img.onload = (function(_this) { - return function() { - var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3; + var fileReader = new FileReader(); + + fileReader.onload = function () { + + file.dataURL = fileReader.result; + + // Don't bother creating a thumbnail for SVG images since they're vector + if (file.type === "image/svg+xml") { + if (callback != null) { + callback(fileReader.result); + } + return; + } + + return _this12.createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback); + }; + + return fileReader.readAsDataURL(file); + } + }, { + key: "createThumbnailFromUrl", + value: function createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback, crossOrigin) { + var _this13 = this; + + // Not using `new Image` here because of a bug in latest Chrome versions. + // See https://github.com/enyo/dropzone/pull/226 + var img = document.createElement("img"); + + if (crossOrigin) { + img.crossOrigin = crossOrigin; + } + + img.onload = function () { + var loadExif = function loadExif(callback) { + return callback(1); + }; + if (typeof EXIF !== 'undefined' && EXIF !== null && fixOrientation) { + loadExif = function loadExif(callback) { + return EXIF.getData(img, function () { + return callback(EXIF.getTag(this, 'Orientation')); + }); + }; + } + + return loadExif(function (orientation) { file.width = img.width; file.height = img.height; - resizeInfo = _this.options.resize.call(_this, file); - if (resizeInfo.trgWidth == null) { - resizeInfo.trgWidth = resizeInfo.optWidth; - } - if (resizeInfo.trgHeight == null) { - resizeInfo.trgHeight = resizeInfo.optHeight; - } - canvas = document.createElement("canvas"); - ctx = canvas.getContext("2d"); + + var resizeInfo = _this13.options.resize.call(_this13, file, width, height, resizeMethod); + + var canvas = document.createElement("canvas"); + var ctx = canvas.getContext("2d"); + canvas.width = resizeInfo.trgWidth; canvas.height = resizeInfo.trgHeight; - drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); - thumbnail = canvas.toDataURL("image/png"); - _this.emit("thumbnail", file, thumbnail); - if (callback != null) { - return callback(); + + if (orientation > 4) { + canvas.width = resizeInfo.trgHeight; + canvas.height = resizeInfo.trgWidth; } - }; - })(this); + + switch (orientation) { + case 2: + // horizontal flip + ctx.translate(canvas.width, 0); + ctx.scale(-1, 1); + break; + case 3: + // 180° rotate left + ctx.translate(canvas.width, canvas.height); + ctx.rotate(Math.PI); + break; + case 4: + // vertical flip + ctx.translate(0, canvas.height); + ctx.scale(1, -1); + break; + case 5: + // vertical flip + 90 rotate right + ctx.rotate(0.5 * Math.PI); + ctx.scale(1, -1); + break; + case 6: + // 90° rotate right + ctx.rotate(0.5 * Math.PI); + ctx.translate(0, -canvas.width); + break; + case 7: + // horizontal flip + 90 rotate right + ctx.rotate(0.5 * Math.PI); + ctx.translate(canvas.height, -canvas.width); + ctx.scale(-1, 1); + break; + case 8: + // 90° rotate left + ctx.rotate(-0.5 * Math.PI); + ctx.translate(-canvas.height, 0); + break; + } + + // This is a bugfix for iOS' scaling bug. + drawImageIOSFix(ctx, img, resizeInfo.srcX != null ? resizeInfo.srcX : 0, resizeInfo.srcY != null ? resizeInfo.srcY : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, resizeInfo.trgX != null ? resizeInfo.trgX : 0, resizeInfo.trgY != null ? resizeInfo.trgY : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); + + var thumbnail = canvas.toDataURL("image/png"); + + if (callback != null) { + return callback(thumbnail, canvas); + } + }); + }; + if (callback != null) { img.onerror = callback; } - return img.src = imageUrl; - }; - Dropzone.prototype.processQueue = function() { - var i, parallelUploads, processingLength, queuedFiles; - parallelUploads = this.options.parallelUploads; - processingLength = this.getUploadingFiles().length; - i = processingLength; + return img.src = file.dataURL; + } + + // Goes through the queue and processes files if there aren't too many already. + + }, { + key: "processQueue", + value: function processQueue() { + var parallelUploads = this.options.parallelUploads; + + var processingLength = this.getUploadingFiles().length; + var i = processingLength; + + // There are already at least as many files uploading than should be if (processingLength >= parallelUploads) { return; } - queuedFiles = this.getQueuedFiles(); + + var queuedFiles = this.getQueuedFiles(); + if (!(queuedFiles.length > 0)) { return; } + if (this.options.uploadMultiple) { + // The files should be uploaded in one request return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength)); } else { while (i < parallelUploads) { if (!queuedFiles.length) { return; - } + } // Nothing left to process this.processFile(queuedFiles.shift()); i++; } } - }; + } - Dropzone.prototype.processFile = function(file) { + // Wrapper for `processFiles` + + }, { + key: "processFile", + value: function processFile(file) { return this.processFiles([file]); - }; + } - Dropzone.prototype.processFiles = function(files) { - var file, _i, _len; - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; - file.processing = true; + // Loads the file, then calls finishedLoading() + + }, { + key: "processFiles", + value: function processFiles(files) { + for (var _iterator19 = files, _isArray19 = true, _i20 = 0, _iterator19 = _isArray19 ? _iterator19 : _iterator19[Symbol.iterator]();;) { + var _ref18; + + if (_isArray19) { + if (_i20 >= _iterator19.length) break; + _ref18 = _iterator19[_i20++]; + } else { + _i20 = _iterator19.next(); + if (_i20.done) break; + _ref18 = _i20.value; + } + + var file = _ref18; + + file.processing = true; // Backwards compatibility file.status = Dropzone.UPLOADING; + this.emit("processing", file); } + if (this.options.uploadMultiple) { this.emit("processingmultiple", files); } + return this.uploadFiles(files); - }; + } + }, { + key: "_getFilesWithXhr", + value: function _getFilesWithXhr(xhr) { + var files = void 0; + return files = this.files.filter(function (file) { + return file.xhr === xhr; + }).map(function (file) { + return file; + }); + } - Dropzone.prototype._getFilesWithXhr = function(xhr) { - var file, files; - return files = (function() { - var _i, _len, _ref, _results; - _ref = this.files; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - file = _ref[_i]; - if (file.xhr === xhr) { - _results.push(file); - } - } - return _results; - }).call(this); - }; + // Cancels the file upload and sets the status to CANCELED + // **if** the file is actually being uploaded. + // If it's still in the queue, the file is being removed from it and the status + // set to CANCELED. - Dropzone.prototype.cancelUpload = function(file) { - var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref; + }, { + key: "cancelUpload", + value: function cancelUpload(file) { if (file.status === Dropzone.UPLOADING) { - groupedFiles = this._getFilesWithXhr(file.xhr); - for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) { - groupedFile = groupedFiles[_i]; + var groupedFiles = this._getFilesWithXhr(file.xhr); + for (var _iterator20 = groupedFiles, _isArray20 = true, _i21 = 0, _iterator20 = _isArray20 ? _iterator20 : _iterator20[Symbol.iterator]();;) { + var _ref19; + + if (_isArray20) { + if (_i21 >= _iterator20.length) break; + _ref19 = _iterator20[_i21++]; + } else { + _i21 = _iterator20.next(); + if (_i21.done) break; + _ref19 = _i21.value; + } + + var groupedFile = _ref19; + groupedFile.status = Dropzone.CANCELED; } - file.xhr.abort(); - for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) { - groupedFile = groupedFiles[_j]; - this.emit("canceled", groupedFile); + if (typeof file.xhr !== 'undefined') { + file.xhr.abort(); + } + for (var _iterator21 = groupedFiles, _isArray21 = true, _i22 = 0, _iterator21 = _isArray21 ? _iterator21 : _iterator21[Symbol.iterator]();;) { + var _ref20; + + if (_isArray21) { + if (_i22 >= _iterator21.length) break; + _ref20 = _iterator21[_i22++]; + } else { + _i22 = _iterator21.next(); + if (_i22.done) break; + _ref20 = _i22.value; + } + + var _groupedFile = _ref20; + + this.emit("canceled", _groupedFile); } if (this.options.uploadMultiple) { this.emit("canceledmultiple", groupedFiles); } - } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) { + } else if (file.status === Dropzone.ADDED || file.status === Dropzone.QUEUED) { file.status = Dropzone.CANCELED; this.emit("canceled", file); if (this.options.uploadMultiple) { this.emit("canceledmultiple", [file]); } } + if (this.options.autoProcessQueue) { return this.processQueue(); } - }; - - resolveOption = function() { - var args, option; - option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + } + }, { + key: "resolveOption", + value: function resolveOption(option) { if (typeof option === 'function') { + for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { + args[_key3 - 1] = arguments[_key3]; + } + return option.apply(this, args); } return option; - }; - - Dropzone.prototype.uploadFile = function(file) { + } + }, { + key: "uploadFile", + value: function uploadFile(file) { return this.uploadFiles([file]); - }; + } + }, { + key: "uploadFiles", + value: function uploadFiles(files) { + var _this14 = this; + + this._transformFiles(files, function (transformedFiles) { + if (files[0].upload.chunked) { + // This file should be sent in chunks! + + // If the chunking option is set, we **know** that there can only be **one** file, since + // uploadMultiple is not allowed with this option. + var file = files[0]; + var transformedFile = transformedFiles[0]; + var startedChunkCount = 0; + + file.upload.chunks = []; + + var handleNextChunk = function handleNextChunk() { + var chunkIndex = 0; + + // Find the next item in file.upload.chunks that is not defined yet. + while (file.upload.chunks[chunkIndex] !== undefined) { + chunkIndex++; + } + + // This means, that all chunks have already been started. + if (chunkIndex >= file.upload.totalChunkCount) return; + + startedChunkCount++; + + var start = chunkIndex * _this14.options.chunkSize; + var end = Math.min(start + _this14.options.chunkSize, file.size); + + var dataBlock = { + name: _this14._getParamName(0), + data: transformedFile.webkitSlice ? transformedFile.webkitSlice(start, end) : transformedFile.slice(start, end), + filename: file.upload.filename, + chunkIndex: chunkIndex + }; + + file.upload.chunks[chunkIndex] = { + file: file, + index: chunkIndex, + dataBlock: dataBlock, // In case we want to retry. + status: Dropzone.UPLOADING, + progress: 0, + retries: 0 // The number of times this block has been retried. + }; + + _this14._uploadData(files, [dataBlock]); + }; + + file.upload.finishedChunkUpload = function (chunk) { + var allFinished = true; + chunk.status = Dropzone.SUCCESS; + + // Clear the data from the chunk + chunk.dataBlock = null; + // Leaving this reference to xhr intact here will cause memory leaks in some browsers + chunk.xhr = null; + + for (var i = 0; i < file.upload.totalChunkCount; i++) { + if (file.upload.chunks[i] === undefined) { + return handleNextChunk(); + } + if (file.upload.chunks[i].status !== Dropzone.SUCCESS) { + allFinished = false; + } + } + + if (allFinished) { + _this14.options.chunksUploaded(file, function () { + _this14._finished(files, '', null); + }); + } + }; + + if (_this14.options.parallelChunkUploads) { + for (var i = 0; i < file.upload.totalChunkCount; i++) { + handleNextChunk(); + } + } else { + handleNextChunk(); + } + } else { + var dataBlocks = []; + for (var _i23 = 0; _i23 < files.length; _i23++) { + dataBlocks[_i23] = { + name: _this14._getParamName(_i23), + data: transformedFiles[_i23], + filename: files[_i23].upload.filename + }; + } + _this14._uploadData(files, dataBlocks); + } + }); + } + + /// Returns the right chunk for given file and xhr + + }, { + key: "_getChunk", + value: function _getChunk(file, xhr) { + for (var i = 0; i < file.upload.totalChunkCount; i++) { + if (file.upload.chunks[i] !== undefined && file.upload.chunks[i].xhr === xhr) { + return file.upload.chunks[i]; + } + } + } + + // This function actually uploads the file(s) to the server. + // If dataBlocks contains the actual data to upload (meaning, that this could either be transformed + // files, or individual chunks for chunked upload). + + }, { + key: "_uploadData", + value: function _uploadData(files, dataBlocks) { + var _this15 = this; + + var xhr = new XMLHttpRequest(); + + // Put the xhr object in the file objects to be able to reference it later. + for (var _iterator22 = files, _isArray22 = true, _i24 = 0, _iterator22 = _isArray22 ? _iterator22 : _iterator22[Symbol.iterator]();;) { + var _ref21; + + if (_isArray22) { + if (_i24 >= _iterator22.length) break; + _ref21 = _iterator22[_i24++]; + } else { + _i24 = _iterator22.next(); + if (_i24.done) break; + _ref21 = _i24.value; + } + + var file = _ref21; - Dropzone.prototype.uploadFiles = function(files) { - var file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, key, method, option, progressObj, response, updateProgress, url, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; - xhr = new XMLHttpRequest(); - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; file.xhr = xhr; } - method = resolveOption(this.options.method, files); - url = resolveOption(this.options.url, files); + if (files[0].upload.chunked) { + // Put the xhr object in the right chunk object, so it can be associated later, and found with _getChunk + files[0].upload.chunks[dataBlocks[0].chunkIndex].xhr = xhr; + } + + var method = this.resolveOption(this.options.method, files); + var url = this.resolveOption(this.options.url, files); xhr.open(method, url, true); + + // Setting the timeout after open because of IE11 issue: https://gitlab.com/meno/dropzone/issues/8 + xhr.timeout = this.resolveOption(this.options.timeout, files); + + // Has to be after `.open()`. See https://github.com/enyo/dropzone/issues/179 xhr.withCredentials = !!this.options.withCredentials; - response = null; - handleError = (function(_this) { - return function() { - var _j, _len1, _results; - _results = []; - for (_j = 0, _len1 = files.length; _j < _len1; _j++) { - file = files[_j]; - _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr)); - } - return _results; - }; - })(this); - updateProgress = (function(_this) { - return function(e) { - var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results; - if (e != null) { - progress = 100 * e.loaded / e.total; - for (_j = 0, _len1 = files.length; _j < _len1; _j++) { - file = files[_j]; - file.upload = { - progress: progress, - total: e.total, - bytesSent: e.loaded - }; - } - } else { - allFilesFinished = true; - progress = 100; - for (_k = 0, _len2 = files.length; _k < _len2; _k++) { - file = files[_k]; - if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) { - allFilesFinished = false; - } - file.upload.progress = progress; - file.upload.bytesSent = file.upload.total; - } - if (allFilesFinished) { - return; - } - } - _results = []; - for (_l = 0, _len3 = files.length; _l < _len3; _l++) { - file = files[_l]; - _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent)); - } - return _results; - }; - })(this); - xhr.onload = (function(_this) { - return function(e) { - var _ref; - if (files[0].status === Dropzone.CANCELED) { - return; - } - if (xhr.readyState !== 4) { - return; - } - response = xhr.responseText; - if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) { - try { - response = JSON.parse(response); - } catch (_error) { - e = _error; - response = "Invalid JSON response from server."; - } - } - updateProgress(); - if (!((200 <= (_ref = xhr.status) && _ref < 300))) { - return handleError(); - } else { - return _this._finished(files, response, e); - } - }; - })(this); - xhr.onerror = (function(_this) { - return function() { - if (files[0].status === Dropzone.CANCELED) { - return; - } - return handleError(); - }; - })(this); - progressObj = (_ref = xhr.upload) != null ? _ref : xhr; - progressObj.onprogress = updateProgress; - headers = { + + xhr.onload = function (e) { + _this15._finishedUploading(files, xhr, e); + }; + + xhr.onerror = function () { + _this15._handleUploadError(files, xhr); + }; + + // Some browsers do not have the .upload property + var progressObj = xhr.upload != null ? xhr.upload : xhr; + progressObj.onprogress = function (e) { + return _this15._updateFilesUploadProgress(files, xhr, e); + }; + + var headers = { "Accept": "application/json", "Cache-Control": "no-cache", "X-Requested-With": "XMLHttpRequest" }; + if (this.options.headers) { - extend(headers, this.options.headers); + Dropzone.extend(headers, this.options.headers); } - for (headerName in headers) { - headerValue = headers[headerName]; - xhr.setRequestHeader(headerName, headerValue); + + for (var headerName in headers) { + var headerValue = headers[headerName]; + if (headerValue) { + xhr.setRequestHeader(headerName, headerValue); + } } - formData = new FormData(); + + var formData = new FormData(); + + // Adding all @options parameters if (this.options.params) { - _ref1 = this.options.params; - for (key in _ref1) { - value = _ref1[key]; + var additionalParams = this.options.params; + if (typeof additionalParams === 'function') { + additionalParams = additionalParams.call(this, files, xhr, files[0].upload.chunked ? this._getChunk(files[0], xhr) : null); + } + + for (var key in additionalParams) { + var value = additionalParams[key]; formData.append(key, value); } } - for (_j = 0, _len1 = files.length; _j < _len1; _j++) { - file = files[_j]; - this.emit("sending", file, xhr, formData); + + // Let the user add additional data if necessary + for (var _iterator23 = files, _isArray23 = true, _i25 = 0, _iterator23 = _isArray23 ? _iterator23 : _iterator23[Symbol.iterator]();;) { + var _ref22; + + if (_isArray23) { + if (_i25 >= _iterator23.length) break; + _ref22 = _iterator23[_i25++]; + } else { + _i25 = _iterator23.next(); + if (_i25.done) break; + _ref22 = _i25.value; + } + + var _file = _ref22; + + this.emit("sending", _file, xhr, formData); } if (this.options.uploadMultiple) { this.emit("sendingmultiple", files, xhr, formData); } + + this._addFormElementData(formData); + + // Finally add the files + // Has to be last because some servers (eg: S3) expect the file to be the last parameter + for (var i = 0; i < dataBlocks.length; i++) { + var dataBlock = dataBlocks[i]; + formData.append(dataBlock.name, dataBlock.data, dataBlock.filename); + } + + this.submitRequest(xhr, formData, files); + } + + // Transforms all files with this.options.transformFile and invokes done with the transformed files when done. + + }, { + key: "_transformFiles", + value: function _transformFiles(files, done) { + var _this16 = this; + + var transformedFiles = []; + // Clumsy way of handling asynchronous calls, until I get to add a proper Future library. + var doneCounter = 0; + + var _loop = function _loop(i) { + _this16.options.transformFile.call(_this16, files[i], function (transformedFile) { + transformedFiles[i] = transformedFile; + if (++doneCounter === files.length) { + done(transformedFiles); + } + }); + }; + + for (var i = 0; i < files.length; i++) { + _loop(i); + } + } + + // Takes care of adding other input elements of the form to the AJAX request + + }, { + key: "_addFormElementData", + value: function _addFormElementData(formData) { + // Take care of other input elements if (this.element.tagName === "FORM") { - _ref2 = this.element.querySelectorAll("input, textarea, select, button"); - for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { - input = _ref2[_k]; - inputName = input.getAttribute("name"); - inputType = input.getAttribute("type"); + for (var _iterator24 = this.element.querySelectorAll("input, textarea, select, button"), _isArray24 = true, _i26 = 0, _iterator24 = _isArray24 ? _iterator24 : _iterator24[Symbol.iterator]();;) { + var _ref23; + + if (_isArray24) { + if (_i26 >= _iterator24.length) break; + _ref23 = _iterator24[_i26++]; + } else { + _i26 = _iterator24.next(); + if (_i26.done) break; + _ref23 = _i26.value; + } + + var input = _ref23; + + var inputName = input.getAttribute("name"); + var inputType = input.getAttribute("type"); + if (inputType) inputType = inputType.toLowerCase(); + + // If the input doesn't have a name, we can't use it. + if (typeof inputName === 'undefined' || inputName === null) continue; + if (input.tagName === "SELECT" && input.hasAttribute("multiple")) { - _ref3 = input.options; - for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { - option = _ref3[_l]; + // Possibly multiple values + for (var _iterator25 = input.options, _isArray25 = true, _i27 = 0, _iterator25 = _isArray25 ? _iterator25 : _iterator25[Symbol.iterator]();;) { + var _ref24; + + if (_isArray25) { + if (_i27 >= _iterator25.length) break; + _ref24 = _iterator25[_i27++]; + } else { + _i27 = _iterator25.next(); + if (_i27.done) break; + _ref24 = _i27.value; + } + + var option = _ref24; + if (option.selected) { formData.append(inputName, option.value); } } - } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) { + } else if (!inputType || inputType !== "checkbox" && inputType !== "radio" || input.checked) { formData.append(inputName, input.value); } } } - for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) { - formData.append(this._getParamName(i), files[i], files[i].name); - } - return xhr.send(formData); - }; + } + + // Invoked when there is new progress information about given files. + // If e is not provided, it is assumed that the upload is finished. + + }, { + key: "_updateFilesUploadProgress", + value: function _updateFilesUploadProgress(files, xhr, e) { + var progress = void 0; + if (typeof e !== 'undefined') { + progress = 100 * e.loaded / e.total; + + if (files[0].upload.chunked) { + var file = files[0]; + // Since this is a chunked upload, we need to update the appropriate chunk progress. + var chunk = this._getChunk(file, xhr); + chunk.progress = progress; + chunk.total = e.total; + chunk.bytesSent = e.loaded; + var fileProgress = 0, + fileTotal = void 0, + fileBytesSent = void 0; + file.upload.progress = 0; + file.upload.total = 0; + file.upload.bytesSent = 0; + for (var i = 0; i < file.upload.totalChunkCount; i++) { + if (file.upload.chunks[i] !== undefined && file.upload.chunks[i].progress !== undefined) { + file.upload.progress += file.upload.chunks[i].progress; + file.upload.total += file.upload.chunks[i].total; + file.upload.bytesSent += file.upload.chunks[i].bytesSent; + } + } + file.upload.progress = file.upload.progress / file.upload.totalChunkCount; + } else { + for (var _iterator26 = files, _isArray26 = true, _i28 = 0, _iterator26 = _isArray26 ? _iterator26 : _iterator26[Symbol.iterator]();;) { + var _ref25; + + if (_isArray26) { + if (_i28 >= _iterator26.length) break; + _ref25 = _iterator26[_i28++]; + } else { + _i28 = _iterator26.next(); + if (_i28.done) break; + _ref25 = _i28.value; + } + + var _file2 = _ref25; + + _file2.upload.progress = progress; + _file2.upload.total = e.total; + _file2.upload.bytesSent = e.loaded; + } + } + for (var _iterator27 = files, _isArray27 = true, _i29 = 0, _iterator27 = _isArray27 ? _iterator27 : _iterator27[Symbol.iterator]();;) { + var _ref26; + + if (_isArray27) { + if (_i29 >= _iterator27.length) break; + _ref26 = _iterator27[_i29++]; + } else { + _i29 = _iterator27.next(); + if (_i29.done) break; + _ref26 = _i29.value; + } + + var _file3 = _ref26; + + this.emit("uploadprogress", _file3, _file3.upload.progress, _file3.upload.bytesSent); + } + } else { + // Called when the file finished uploading + + var allFilesFinished = true; + + progress = 100; + + for (var _iterator28 = files, _isArray28 = true, _i30 = 0, _iterator28 = _isArray28 ? _iterator28 : _iterator28[Symbol.iterator]();;) { + var _ref27; + + if (_isArray28) { + if (_i30 >= _iterator28.length) break; + _ref27 = _iterator28[_i30++]; + } else { + _i30 = _iterator28.next(); + if (_i30.done) break; + _ref27 = _i30.value; + } + + var _file4 = _ref27; + + if (_file4.upload.progress !== 100 || _file4.upload.bytesSent !== _file4.upload.total) { + allFilesFinished = false; + } + _file4.upload.progress = progress; + _file4.upload.bytesSent = _file4.upload.total; + } + + // Nothing to do, all files already at 100% + if (allFilesFinished) { + return; + } + + for (var _iterator29 = files, _isArray29 = true, _i31 = 0, _iterator29 = _isArray29 ? _iterator29 : _iterator29[Symbol.iterator]();;) { + var _ref28; + + if (_isArray29) { + if (_i31 >= _iterator29.length) break; + _ref28 = _iterator29[_i31++]; + } else { + _i31 = _iterator29.next(); + if (_i31.done) break; + _ref28 = _i31.value; + } + + var _file5 = _ref28; + + this.emit("uploadprogress", _file5, progress, _file5.upload.bytesSent); + } + } + } + }, { + key: "_finishedUploading", + value: function _finishedUploading(files, xhr, e) { + var response = void 0; + + if (files[0].status === Dropzone.CANCELED) { + return; + } + + if (xhr.readyState !== 4) { + return; + } + + if (xhr.responseType !== 'arraybuffer' && xhr.responseType !== 'blob') { + response = xhr.responseText; + + if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) { + try { + response = JSON.parse(response); + } catch (error) { + e = error; + response = "Invalid JSON response from server."; + } + } + } + + this._updateFilesUploadProgress(files); + + if (!(200 <= xhr.status && xhr.status < 300)) { + this._handleUploadError(files, xhr, response); + } else { + if (files[0].upload.chunked) { + files[0].upload.finishedChunkUpload(this._getChunk(files[0], xhr)); + } else { + this._finished(files, response, e); + } + } + } + }, { + key: "_handleUploadError", + value: function _handleUploadError(files, xhr, response) { + if (files[0].status === Dropzone.CANCELED) { + return; + } + + if (files[0].upload.chunked && this.options.retryChunks) { + var chunk = this._getChunk(files[0], xhr); + if (chunk.retries++ < this.options.retryChunksLimit) { + this._uploadData(files, [chunk.dataBlock]); + return; + } else { + console.warn('Retried this chunk too often. Giving up.'); + } + } + + for (var _iterator30 = files, _isArray30 = true, _i32 = 0, _iterator30 = _isArray30 ? _iterator30 : _iterator30[Symbol.iterator]();;) { + var _ref29; + + if (_isArray30) { + if (_i32 >= _iterator30.length) break; + _ref29 = _iterator30[_i32++]; + } else { + _i32 = _iterator30.next(); + if (_i32.done) break; + _ref29 = _i32.value; + } + + var file = _ref29; + + this._errorProcessing(files, response || this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr); + } + } + }, { + key: "submitRequest", + value: function submitRequest(xhr, formData, files) { + xhr.send(formData); + } + + // Called internally when processing is finished. + // Individual callbacks have to be called in the appropriate sections. + + }, { + key: "_finished", + value: function _finished(files, responseText, e) { + for (var _iterator31 = files, _isArray31 = true, _i33 = 0, _iterator31 = _isArray31 ? _iterator31 : _iterator31[Symbol.iterator]();;) { + var _ref30; + + if (_isArray31) { + if (_i33 >= _iterator31.length) break; + _ref30 = _iterator31[_i33++]; + } else { + _i33 = _iterator31.next(); + if (_i33.done) break; + _ref30 = _i33.value; + } + + var file = _ref30; - Dropzone.prototype._finished = function(files, responseText, e) { - var file, _i, _len; - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; file.status = Dropzone.SUCCESS; this.emit("success", file, responseText, e); this.emit("complete", file); @@ -1359,15 +2814,32 @@ this.emit("successmultiple", files, responseText, e); this.emit("completemultiple", files); } + if (this.options.autoProcessQueue) { return this.processQueue(); } - }; + } + + // Called internally when processing is finished. + // Individual callbacks have to be called in the appropriate sections. + + }, { + key: "_errorProcessing", + value: function _errorProcessing(files, message, xhr) { + for (var _iterator32 = files, _isArray32 = true, _i34 = 0, _iterator32 = _isArray32 ? _iterator32 : _iterator32[Symbol.iterator]();;) { + var _ref31; + + if (_isArray32) { + if (_i34 >= _iterator32.length) break; + _ref31 = _iterator32[_i34++]; + } else { + _i34 = _iterator32.next(); + if (_i34.done) break; + _ref31 = _i34.value; + } + + var file = _ref31; - Dropzone.prototype._errorProcessing = function(files, message, xhr) { - var file, _i, _len; - for (_i = 0, _len = files.length; _i < _len; _i++) { - file = files[_i]; file.status = Dropzone.ERROR; this.emit("error", file, message, xhr); this.emit("complete", file); @@ -1376,353 +2848,688 @@ this.emit("errormultiple", files, message, xhr); this.emit("completemultiple", files); } + if (this.options.autoProcessQueue) { return this.processQueue(); } - }; - - return Dropzone; - - })(Emitter); - - Dropzone.version = "4.0.1"; - - Dropzone.options = {}; - - Dropzone.optionsForElement = function(element) { - if (element.getAttribute("id")) { - return Dropzone.options[camelize(element.getAttribute("id"))]; - } else { - return void 0; } - }; - - Dropzone.instances = []; - - Dropzone.forElement = function(element) { - if (typeof element === "string") { - element = document.querySelector(element); + }], [{ + key: "uuidv4", + value: function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, + v = c === 'x' ? r : r & 0x3 | 0x8; + return v.toString(16); + }); } - if ((element != null ? element.dropzone : void 0) == null) { - throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."); - } - return element.dropzone; - }; + }]); - Dropzone.autoDiscover = true; + return Dropzone; +}(Emitter); - Dropzone.discover = function() { - var checkElements, dropzone, dropzones, _i, _len, _results; - if (document.querySelectorAll) { - dropzones = document.querySelectorAll(".dropzone"); - } else { - dropzones = []; - checkElements = function(elements) { - var el, _i, _len, _results; - _results = []; - for (_i = 0, _len = elements.length; _i < _len; _i++) { - el = elements[_i]; - if (/(^| )dropzone($| )/.test(el.className)) { - _results.push(dropzones.push(el)); +Dropzone.initClass(); + +Dropzone.version = "5.5.1"; + +// This is a map of options for your different dropzones. Add configurations +// to this object for your different dropzone elemens. +// +// Example: +// +// Dropzone.options.myDropzoneElementId = { maxFilesize: 1 }; +// +// To disable autoDiscover for a specific element, you can set `false` as an option: +// +// Dropzone.options.myDisabledElementId = false; +// +// And in html: +// +//
+Dropzone.options = {}; + +// Returns the options for an element or undefined if none available. +Dropzone.optionsForElement = function (element) { + // Get the `Dropzone.options.elementId` for this element if it exists + if (element.getAttribute("id")) { + return Dropzone.options[camelize(element.getAttribute("id"))]; + } else { + return undefined; + } +}; + +// Holds a list of all dropzone instances +Dropzone.instances = []; + +// Returns the dropzone for given element if any +Dropzone.forElement = function (element) { + if (typeof element === "string") { + element = document.querySelector(element); + } + if ((element != null ? element.dropzone : undefined) == null) { + throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."); + } + return element.dropzone; +}; + +// Set to false if you don't want Dropzone to automatically find and attach to .dropzone elements. +Dropzone.autoDiscover = true; + +// Looks for all .dropzone elements and creates a dropzone for them +Dropzone.discover = function () { + var dropzones = void 0; + if (document.querySelectorAll) { + dropzones = document.querySelectorAll(".dropzone"); + } else { + dropzones = []; + // IE :( + var checkElements = function checkElements(elements) { + return function () { + var result = []; + for (var _iterator33 = elements, _isArray33 = true, _i35 = 0, _iterator33 = _isArray33 ? _iterator33 : _iterator33[Symbol.iterator]();;) { + var _ref32; + + if (_isArray33) { + if (_i35 >= _iterator33.length) break; + _ref32 = _iterator33[_i35++]; } else { - _results.push(void 0); + _i35 = _iterator33.next(); + if (_i35.done) break; + _ref32 = _i35.value; + } + + var el = _ref32; + + if (/(^| )dropzone($| )/.test(el.className)) { + result.push(dropzones.push(el)); + } else { + result.push(undefined); } } - return _results; - }; - checkElements(document.getElementsByTagName("div")); - checkElements(document.getElementsByTagName("form")); - } - _results = []; - for (_i = 0, _len = dropzones.length; _i < _len; _i++) { - dropzone = dropzones[_i]; + return result; + }(); + }; + checkElements(document.getElementsByTagName("div")); + checkElements(document.getElementsByTagName("form")); + } + + return function () { + var result = []; + for (var _iterator34 = dropzones, _isArray34 = true, _i36 = 0, _iterator34 = _isArray34 ? _iterator34 : _iterator34[Symbol.iterator]();;) { + var _ref33; + + if (_isArray34) { + if (_i36 >= _iterator34.length) break; + _ref33 = _iterator34[_i36++]; + } else { + _i36 = _iterator34.next(); + if (_i36.done) break; + _ref33 = _i36.value; + } + + var dropzone = _ref33; + + // Create a dropzone unless auto discover has been disabled for specific element if (Dropzone.optionsForElement(dropzone) !== false) { - _results.push(new Dropzone(dropzone)); + result.push(new Dropzone(dropzone)); } else { - _results.push(void 0); + result.push(undefined); } } - return _results; - }; + return result; + }(); +}; - Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i]; +// Since the whole Drag'n'Drop API is pretty new, some browsers implement it, +// but not correctly. +// So I created a blacklist of userAgents. Yes, yes. Browser sniffing, I know. +// But what to do when browsers *theoretically* support an API, but crash +// when using it. +// +// This is a list of regular expressions tested against navigator.userAgent +// +// ** It should only be used on browser that *do* support the API, but +// incorrectly ** +// +Dropzone.blacklistedBrowsers = [ +// The mac os and windows phone version of opera 12 seems to have a problem with the File drag'n'drop API. +/opera.*(Macintosh|Windows Phone).*version\/12/i]; - Dropzone.isBrowserSupported = function() { - var capableBrowser, regex, _i, _len, _ref; - capableBrowser = true; - if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) { - if (!("classList" in document.createElement("a"))) { - capableBrowser = false; - } else { - _ref = Dropzone.blacklistedBrowsers; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - regex = _ref[_i]; - if (regex.test(navigator.userAgent)) { - capableBrowser = false; - continue; - } +// Checks if the browser is supported +Dropzone.isBrowserSupported = function () { + var capableBrowser = true; + + if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) { + if (!("classList" in document.createElement("a"))) { + capableBrowser = false; + } else { + // The browser supports the API, but may be blacklisted. + for (var _iterator35 = Dropzone.blacklistedBrowsers, _isArray35 = true, _i37 = 0, _iterator35 = _isArray35 ? _iterator35 : _iterator35[Symbol.iterator]();;) { + var _ref34; + + if (_isArray35) { + if (_i37 >= _iterator35.length) break; + _ref34 = _iterator35[_i37++]; + } else { + _i37 = _iterator35.next(); + if (_i37.done) break; + _ref34 = _i37.value; + } + + var regex = _ref34; + + if (regex.test(navigator.userAgent)) { + capableBrowser = false; + continue; } } - } else { - capableBrowser = false; } - return capableBrowser; - }; + } else { + capableBrowser = false; + } - without = function(list, rejectedItem) { - var item, _i, _len, _results; - _results = []; - for (_i = 0, _len = list.length; _i < _len; _i++) { - item = list[_i]; - if (item !== rejectedItem) { - _results.push(item); - } - } - return _results; - }; + return capableBrowser; +}; - camelize = function(str) { - return str.replace(/[\-_](\w)/g, function(match) { - return match.charAt(1).toUpperCase(); - }); - }; +Dropzone.dataURItoBlob = function (dataURI) { + // convert base64 to raw binary data held in a string + // doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this + var byteString = atob(dataURI.split(',')[1]); - Dropzone.createElement = function(string) { - var div; - div = document.createElement("div"); - div.innerHTML = string; - return div.childNodes[0]; - }; + // separate out the mime component + var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; - Dropzone.elementInside = function(element, container) { + // write the bytes of the string to an ArrayBuffer + var ab = new ArrayBuffer(byteString.length); + var ia = new Uint8Array(ab); + for (var i = 0, end = byteString.length, asc = 0 <= end; asc ? i <= end : i >= end; asc ? i++ : i--) { + ia[i] = byteString.charCodeAt(i); + } + + // write the ArrayBuffer to a blob + return new Blob([ab], { type: mimeString }); +}; + +// Returns an array without the rejected item +var without = function without(list, rejectedItem) { + return list.filter(function (item) { + return item !== rejectedItem; + }).map(function (item) { + return item; + }); +}; + +// abc-def_ghi -> abcDefGhi +var camelize = function camelize(str) { + return str.replace(/[\-_](\w)/g, function (match) { + return match.charAt(1).toUpperCase(); + }); +}; + +// Creates an element from string +Dropzone.createElement = function (string) { + var div = document.createElement("div"); + div.innerHTML = string; + return div.childNodes[0]; +}; + +// Tests if given element is inside (or simply is) the container +Dropzone.elementInside = function (element, container) { + if (element === container) { + return true; + } // Coffeescript doesn't support do/while loops + while (element = element.parentNode) { if (element === container) { return true; } - while (element = element.parentNode) { - if (element === container) { + } + return false; +}; + +Dropzone.getElement = function (el, name) { + var element = void 0; + if (typeof el === "string") { + element = document.querySelector(el); + } else if (el.nodeType != null) { + element = el; + } + if (element == null) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element."); + } + return element; +}; + +Dropzone.getElements = function (els, name) { + var el = void 0, + elements = void 0; + if (els instanceof Array) { + elements = []; + try { + for (var _iterator36 = els, _isArray36 = true, _i38 = 0, _iterator36 = _isArray36 ? _iterator36 : _iterator36[Symbol.iterator]();;) { + if (_isArray36) { + if (_i38 >= _iterator36.length) break; + el = _iterator36[_i38++]; + } else { + _i38 = _iterator36.next(); + if (_i38.done) break; + el = _i38.value; + } + + elements.push(this.getElement(el, name)); + } + } catch (e) { + elements = null; + } + } else if (typeof els === "string") { + elements = []; + for (var _iterator37 = document.querySelectorAll(els), _isArray37 = true, _i39 = 0, _iterator37 = _isArray37 ? _iterator37 : _iterator37[Symbol.iterator]();;) { + if (_isArray37) { + if (_i39 >= _iterator37.length) break; + el = _iterator37[_i39++]; + } else { + _i39 = _iterator37.next(); + if (_i39.done) break; + el = _i39.value; + } + + elements.push(el); + } + } else if (els.nodeType != null) { + elements = [els]; + } + + if (elements == null || !elements.length) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."); + } + + return elements; +}; + +// Asks the user the question and calls accepted or rejected accordingly +// +// The default implementation just uses `window.confirm` and then calls the +// appropriate callback. +Dropzone.confirm = function (question, accepted, rejected) { + if (window.confirm(question)) { + return accepted(); + } else if (rejected != null) { + return rejected(); + } +}; + +// Validates the mime type like this: +// +// https://developer.mozilla.org/en-US/docs/HTML/Element/input#attr-accept +Dropzone.isValidFile = function (file, acceptedFiles) { + if (!acceptedFiles) { + return true; + } // If there are no accepted mime types, it's OK + acceptedFiles = acceptedFiles.split(","); + + var mimeType = file.type; + var baseMimeType = mimeType.replace(/\/.*$/, ""); + + for (var _iterator38 = acceptedFiles, _isArray38 = true, _i40 = 0, _iterator38 = _isArray38 ? _iterator38 : _iterator38[Symbol.iterator]();;) { + var _ref35; + + if (_isArray38) { + if (_i40 >= _iterator38.length) break; + _ref35 = _iterator38[_i40++]; + } else { + _i40 = _iterator38.next(); + if (_i40.done) break; + _ref35 = _i40.value; + } + + var validType = _ref35; + + validType = validType.trim(); + if (validType.charAt(0) === ".") { + if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) { + return true; + } + } else if (/\/\*$/.test(validType)) { + // This is something like a image/* mime type + if (baseMimeType === validType.replace(/\/.*$/, "")) { + return true; + } + } else { + if (mimeType === validType) { return true; } } - return false; - }; - - Dropzone.getElement = function(el, name) { - var element; - if (typeof el === "string") { - element = document.querySelector(el); - } else if (el.nodeType != null) { - element = el; - } - if (element == null) { - throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element."); - } - return element; - }; - - Dropzone.getElements = function(els, name) { - var e, el, elements, _i, _j, _len, _len1, _ref; - if (els instanceof Array) { - elements = []; - try { - for (_i = 0, _len = els.length; _i < _len; _i++) { - el = els[_i]; - elements.push(this.getElement(el, name)); - } - } catch (_error) { - e = _error; - elements = null; - } - } else if (typeof els === "string") { - elements = []; - _ref = document.querySelectorAll(els); - for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { - el = _ref[_j]; - elements.push(el); - } - } else if (els.nodeType != null) { - elements = [els]; - } - if (!((elements != null) && elements.length)) { - throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."); - } - return elements; - }; - - Dropzone.confirm = function(question, accepted, rejected) { - if (window.confirm(question)) { - return accepted(); - } else if (rejected != null) { - return rejected(); - } - }; - - Dropzone.isValidFile = function(file, acceptedFiles) { - var baseMimeType, mimeType, validType, _i, _len; - if (!acceptedFiles) { - return true; - } - acceptedFiles = acceptedFiles.split(","); - mimeType = file.type; - baseMimeType = mimeType.replace(/\/.*$/, ""); - for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) { - validType = acceptedFiles[_i]; - validType = validType.trim(); - if (validType.charAt(0) === ".") { - if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) { - return true; - } - } else if (/\/\*$/.test(validType)) { - if (baseMimeType === validType.replace(/\/.*$/, "")) { - return true; - } - } else { - if (mimeType === validType) { - return true; - } - } - } - return false; - }; - - if (typeof jQuery !== "undefined" && jQuery !== null) { - jQuery.fn.dropzone = function(options) { - return this.each(function() { - return new Dropzone(this, options); - }); - }; } - if (typeof module !== "undefined" && module !== null) { - module.exports = Dropzone; - } else { - window.Dropzone = Dropzone; - } + return false; +}; - Dropzone.ADDED = "added"; +// Augment jQuery +if (typeof jQuery !== 'undefined' && jQuery !== null) { + jQuery.fn.dropzone = function (options) { + return this.each(function () { + return new Dropzone(this, options); + }); + }; +} - Dropzone.QUEUED = "queued"; +if (typeof module !== 'undefined' && module !== null) { + module.exports = Dropzone; +} else { + window.Dropzone = Dropzone; +} - Dropzone.ACCEPTED = Dropzone.QUEUED; +// Dropzone file status codes +Dropzone.ADDED = "added"; - Dropzone.UPLOADING = "uploading"; +Dropzone.QUEUED = "queued"; +// For backwards compatibility. Now, if a file is accepted, it's either queued +// or uploading. +Dropzone.ACCEPTED = Dropzone.QUEUED; - Dropzone.PROCESSING = Dropzone.UPLOADING; +Dropzone.UPLOADING = "uploading"; +Dropzone.PROCESSING = Dropzone.UPLOADING; // alias - Dropzone.CANCELED = "canceled"; +Dropzone.CANCELED = "canceled"; +Dropzone.ERROR = "error"; +Dropzone.SUCCESS = "success"; - Dropzone.ERROR = "error"; +/* - Dropzone.SUCCESS = "success"; + Bugfix for iOS 6 and 7 + Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios + based on the work of https://github.com/stomita/ios-imagefile-megapixel + + */ + +// Detecting vertical squash in loaded image. +// Fixes a bug which squash image vertically while drawing into canvas for some images. +// This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel +var detectVerticalSquash = function detectVerticalSquash(img) { + var iw = img.naturalWidth; + var ih = img.naturalHeight; + var canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = ih; + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + + var _ctx$getImageData = ctx.getImageData(1, 0, 1, ih), + data = _ctx$getImageData.data; + + // search image edge pixel position in case it is squashed vertically. - /* - - Bugfix for iOS 6 and 7 - Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios - based on the work of https://github.com/stomita/ios-imagefile-megapixel - */ + var sy = 0; + var ey = ih; + var py = ih; + while (py > sy) { + var alpha = data[(py - 1) * 4 + 3]; - detectVerticalSquash = function(img) { - var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy; - iw = img.naturalWidth; - ih = img.naturalHeight; - canvas = document.createElement("canvas"); - canvas.width = 1; - canvas.height = ih; - ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0); - data = ctx.getImageData(0, 0, 1, ih).data; - sy = 0; - ey = ih; - py = ih; - while (py > sy) { - alpha = data[(py - 1) * 4 + 3]; - if (alpha === 0) { - ey = py; - } else { - sy = py; - } - py = (ey + sy) >> 1; - } - ratio = py / ih; - if (ratio === 0) { - return 1; + if (alpha === 0) { + ey = py; } else { - return ratio; + sy = py; } - }; - drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { - var vertSquashRatio; - vertSquashRatio = detectVerticalSquash(img); - return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); - }; + py = ey + sy >> 1; + } + var ratio = py / ih; + if (ratio === 0) { + return 1; + } else { + return ratio; + } +}; - /* - * contentloaded.js - * - * Author: Diego Perini (diego.perini at gmail.com) - * Summary: cross-browser wrapper for DOMContentLoaded - * Updated: 20101020 - * License: MIT - * Version: 1.2 - * - * URL: - * http://javascript.nwbox.com/ContentLoaded/ - * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE - */ +// A replacement for context.drawImage +// (args are for source and destination). +var drawImageIOSFix = function drawImageIOSFix(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { + var vertSquashRatio = detectVerticalSquash(img); + return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); +}; - contentLoaded = function(win, fn) { - var add, doc, done, init, poll, pre, rem, root, top; - done = false; - top = true; - doc = win.document; - root = doc.documentElement; - add = (doc.addEventListener ? "addEventListener" : "attachEvent"); - rem = (doc.addEventListener ? "removeEventListener" : "detachEvent"); - pre = (doc.addEventListener ? "" : "on"); - init = function(e) { - if (e.type === "readystatechange" && doc.readyState !== "complete") { - return; - } - (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); - if (!done && (done = true)) { - return fn.call(win, e.type || e); - } - }; - poll = function() { - var e; - try { - root.doScroll("left"); - } catch (_error) { - e = _error; - setTimeout(poll, 50); - return; - } - return init("poll"); - }; - if (doc.readyState !== "complete") { - if (doc.createEventObject && root.doScroll) { - try { - top = !win.frameElement; - } catch (_error) {} - if (top) { - poll(); +// Based on MinifyJpeg +// Source: http://www.perry.cz/files/ExifRestorer.js +// http://elicon.blog57.fc2.com/blog-entry-206.html + +var ExifRestore = function () { + function ExifRestore() { + _classCallCheck(this, ExifRestore); + } + + _createClass(ExifRestore, null, [{ + key: "initClass", + value: function initClass() { + this.KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + } + }, { + key: "encode64", + value: function encode64(input) { + var output = ''; + var chr1 = undefined; + var chr2 = undefined; + var chr3 = ''; + var enc1 = undefined; + var enc2 = undefined; + var enc3 = undefined; + var enc4 = ''; + var i = 0; + while (true) { + chr1 = input[i++]; + chr2 = input[i++]; + chr3 = input[i++]; + enc1 = chr1 >> 2; + enc2 = (chr1 & 3) << 4 | chr2 >> 4; + enc3 = (chr2 & 15) << 2 | chr3 >> 6; + enc4 = chr3 & 63; + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + output = output + this.KEY_STR.charAt(enc1) + this.KEY_STR.charAt(enc2) + this.KEY_STR.charAt(enc3) + this.KEY_STR.charAt(enc4); + chr1 = chr2 = chr3 = ''; + enc1 = enc2 = enc3 = enc4 = ''; + if (!(i < input.length)) { + break; } } - doc[add](pre + "DOMContentLoaded", init, false); - doc[add](pre + "readystatechange", init, false); - return win[add](pre + "load", init, false); + return output; + } + }, { + key: "restore", + value: function restore(origFileBase64, resizedFileBase64) { + if (!origFileBase64.match('data:image/jpeg;base64,')) { + return resizedFileBase64; + } + var rawImage = this.decode64(origFileBase64.replace('data:image/jpeg;base64,', '')); + var segments = this.slice2Segments(rawImage); + var image = this.exifManipulation(resizedFileBase64, segments); + return "data:image/jpeg;base64," + this.encode64(image); + } + }, { + key: "exifManipulation", + value: function exifManipulation(resizedFileBase64, segments) { + var exifArray = this.getExifArray(segments); + var newImageArray = this.insertExif(resizedFileBase64, exifArray); + var aBuffer = new Uint8Array(newImageArray); + return aBuffer; + } + }, { + key: "getExifArray", + value: function getExifArray(segments) { + var seg = undefined; + var x = 0; + while (x < segments.length) { + seg = segments[x]; + if (seg[0] === 255 & seg[1] === 225) { + return seg; + } + x++; + } + return []; + } + }, { + key: "insertExif", + value: function insertExif(resizedFileBase64, exifArray) { + var imageData = resizedFileBase64.replace('data:image/jpeg;base64,', ''); + var buf = this.decode64(imageData); + var separatePoint = buf.indexOf(255, 3); + var mae = buf.slice(0, separatePoint); + var ato = buf.slice(separatePoint); + var array = mae; + array = array.concat(exifArray); + array = array.concat(ato); + return array; + } + }, { + key: "slice2Segments", + value: function slice2Segments(rawImageArray) { + var head = 0; + var segments = []; + while (true) { + var length; + if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 218) { + break; + } + if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 216) { + head += 2; + } else { + length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3]; + var endPoint = head + length + 2; + var seg = rawImageArray.slice(head, endPoint); + segments.push(seg); + head = endPoint; + } + if (head > rawImageArray.length) { + break; + } + } + return segments; + } + }, { + key: "decode64", + value: function decode64(input) { + var output = ''; + var chr1 = undefined; + var chr2 = undefined; + var chr3 = ''; + var enc1 = undefined; + var enc2 = undefined; + var enc3 = undefined; + var enc4 = ''; + var i = 0; + var buf = []; + // remove all characters that are not A-Z, a-z, 0-9, +, /, or = + var base64test = /[^A-Za-z0-9\+\/\=]/g; + if (base64test.exec(input)) { + console.warn('There were invalid base64 characters in the input text.\nValid base64 characters are A-Z, a-z, 0-9, \'+\', \'/\',and \'=\'\nExpect errors in decoding.'); + } + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ''); + while (true) { + enc1 = this.KEY_STR.indexOf(input.charAt(i++)); + enc2 = this.KEY_STR.indexOf(input.charAt(i++)); + enc3 = this.KEY_STR.indexOf(input.charAt(i++)); + enc4 = this.KEY_STR.indexOf(input.charAt(i++)); + chr1 = enc1 << 2 | enc2 >> 4; + chr2 = (enc2 & 15) << 4 | enc3 >> 2; + chr3 = (enc3 & 3) << 6 | enc4; + buf.push(chr1); + if (enc3 !== 64) { + buf.push(chr2); + } + if (enc4 !== 64) { + buf.push(chr3); + } + chr1 = chr2 = chr3 = ''; + enc1 = enc2 = enc3 = enc4 = ''; + if (!(i < input.length)) { + break; + } + } + return buf; + } + }]); + + return ExifRestore; +}(); + +ExifRestore.initClass(); + +/* + * contentloaded.js + * + * Author: Diego Perini (diego.perini at gmail.com) + * Summary: cross-browser wrapper for DOMContentLoaded + * Updated: 20101020 + * License: MIT + * Version: 1.2 + * + * URL: + * http://javascript.nwbox.com/ContentLoaded/ + * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE + */ + +// @win window reference +// @fn function reference +var contentLoaded = function contentLoaded(win, fn) { + var done = false; + var top = true; + var doc = win.document; + var root = doc.documentElement; + var add = doc.addEventListener ? "addEventListener" : "attachEvent"; + var rem = doc.addEventListener ? "removeEventListener" : "detachEvent"; + var pre = doc.addEventListener ? "" : "on"; + var init = function init(e) { + if (e.type === "readystatechange" && doc.readyState !== "complete") { + return; + } + (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); + if (!done && (done = true)) { + return fn.call(win, e.type || e); } }; - Dropzone._autoDiscoverFunction = function() { - if (Dropzone.autoDiscover) { - return Dropzone.discover(); + var poll = function poll() { + try { + root.doScroll("left"); + } catch (e) { + setTimeout(poll, 50); + return; } + return init("poll"); }; - contentLoaded(window, Dropzone._autoDiscoverFunction); + if (doc.readyState !== "complete") { + if (doc.createEventObject && root.doScroll) { + try { + top = !win.frameElement; + } catch (error) {} + if (top) { + poll(); + } + } + doc[add](pre + "DOMContentLoaded", init, false); + doc[add](pre + "readystatechange", init, false); + return win[add](pre + "load", init, false); + } +}; -}).call(this); +// As a single function to be able to write tests. +Dropzone._autoDiscoverFunction = function () { + if (Dropzone.autoDiscover) { + return Dropzone.discover(); + } +}; +contentLoaded(window, Dropzone._autoDiscoverFunction); + +function __guard__(value, transform) { + return typeof value !== 'undefined' && value !== null ? transform(value) : undefined; +} +function __guardMethod__(obj, methodName, transform) { + if (typeof obj !== 'undefined' && obj !== null && typeof obj[methodName] === 'function') { + return transform(obj, methodName); + } else { + return undefined; + } +} \ No newline at end of file diff --git a/modules/backend/assets/vendor/sweet-alert/sweet-alert.js b/modules/backend/assets/vendor/sweet-alert/sweet-alert.js index 77505356c..65c1d6f56 100644 --- a/modules/backend/assets/vendor/sweet-alert/sweet-alert.js +++ b/modules/backend/assets/vendor/sweet-alert/sweet-alert.js @@ -181,7 +181,7 @@ */ window.sweetAlertInitialize = function() { - var sweetHTML = '

Title

Text

', + var sweetHTML = '

Title

Text

', sweetWrap = document.createElement('div'); sweetWrap.innerHTML = sweetHTML; @@ -558,7 +558,7 @@ } else { hide($cancelBtn); } - + // Confirm button modal.setAttribute('data-has-confirm-button', params.showConfirmButton); if (params.showConfirmButton) { @@ -566,7 +566,7 @@ } else { hide($confirmBtn); } - + // Edit text on cancel and confirm buttons if (params.cancelButtonText) { diff --git a/modules/backend/behaviors/FormController.php b/modules/backend/behaviors/FormController.php index e4f38f800..ae3459235 100644 --- a/modules/backend/behaviors/FormController.php +++ b/modules/backend/behaviors/FormController.php @@ -159,7 +159,9 @@ class FormController extends ControllerBehavior $this->formWidget->bindEvent('form.beforeRefresh', function ($holder) { $result = $this->controller->formExtendRefreshData($this->formWidget, $holder->data); - if (is_array($result)) $holder->data = $result; + if (is_array($result)) { + $holder->data = $result; + } }); $this->formWidget->bindEvent('form.refreshFields', function ($fields) { diff --git a/modules/backend/behaviors/RelationController.php b/modules/backend/behaviors/RelationController.php index 74a83ebb5..542e4c7cf 100644 --- a/modules/backend/behaviors/RelationController.php +++ b/modules/backend/behaviors/RelationController.php @@ -886,7 +886,6 @@ class RelationController extends ControllerBehavior * Form */ elseif ($this->manageMode == 'form') { - if (!$config = $this->makeConfigForMode('manage', 'form', false)) { return null; } @@ -900,7 +899,7 @@ class RelationController extends ControllerBehavior * Existing record */ if ($this->manageId) { - $model = $config->model->find($id); + $model = $config->model->find($this->manageId); if ($model) { $config->model = $model; } else { @@ -1205,7 +1204,6 @@ class RelationController extends ControllerBehavior * Add */ if ($this->viewMode == 'multi') { - $checkedIds = $recordId ? [$recordId] : post('checked'); if (is_array($checkedIds)) { @@ -1221,14 +1219,12 @@ class RelationController extends ControllerBehavior $this->relationObject->add($model, $sessionKey); } } - } /* * Link */ elseif ($this->viewMode == 'single') { if ($recordId && ($model = $this->relationModel->find($recordId))) { - $this->relationObject->add($model, $sessionKey); $this->viewWidget->setFormValues($model->attributes); @@ -1242,7 +1238,6 @@ class RelationController extends ControllerBehavior $parentModel->save(); } } - } } @@ -1264,7 +1259,6 @@ class RelationController extends ControllerBehavior * Remove */ if ($this->viewMode == 'multi') { - $checkedIds = $recordId ? [$recordId] : post('checked'); if (is_array($checkedIds)) { @@ -1707,7 +1701,8 @@ class RelationController extends ControllerBehavior * * @return \Backend\Classes\WidgetBase */ - public function relationGetManageWidget() { + public function relationGetManageWidget() + { return $this->manageWidget; } @@ -1716,7 +1711,8 @@ class RelationController extends ControllerBehavior * * @return \Backend\Classes\WidgetBase */ - public function relationGetViewWidget() { + public function relationGetViewWidget() + { return $this->viewWidget; } -} \ No newline at end of file +} diff --git a/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js b/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js index 80bf1bea4..126e39284 100644 --- a/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js +++ b/modules/backend/behaviors/relationcontroller/assets/js/october.relation.js @@ -70,7 +70,7 @@ /* * This function transfers the supplied variables as hidden form inputs, - * to any popup that is spawned within the supplied container. The spawned + * to any popup that is spawned within the supplied container. The spawned * popup must contain a form element. */ this.bindToPopups = function(container, vars) { @@ -87,7 +87,7 @@ if (typeof value == 'object') return value try { - return JSON.parse(JSON.stringify(eval("({" + value + "})"))) + return ocJSON("{" + value + "}") } catch (e) { throw new Error('Error parsing the '+name+' attribute value. '+e) @@ -97,4 +97,4 @@ } $.oc.relationBehavior = new RelationBehavior; -}(window.jQuery); \ No newline at end of file +}(window.jQuery); diff --git a/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm b/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm index 7ad9806c8..adfe14959 100644 --- a/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm +++ b/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm @@ -1,7 +1,11 @@
- true, 'sessionKey' => $newSessionKey]) ?> + true, + 'sessionKey' => $newSessionKey, + 'data-request-success' => "$.oc.relationBehavior.changed('" . e($this->vars['relationField']) . "', 'updated')", + ]) ?> diff --git a/modules/backend/classes/AuthManager.php b/modules/backend/classes/AuthManager.php index f78e33c6d..365dd2522 100644 --- a/modules/backend/classes/AuthManager.php +++ b/modules/backend/classes/AuthManager.php @@ -2,6 +2,7 @@ use System\Classes\PluginManager; use October\Rain\Auth\Manager as RainAuthManager; +use October\Rain\Exception\SystemException; /** * Back-end authentication manager. @@ -61,7 +62,7 @@ class AuthManager extends RainAuthManager * registerPermissions() function. The manager instance is passed to the * callback function as an argument. Usage: * - * BackendAuth::registerCallback(function($manager){ + * BackendAuth::registerCallback(function ($manager) { * $manager->registerPermissions([...]); * }); * @@ -81,7 +82,7 @@ class AuthManager extends RainAuthManager * - order - a position of the item in the menu, optional. * - comment - a brief comment that describes the permission, optional. * - tab - assign this permission to a tabbed group, optional. - * @param string $owner Specifies the menu items owner plugin or module in the format Vendor/Module. + * @param string $owner Specifies the permissions' owner plugin or module in the format Author.Plugin * @param array $definitions An array of the menu item definitions. */ public function registerPermissions($owner, array $definitions) @@ -96,6 +97,29 @@ class AuthManager extends RainAuthManager } } + /** + * Removes a single back-end permission + * @param string $owner Specifies the permissions' owner plugin or module in the format Author.Plugin + * @param string $code The code of the permission to remove + * @return void + */ + public function removePermission($owner, $code) + { + if (!$this->permissions) { + throw new SystemException('Unable to remove permissions before they are loaded.'); + } + + $ownerPermissions = array_filter($this->permissions, function ($permission) use ($owner) { + return $permission->owner === $owner; + }); + + foreach ($ownerPermissions as $key => $permission) { + if ($permission->code === $code) { + unset($this->permissions[$key]); + } + } + } + /** * Returns a list of the registered permissions items. * @return array diff --git a/modules/backend/classes/BackendController.php b/modules/backend/classes/BackendController.php index 7ccbfdd55..481df7ae1 100644 --- a/modules/backend/classes/BackendController.php +++ b/modules/backend/classes/BackendController.php @@ -8,10 +8,10 @@ use Event; use Config; use Request; use Response; -use Closure; use Illuminate\Routing\Controller as ControllerBase; use October\Rain\Router\Helper as RouterHelper; use System\Classes\PluginManager; +use Closure; /** * This is the master controller for all back-end pages. @@ -77,7 +77,11 @@ class BackendController extends ControllerBase $path = implode('/', $pathParts); $requestedController = $this->getRequestedController($path); - if (!is_null($requestedController) && count($requestedController['controller']->getMiddleware())) { + if ( + !is_null($requestedController) + && is_array($requestedController) + && count($requestedController['controller']->getMiddleware()) + ) { $action = $requestedController['action']; // Collect applicable middleware and insert middleware into pipeline diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php index f76eec54e..d019150cb 100644 --- a/modules/backend/classes/Controller.php +++ b/modules/backend/classes/Controller.php @@ -36,6 +36,8 @@ class Controller extends ControllerBase use \System\Traits\AssetMaker; use \System\Traits\ConfigMaker; use \System\Traits\EventEmitter; + use \System\Traits\ResponseMaker; + use \System\Traits\SecurityController; use \Backend\Traits\ErrorMaker; use \Backend\Traits\WidgetMaker; use \October\Rain\Extension\ExtendableTrait; @@ -113,16 +115,6 @@ class Controller extends ControllerBase */ protected $guarded = []; - /** - * @var int Response status code - */ - protected $statusCode = 200; - - /** - * @var mixed Override the standard controller response. - */ - protected $responseOverride = null; - /** * Constructor. */ @@ -210,13 +202,15 @@ class Controller extends ControllerBase /* * Check security token. + * @see \System\Traits\SecurityController */ if (!$this->verifyCsrfToken()) { - return Response::make(Lang::get('backend::lang.page.invalid_token.label'), 403); + return Response::make(Lang::get('system::lang.page.invalid_token.label'), 403); } /* * Check forced HTTPS protocol. + * @see \System\Traits\SecurityController */ if (!$this->verifyForceSecure()) { return Redirect::secure(Request::path()); @@ -231,7 +225,6 @@ class Controller extends ControllerBase * Check that user is logged in and has permission to view this page */ if (!$isPublicAction) { - /* * Not logged in, redirect to login screen or show ajax error. */ @@ -266,34 +259,30 @@ class Controller extends ControllerBase * Execute AJAX event */ if ($ajaxResponse = $this->execAjaxHandlers()) { - return $ajaxResponse; + $result = $ajaxResponse; } - /* * Execute postback handler */ - if ( + elseif ( ($handler = post('_handler')) && ($handlerResponse = $this->runAjaxHandler($handler)) && $handlerResponse !== true ) { - return $handlerResponse; + $result = $handlerResponse; } - /* * Execute page action */ - $result = $this->execPageAction($action, $params); - - if ($this->responseOverride !== null) { - $result = $this->responseOverride; + else { + $result = $this->execPageAction($action, $params); } - if (!is_string($result)) { - return $result; - } - - return Response::make($result, $this->statusCode); + /* + * Prepare and return response + * @see \System\Traits\ResponseMaker + */ + return $this->makeResponse($result); } /** @@ -554,7 +543,7 @@ class Controller extends ControllerBase * * Example usage (forwards AJAX handlers to a backend widget): * - * Event::listen('backend.ajax.beforeRunHandler', function((\Backend\Classes\Controller) $controller, (string) $handler) { + * Event::listen('backend.ajax.beforeRunHandler', function ((\Backend\Classes\Controller) $controller, (string) $handler) { * if (strpos($handler, '::')) { * list($componentAlias, $handlerName) = explode('::', $handler); * if ($componentAlias === $this->getBackendWidgetAlias()) { @@ -683,27 +672,6 @@ class Controller extends ControllerBase return $id; } - /** - * Sets the status code for the current web response. - * @param int $code Status code - */ - public function setStatusCode($code) - { - $this->statusCode = (int) $code; - return $this; - } - - /** - * Sets the response for the current page request cycle, this value takes priority - * over the standard response prepared by the controller. - * @param mixed $response Response object or string - */ - public function setResponse($response) - { - $this->responseOverride = $response; - return $this; - } - // // Hints // @@ -764,55 +732,4 @@ class Controller extends ControllerBase $hiddenHints = UserPreference::forUser()->get('backend::hints.hidden', []); return array_key_exists($name, $hiddenHints); } - - // - // Security - // - - /** - * Checks the request data / headers for a valid CSRF token. - * Returns false if a valid token is not found. Override this - * method to disable the check. - * @return bool - */ - protected function verifyCsrfToken() - { - if (!Config::get('cms.enableCsrfProtection')) { - return true; - } - - if (in_array(Request::method(), ['HEAD', 'GET', 'OPTIONS'])) { - return true; - } - - $token = Request::input('_token') ?: Request::header('X-CSRF-TOKEN'); - - if (!strlen($token)) { - return false; - } - - return hash_equals( - Session::token(), - $token - ); - } - - /** - * Checks if the back-end should force a secure protocol (HTTPS) enabled by config. - * @return bool - */ - protected function verifyForceSecure() - { - if (Request::secure() || Request::ajax()) { - return true; - } - - // @todo if year >= 2018 change default from false to null - $forceSecure = Config::get('cms.backendForceSecure', false); - if ($forceSecure === null) { - $forceSecure = !Config::get('app.debug', false); - } - - return !$forceSecure; - } } diff --git a/modules/backend/classes/FilterScope.php b/modules/backend/classes/FilterScope.php index d82cb1f81..bf5d062fb 100644 --- a/modules/backend/classes/FilterScope.php +++ b/modules/backend/classes/FilterScope.php @@ -51,6 +51,11 @@ class FilterScope */ public $options; + /** + * @var array Other scope names this scope depends on, when the other scopes are modified, this scope will update. + */ + public $dependsOn; + /** * @var string Specifies contextual visibility of this form scope. */ @@ -113,33 +118,32 @@ class FilterScope */ protected function evalConfig($config) { - if (isset($config['options'])) { - $this->options = $config['options']; + if ($config === null) { + $config = []; } - if (isset($config['context'])) { - $this->context = $config['context']; - } - if (isset($config['default'])) { - $this->defaults = $config['default']; - } - if (isset($config['conditions'])) { - $this->conditions = $config['conditions']; - } - if (isset($config['scope'])) { - $this->scope = $config['scope']; - } - if (isset($config['cssClass'])) { - $this->cssClass = $config['cssClass']; - } - if (isset($config['nameFrom'])) { - $this->nameFrom = $config['nameFrom']; - } - if (isset($config['descriptionFrom'])) { - $this->descriptionFrom = $config['descriptionFrom']; - } - if (array_key_exists('disabled', $config)) { - $this->disabled = $config['disabled']; + + /* + * Standard config:property values + */ + $applyConfigValues = [ + 'options', + 'dependsOn', + 'context', + 'default', + 'conditions', + 'scope', + 'cssClass', + 'nameFrom', + 'descriptionFrom', + 'disabled', + ]; + + foreach ($applyConfigValues as $value) { + if (array_key_exists($value, $config)) { + $this->{$value} = $config[$value]; + } } + return $config; } diff --git a/modules/backend/classes/FormField.php b/modules/backend/classes/FormField.php index a59dbcaf3..5f52b5c23 100644 --- a/modules/backend/classes/FormField.php +++ b/modules/backend/classes/FormField.php @@ -688,7 +688,6 @@ class FormField * relation value, all others will look up the relation object as normal. */ foreach ($keyParts as $key) { - if ($result instanceof Model && $result->hasRelation($key)) { if ($key == $lastField) { $result = $result->getRelationValue($key) ?: $default; @@ -709,7 +708,6 @@ class FormField } $result = $result->{$key}; } - } return $result; diff --git a/modules/backend/classes/FormTabs.php b/modules/backend/classes/FormTabs.php index d4360c3b4..709a0306e 100644 --- a/modules/backend/classes/FormTabs.php +++ b/modules/backend/classes/FormTabs.php @@ -31,12 +31,12 @@ class FormTabs implements IteratorAggregate, ArrayAccess * @var string Default tab label to use when none is specified. */ public $defaultTab = 'backend::lang.form.undefined_tab'; - + /** * @var array List of icons for their corresponding tabs. */ public $icons = []; - + /** * @var bool Should these tabs stretch to the bottom of the page layout. */ @@ -86,11 +86,11 @@ class FormTabs implements IteratorAggregate, ArrayAccess if (array_key_exists('defaultTab', $config)) { $this->defaultTab = $config['defaultTab']; } - + if (array_key_exists('icons', $config)) { $this->icons = $config['icons']; } - + if (array_key_exists('stretch', $config)) { $this->stretch = $config['stretch']; } @@ -182,7 +182,7 @@ class FormTabs implements IteratorAggregate, ArrayAccess return $tablessFields; } - + /** * Returns an icon for the tab based on the tab's name. * @param string $name @@ -194,7 +194,7 @@ class FormTabs implements IteratorAggregate, ArrayAccess return $this->icons[$name]; } } - + /** * Returns a tab pane CSS class. * @param string $index @@ -222,9 +222,10 @@ class FormTabs implements IteratorAggregate, ArrayAccess */ public function getIterator() { - return new ArrayIterator($this->suppressTabs - ? $this->getAllFields() - : $this->getFields() + return new ArrayIterator( + $this->suppressTabs + ? $this->getAllFields() + : $this->getFields() ); } diff --git a/modules/backend/classes/FormWidgetBase.php b/modules/backend/classes/FormWidgetBase.php index 91bbf2be2..d505e770f 100644 --- a/modules/backend/classes/FormWidgetBase.php +++ b/modules/backend/classes/FormWidgetBase.php @@ -149,5 +149,4 @@ abstract class FormWidgetBase extends WidgetBase return $this->formField->getValueFromData($this->data ?: $this->model, $defaultValue); } - } diff --git a/modules/backend/classes/ListColumn.php b/modules/backend/classes/ListColumn.php index f66d55937..96384add5 100644 --- a/modules/backend/classes/ListColumn.php +++ b/modules/backend/classes/ListColumn.php @@ -219,6 +219,17 @@ class ListColumn return $this->align ? 'list-cell-align-' . $this->align : ''; } + /** + * Returns a raw config item value. + * @param string $value + * @param string $default + * @return mixed + */ + public function getConfig($value, $default = null) + { + return array_get($this->config, $value, $default); + } + /** * Returns this columns value from a supplied data set, which can be * an array or a model or another generic collection. diff --git a/modules/backend/classes/NavigationManager.php b/modules/backend/classes/NavigationManager.php index 4a3af1e18..d91915a40 100644 --- a/modules/backend/classes/NavigationManager.php +++ b/modules/backend/classes/NavigationManager.php @@ -3,6 +3,10 @@ use Event; use BackendAuth; use System\Classes\PluginManager; +use Validator; +use SystemException; +use Log; +use Config; /** * Manages the backend navigation. @@ -124,7 +128,9 @@ class NavigationManager */ $orderCount = 0; foreach ($item->sideMenu as $sideMenuItem) { - if ($sideMenuItem->order !== -1) continue; + if ($sideMenuItem->order !== -1) { + continue; + } $sideMenuItem->order = ($orderCount += 100); } @@ -148,7 +154,7 @@ class NavigationManager * `registerMenuItems` method. The manager instance is passed to the callback * function as an argument. Usage: * - * BackendMenu::registerCallback(function($manager){ + * BackendMenu::registerCallback(function ($manager) { * $manager->registerMenuItems([...]); * }); * @@ -193,6 +199,24 @@ class NavigationManager $this->items = []; } + $validator = Validator::make($definitions, [ + '*.label' => 'required', + '*.icon' => 'required_without:*.iconSvg', + '*.url' => 'required', + '*.sideMenu.*.label' => 'nullable|required', + '*.sideMenu.*.icon' => 'nullable|required_without:*.sideMenu.*.iconSvg', + '*.sideMenu.*.url' => 'nullable|required', + ]); + + if ($validator->fails()) { + $errorMessage = 'Invalid menu item detected in ' . $owner . '. Contact the plugin author to fix (' . $validator->errors()->first() . ')'; + if (Config::get('app.debug', false)) { + throw new SystemException($errorMessage); + } else { + Log::error($errorMessage); + } + } + $this->addMainMenuItems($owner, $definitions); } @@ -530,4 +554,4 @@ class NavigationManager { return strtoupper($owner).'.'.strtoupper($code); } -} \ No newline at end of file +} diff --git a/modules/backend/classes/WidgetManager.php b/modules/backend/classes/WidgetManager.php index 35099a48d..7ec610c60 100644 --- a/modules/backend/classes/WidgetManager.php +++ b/modules/backend/classes/WidgetManager.php @@ -2,6 +2,7 @@ use Str; use BackendAuth; +use SystemException; use System\Classes\PluginManager; use Event; @@ -115,10 +116,9 @@ class WidgetManager } /** - * Manually registers form widget for consideration. - * Usage: + * Manually registers form widget for consideration. Usage: * - * WidgetManager::registerFormWidgets(function($manager){ + * WidgetManager::registerFormWidgets(function ($manager) { * $manager->registerFormWidget('Backend\FormWidgets\CodeEditor', 'codeeditor'); * }); * @@ -200,7 +200,7 @@ class WidgetManager * * Example usage: * - * Event::listen('system.reportwidgets.extendItems', function($manager) { + * Event::listen('system.reportwidgets.extendItems', function ($manager) { * $manager->removeReportWidget('Acme\ReportWidgets\YourWidget'); * }); * @@ -219,6 +219,15 @@ class WidgetManager return $this->reportWidgets; } + /** + * Returns the raw array of registered report widgets. + * @return array Array keys are class names. + */ + public function getReportWidgets() + { + return $this->reportWidgets; + } + /* * Registers a single report widget. */ @@ -228,10 +237,9 @@ class WidgetManager } /** - * Manually registers report widget for consideration. - * Usage: + * Manually registers report widget for consideration. Usage: * - * WidgetManager::registerReportWidgets(function($manager){ + * WidgetManager::registerReportWidgets(function ($manager) { * $manager->registerReportWidget('RainLab\GoogleAnalytics\ReportWidgets\TrafficOverview', [ * 'name' => 'Google Analytics traffic overview', * 'context' => 'dashboard' @@ -257,4 +265,4 @@ class WidgetManager unset($this->reportWidgets[$className]); } -} \ No newline at end of file +} diff --git a/modules/backend/controllers/Auth.php b/modules/backend/controllers/Auth.php index 42f80bcf7..7bd8d59ac 100644 --- a/modules/backend/controllers/Auth.php +++ b/modules/backend/controllers/Auth.php @@ -3,6 +3,7 @@ use Mail; use Flash; use Backend; +use Request; use Validator; use BackendAuth; use Backend\Models\AccessLog; @@ -11,6 +12,7 @@ use System\Classes\UpdateManager; use ApplicationException; use ValidationException; use Exception; +use Config; /** * Authentication controller @@ -33,21 +35,6 @@ class Auth extends Controller { parent::__construct(); - $this->middleware(function ($request, $response) { - // Clear Cache and any previous data to fix Invalid security token issue, see github: #3707 - $response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate'); - })->only('signin'); - - // Only run on HTTPS connections - if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on") { - $this->middleware(function ($request, $response) { - // Add HTTP Header 'Clear Site Data' to remove all Sensitive Data when signout, see github issue: #3707 - $response->headers->set('Clear-Site-Data', 'cache, cookies, storage, executionContexts'); - })->only('signout'); - } - - // Add JS File to un-install SW to avoid Cookie Cache Issues when Signin, see github issue: #3707 - $this->addJs(url("/modules/backend/assets/js/auth/uninstall-sw.js")); $this->layout = 'auth'; } @@ -66,14 +53,16 @@ class Auth extends Controller { $this->bodyClass = 'signin'; + // Clear Cache and any previous data to fix invalid security token issue + $this->setResponseHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); + try { if (post('postback')) { return $this->signin_onSubmit(); } $this->bodyClass .= ' preload'; - } - catch (Exception $ex) { + } catch (Exception $ex) { Flash::error($ex->getMessage()); } } @@ -90,7 +79,7 @@ class Auth extends Controller throw new ValidationException($validation); } - if (($remember = config('cms.backendForceRemember', true)) === null) { + if (is_null($remember = Config::get('cms.backendForceRemember', true))) { $remember = (bool) post('remember'); } @@ -100,12 +89,17 @@ class Auth extends Controller 'password' => post('password') ], $remember); - try { - // Load version updates - UpdateManager::instance()->update(); + if (is_null($runMigrationsOnLogin = Config::get('cms.runMigrationsOnLogin', null))) { + $runMigrationsOnLogin = Config::get('app.debug', false); } - catch (Exception $ex) { - Flash::error($ex->getMessage()); + + if ($runMigrationsOnLogin) { + try { + // Load version updates + UpdateManager::instance()->update(); + } catch (Exception $ex) { + Flash::error($ex->getMessage()); + } } // Log the sign in event @@ -126,6 +120,11 @@ class Auth extends Controller BackendAuth::logout(); } + // Add HTTP Header 'Clear Site Data' to purge all sensitive data upon signout + if (Request::secure()) { + $this->setResponseHeader('Clear-Site-Data', 'cache, cookies, storage, executionContexts'); + } + return Backend::redirect('backend'); } @@ -138,12 +137,14 @@ class Auth extends Controller if (post('postback')) { return $this->restore_onSubmit(); } - } - catch (Exception $ex) { + } catch (Exception $ex) { Flash::error($ex->getMessage()); } } + /** + * Submits the restore form. + */ public function restore_onSubmit() { $rules = [ @@ -165,7 +166,7 @@ class Auth extends Controller Flash::success(trans('backend::lang.account.restore_success')); $code = $user->getResetPasswordCode(); - $link = Backend::url('backend/auth/reset/'.$user->id.'/'.$code); + $link = Backend::url('backend/auth/reset/' . $user->id . '/' . $code); $data = [ 'name' => $user->full_name, @@ -192,8 +193,7 @@ class Auth extends Controller if (!$userId || !$code) { throw new ApplicationException(trans('backend::lang.account.reset_error')); } - } - catch (Exception $ex) { + } catch (Exception $ex) { Flash::error($ex->getMessage()); } @@ -201,6 +201,9 @@ class Auth extends Controller $this->vars['id'] = $userId; } + /** + * Submits the reset form. + */ public function reset_onSubmit() { if (!post('id') || !post('code')) { diff --git a/modules/backend/controllers/Files.php b/modules/backend/controllers/Files.php index 2ac62a722..adc5bcf32 100644 --- a/modules/backend/controllers/Files.php +++ b/modules/backend/controllers/Files.php @@ -30,7 +30,8 @@ class Files extends Controller try { return $this->findFileObject($code)->output('inline', true); } - catch (Exception $ex) {} + catch (Exception $ex) { + } return Response::make(View::make('backend::404'), 404); } @@ -48,7 +49,8 @@ class Files extends Controller true ); } - catch (Exception $ex) {} + catch (Exception $ex) { + } return Response::make(View::make('backend::404'), 404); } @@ -77,11 +79,18 @@ class Files extends Controller if (empty($path)) { $path = $file->getDiskPath(); } - $expires = now()->addSeconds(Config::get('cms.storage.uploads.temporaryUrlTTL', 3600)); - $url = Cache::remember('backend.file:' . $path, $expires, function () use ($disk, $path, $expires) { - return $disk->temporaryUrl($path, $expires); - }); + // Check to see if the URL has already been generated + $pathKey = 'backend.file:' . $path; + $url = Cache::get($pathKey); + + // The AWS S3 storage drivers will return a valid temporary URL even if the file does not exist + if (is_null($url) && $disk->exists($path)) { + $expires = now()->addSeconds(Config::get('cms.storage.uploads.temporaryUrlTTL', 3600)); + $url = Cache::remember($pathKey, $expires, function () use ($disk, $path, $expires) { + return $disk->temporaryUrl($path, $expires); + }); + } } return $url; diff --git a/modules/backend/controllers/Index.php b/modules/backend/controllers/Index.php index 28f7289c1..75a18eb3a 100644 --- a/modules/backend/controllers/Index.php +++ b/modules/backend/controllers/Index.php @@ -71,7 +71,9 @@ class Index extends Controller protected function checkPermissionRedirect() { if (!$this->user->hasAccess('backend.access_dashboard')) { - $true = function () { return true; }; + $true = function () { + return true; + }; if ($first = array_first(BackendMenu::listMainMenuItems(), $true)) { return Redirect::intended($first->url); } diff --git a/modules/backend/controllers/UserRoles.php b/modules/backend/controllers/UserRoles.php index caca7b288..bed79dbe9 100644 --- a/modules/backend/controllers/UserRoles.php +++ b/modules/backend/controllers/UserRoles.php @@ -51,7 +51,7 @@ class UserRoles extends Controller /* * Only super users can access */ - $this->bindEvent('page.beforeDisplay', function() { + $this->bindEvent('page.beforeDisplay', function () { if (!$this->user->isSuperUser()) { return Response::make(View::make('backend::access_denied'), 403); } diff --git a/modules/backend/controllers/usergroups/update.htm b/modules/backend/controllers/usergroups/update.htm index a262441d7..d73dc55f9 100644 --- a/modules/backend/controllers/usergroups/update.htm +++ b/modules/backend/controllers/usergroups/update.htm @@ -36,11 +36,10 @@
diff --git a/modules/backend/controllers/userroles/update.htm b/modules/backend/controllers/userroles/update.htm index 7f1b97736..9a50815d9 100644 --- a/modules/backend/controllers/userroles/update.htm +++ b/modules/backend/controllers/userroles/update.htm @@ -36,11 +36,10 @@ diff --git a/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php b/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php index 601893a45..5fad314c5 100644 --- a/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php +++ b/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php @@ -88,7 +88,8 @@ class DbBackendUserRoles extends Migration 'permissions' => $group->permissions ?? null ]); } - catch (Exception $ex) {} + catch (Exception $ex) { + } $permissions[$group->id] = $group->permissions ?? null; } @@ -130,7 +131,8 @@ class DbBackendUserRoles extends Migration $perms = json_decode($permString, true); $userPerms = array_merge($userPerms, $perms); } - catch (Exception $ex) {} + catch (Exception $ex) { + } } if (count($userPerms) > 0) { @@ -157,6 +159,7 @@ class DbBackendUserRoles extends Migration 'permissions' => json_encode($newPerms) ]); } - catch (Exception $ex) {} + catch (Exception $ex) { + } } } diff --git a/modules/backend/formwidgets/CodeEditor.php b/modules/backend/formwidgets/CodeEditor.php index e6dd6a1d5..9572aa62d 100644 --- a/modules/backend/formwidgets/CodeEditor.php +++ b/modules/backend/formwidgets/CodeEditor.php @@ -215,5 +215,4 @@ class CodeEditor extends FormWidgetBase $this->displayIndentGuides = $preferences->editor_display_indent_guides; $this->showPrintMargin = $preferences->editor_show_print_margin; } - } diff --git a/modules/backend/formwidgets/DataTable.php b/modules/backend/formwidgets/DataTable.php index 40a5609b0..65d18f5df 100644 --- a/modules/backend/formwidgets/DataTable.php +++ b/modules/backend/formwidgets/DataTable.php @@ -165,25 +165,35 @@ class DataTable extends FormWidgetBase } /** + * Dropdown/autocomplete option callback handler + * * Looks at the model for getXXXDataTableOptions or getDataTableOptions methods - * to obtain values for autocomplete field types. - * @param string $field Table field name - * @param string $data Data for the entire table - * @return array + * to obtain values for autocomplete and dropdown column types. + * + * @param string $columnName The name of the column to pass through to the callback. + * @param array $rowData The data provided for the current row in the datatable. + * @return array The options to make available to the dropdown or autocomplete, in format ["value" => "label"] */ - public function getDataTableOptions($field, $data) + public function getDataTableOptions($columnName, $rowData) { - $methodName = 'get'.studly_case($this->fieldName).'DataTableOptions'; + $methodName = 'get' . studly_case($this->fieldName) . 'DataTableOptions'; if (!$this->model->methodExists($methodName) && !$this->model->methodExists('getDataTableOptions')) { - throw new ApplicationException(Lang::get('backend::lang.model.missing_method', ['class' => get_class($this->model), 'method' => 'getDataTableOptions'])); + throw new ApplicationException( + Lang::get( + 'backend::lang.model.missing_method', + [ + 'class' => get_class($this->model), + 'method' => 'getDataTableOptions' + ] + ) + ); } if ($this->model->methodExists($methodName)) { - $result = $this->model->$methodName($field, $data); - } - else { - $result = $this->model->getDataTableOptions($this->fieldName, $field, $data); + $result = $this->model->$methodName($columnName, $rowData); + } else { + $result = $this->model->getDataTableOptions($this->fieldName, $columnName, $rowData); } if (!is_array($result)) { @@ -192,5 +202,4 @@ class DataTable extends FormWidgetBase return $result; } - } diff --git a/modules/backend/formwidgets/DatePicker.php b/modules/backend/formwidgets/DatePicker.php index 9c53bfd43..569bb6208 100644 --- a/modules/backend/formwidgets/DatePicker.php +++ b/modules/backend/formwidgets/DatePicker.php @@ -122,7 +122,7 @@ class DatePicker extends FormWidgetBase if ($this->mode === 'date' && !$this->ignoreTimezone) { $backendTimeZone = \Backend\Models\Preference::get('timezone'); $value->setTimezone($backendTimeZone); - $value->setTime(0,0,0); + $value->setTime(0, 0, 0); $value->setTimezone(Config::get('app.timezone')); } $value = $value->toDateTimeString(); diff --git a/modules/backend/formwidgets/FileUpload.php b/modules/backend/formwidgets/FileUpload.php index 7deae0d99..3a6cb2328 100644 --- a/modules/backend/formwidgets/FileUpload.php +++ b/modules/backend/formwidgets/FileUpload.php @@ -8,7 +8,6 @@ use Validator; use Backend\Widgets\Form; use Backend\Classes\FormField; use Backend\Classes\FormWidgetBase; -use Backend\Controllers\Files as FilesController; use October\Rain\Filesystem\Definitions as FileDefinitions; use ApplicationException; use ValidationException; @@ -60,6 +59,11 @@ class FileUpload extends FormWidgetBase */ public $mimeTypes = false; + /** + * @var mixed Max file size. + */ + public $maxFilesize; + /** * @var array Options used for generating thumbnails. */ @@ -97,11 +101,14 @@ class FileUpload extends FormWidgetBase */ public function init() { + $this->maxFilesize = $this->getUploadMaxFilesize(); + $this->fillFromConfig([ 'prompt', 'imageWidth', 'imageHeight', 'fileTypes', + 'maxFilesize', 'mimeTypes', 'thumbOptions', 'useCaption', @@ -137,6 +144,10 @@ class FileUpload extends FormWidgetBase $this->useCaption = false; } + if ($this->maxFilesize > $this->getUploadMaxFilesize()) { + throw new ApplicationException('Maximum allowed size for uploaded files: ' . $this->getUploadMaxFilesize()); + } + $this->vars['fileList'] = $fileList = $this->getFileList(); $this->vars['singleFile'] = $fileList->first(); $this->vars['displayMode'] = $this->getDisplayMode(); @@ -144,6 +155,7 @@ class FileUpload extends FormWidgetBase $this->vars['imageHeight'] = $this->imageHeight; $this->vars['imageWidth'] = $this->imageWidth; $this->vars['acceptedFileTypes'] = $this->getAcceptedFileTypes(true); + $this->vars['maxFilesize'] = $this->maxFilesize; $this->vars['cssDimensions'] = $this->getCssDimensions(); $this->vars['cssBlockDimensions'] = $this->getCssDimensions('block'); $this->vars['useCaption'] = $this->useCaption; @@ -394,10 +406,7 @@ class FileUpload extends FormWidgetBase protected function loadAssets() { $this->addCss('css/fileupload.css', 'core'); - $this->addJs('js/fileupload.js', [ - 'build' => 'core', - 'cache' => 'false' - ]); + $this->addJs('js/fileupload.js', 'core'); } /** @@ -487,25 +496,10 @@ class FileUpload extends FormWidgetBase */ protected function decorateFileAttributes($file) { - /* - * File is protected, create a secure public path - */ - if (!$file->isPublic()) { - $path = $thumb = FilesController::getDownloadUrl($file); + $path = $thumb = $file->getPath(); - if ($this->imageWidth || $this->imageHeight) { - $thumb = FilesController::getThumbUrl($file, $this->imageWidth, $this->imageHeight, $this->thumbOptions); - } - } - /* - * Otherwise use public paths - */ - else { - $path = $thumb = $file->getPath(); - - if ($this->imageWidth || $this->imageHeight) { - $thumb = $file->getThumb($this->imageWidth, $this->imageHeight, $this->thumbOptions); - } + if ($this->imageWidth || $this->imageHeight) { + $thumb = $file->getThumb($this->imageWidth, $this->imageHeight, $this->thumbOptions); } $file->pathUrl = $path; @@ -513,4 +507,20 @@ class FileUpload extends FormWidgetBase return $file; } + + /** + * Return max upload filesize in Mb + * @return integer + */ + protected function getUploadMaxFilesize() + { + $size = ini_get('upload_max_filesize'); + if (preg_match('/^([\d\.]+)([KMG])$/i', $size, $match)) { + $pos = array_search($match[2], ['K', 'M', 'G']); + if ($pos !== false) { + $size = $match[1] * pow(1024, $pos + 1); + } + } + return floor($size / 1024 / 1024); + } } diff --git a/modules/backend/formwidgets/MediaFinder.php b/modules/backend/formwidgets/MediaFinder.php index 3567b2e68..82a179694 100644 --- a/modules/backend/formwidgets/MediaFinder.php +++ b/modules/backend/formwidgets/MediaFinder.php @@ -1,7 +1,8 @@ formField->disabled) { + $user = BackendAuth::getUser(); + + if ($this->formField->disabled || !$user || !$user->hasAccess('media.manage_media')) { $this->previewMode = true; } } diff --git a/modules/backend/formwidgets/NestedForm.php b/modules/backend/formwidgets/NestedForm.php index a71152d6e..cd3b5ce45 100644 --- a/modules/backend/formwidgets/NestedForm.php +++ b/modules/backend/formwidgets/NestedForm.php @@ -35,12 +35,17 @@ class NestedForm extends FormWidgetBase /** * @inheritDoc */ - public function init() { + public function init() + { $this->fillFromConfig([ 'form', 'usePanelStyles', ]); + if ($this->formField->disabled) { + $this->previewMode = true; + } + $config = $this->makeConfig($this->form); $config->model = $this->model; $config->data = $this->getLoadValue(); @@ -48,7 +53,12 @@ class NestedForm extends FormWidgetBase $config->arrayName = $this->getFieldName(); $config->isNested = true; + if (object_get($this->getParentForm()->config, 'enableDefaults') === true) { + $config->enableDefaults = true; + } + $widget = $this->makeWidget(Form::class, $config); + $widget->previewMode = $this->previewMode; $widget->bindToController(); $this->formWidget = $widget; @@ -62,13 +72,14 @@ class NestedForm extends FormWidgetBase /** * @inheritdoc */ - function render() + public function render() { $this->prepareVars(); return $this->makePartial('nestedform'); } - function prepareVars() { + public function prepareVars() + { $this->formWidget->previewMode = $this->previewMode; } } diff --git a/modules/backend/formwidgets/PermissionEditor.php b/modules/backend/formwidgets/PermissionEditor.php index 014e523b3..be7c81acb 100644 --- a/modules/backend/formwidgets/PermissionEditor.php +++ b/modules/backend/formwidgets/PermissionEditor.php @@ -7,6 +7,26 @@ use BackendAuth; * User/group permission editor * This widget is used by the system internally on the System / Administrators pages. * + * Available Modes: + * - radio: Default mode, used by user-level permissions. + * Provides three-state control over each available permission. States are + * -1: Explicitly deny the permission + * 0: Inherit the permission's value from a parent source (User inherits from Role) + * 1: Explicitly grant the permission + * - checkbox: Used to define permissions for roles. Intended to define a base of what permissions are available + * Provides two state control over each available permission. States are + * 1: Explicitly allow the permission + * null: If the checkbox is not ticked, the permission will not be sent to the server and will not be stored. + * This is interpreted as the permission not being present and thus not allowed + * - switch: Used to define overriding permissions in a simpler UX than the radio. + * Provides two state control over each available permission. States are + * 1: Explicitly allow the permission + * -1: Explicitly deny the permission + * + * Available permissions can be defined in the form of an array of permission codes to allow: + * NOTE: Users are still not allowed to modify permissions that they themselves do not have access to + * availablePermissions: ['some.author.permission', 'some.other.permission', 'etc.some.system.permission'] + * * @package october\backend * @author Alexey Bobkov, Samuel Georges */ @@ -14,7 +34,15 @@ class PermissionEditor extends FormWidgetBase { protected $user; - public $mode; + /** + * @var string Mode to display the permission editor with. Available options: radio, checkbox, switch + */ + public $mode = 'radio'; + + /** + * @var array Permission codes to allow to be interacted with through this widget + */ + public $availablePermissions; /** * @inheritDoc @@ -22,7 +50,8 @@ class PermissionEditor extends FormWidgetBase public function init() { $this->fillFromConfig([ - 'mode' + 'mode', + 'availablePermissions', ]); $this->user = BackendAuth::getUser(); @@ -51,7 +80,7 @@ class PermissionEditor extends FormWidgetBase $permissionsData = []; } - $this->vars['checkboxMode'] = $this->getControlMode() === 'checkbox'; + $this->vars['mode'] = $this->mode; $this->vars['permissions'] = $this->getFilteredPermissions(); $this->vars['baseFieldName'] = $this->getFieldName(); $this->vars['permissionsData'] = $permissionsData; @@ -79,11 +108,6 @@ class PermissionEditor extends FormWidgetBase $this->addJs('js/permissioneditor.js', 'core'); } - protected function getControlMode() - { - return strlen($this->mode) ? $this->mode : 'radio'; - } - /** * Returns a safely parsed set of permissions, ensuring the user cannot elevate * their own permissions or permissions of another user above their own. @@ -117,19 +141,19 @@ class PermissionEditor extends FormWidgetBase /** * Returns the available permissions; removing those that the logged-in user does not have access to * - * @return array The permissions that the logged-in user does have access to + * @return array The permissions that the logged-in user does have access to ['permission-tab' => $arrayOfAllowedPermissionObjects] */ protected function getFilteredPermissions() { $permissions = BackendAuth::listTabbedPermissions(); - if ($this->user->isSuperUser()) { - return $permissions; - } - foreach ($permissions as $tab => $permissionsArray) { foreach ($permissionsArray as $index => $permission) { - if (!$this->user->hasAccess($permission->code)) { + if (!$this->user->hasAccess($permission->code) || + ( + is_array($this->availablePermissions) && + !in_array($permission->code, $this->availablePermissions) + )) { unset($permissionsArray[$index]); } } diff --git a/modules/backend/formwidgets/Relation.php b/modules/backend/formwidgets/Relation.php index 4851aba9f..6571a67ba 100644 --- a/modules/backend/formwidgets/Relation.php +++ b/modules/backend/formwidgets/Relation.php @@ -3,7 +3,7 @@ use Db; use Backend\Classes\FormField; use Backend\Classes\FormWidgetBase; -use Illuminate\Database\Eloquent\Relations\Relation as RelationBase; +use October\Rain\Database\Relations\Relation as RelationBase; /** * Form Relationship @@ -40,6 +40,11 @@ class Relation extends FormWidgetBase */ public $scope; + /** + * @var string Define the order of the list query. + */ + public $order; + // // Object properties // @@ -63,6 +68,7 @@ class Relation extends FormWidgetBase 'nameFrom', 'emptyOption', 'scope', + 'order', ]); if (isset($this->config->select)) { @@ -109,6 +115,12 @@ class Relation extends FormWidgetBase $field->type = 'dropdown'; } + // Order query by the configured option. + if ($this->order) { + // Using "raw" to allow authors to use a string to define the order clause. + $query->orderByRaw($this->order); + } + // It is safe to assume that if the model and related model are of // the exact same class, then it cannot be related to itself if ($model->exists && (get_class($model) == get_class($relationModel))) { diff --git a/modules/backend/formwidgets/Repeater.php b/modules/backend/formwidgets/Repeater.php index f8557a0d3..e25df461f 100644 --- a/modules/backend/formwidgets/Repeater.php +++ b/modules/backend/formwidgets/Repeater.php @@ -3,7 +3,6 @@ use Lang; use ApplicationException; use Backend\Classes\FormWidgetBase; -use October\Rain\Html\Helper as HtmlHelper; /** * Repeater Form Widget @@ -22,7 +21,7 @@ class Repeater extends FormWidgetBase /** * @var string Prompt text for adding new items. */ - public $prompt = 'Add new item'; + public $prompt; /** * @var bool Items can be sorted. @@ -53,11 +52,6 @@ class Repeater extends FormWidgetBase */ protected $defaultAlias = 'repeater'; - /** - * @var int Count of repeated items. - */ - protected $indexCount = 0; - /** * @var array Meta data associated to each field, organised by index */ @@ -73,6 +67,20 @@ class Repeater extends FormWidgetBase */ protected static $onAddItemCalled = false; + /** + * Determines if a child repeater has made an AJAX request to add an item + * + * @var bool + */ + protected $childAddItemCalled = false; + + /** + * Determines which child index has made the AJAX request to add an item + * + * @var int + */ + protected $childIndexCalled; + protected $useGroups = false; protected $groupDefinitions = []; @@ -89,6 +97,8 @@ class Repeater extends FormWidgetBase */ public function init() { + $this->prompt = Lang::get('backend::lang.repeater.add_new_item'); + $this->fillFromConfig([ 'form', 'prompt', @@ -107,8 +117,7 @@ class Repeater extends FormWidgetBase $this->loaded = true; } - $fieldName = $this->formField->getName(false); - + $this->checkAddItemRequest(); $this->processGroupMode(); if (!self::$onAddItemCalled) { @@ -213,11 +222,30 @@ class Repeater extends FormWidgetBase ? post($this->formField->getName()) : $this->getLoadValue(); - if ($currentValue === null) { - $this->indexCount = 0; + // Detect when a child widget is trying to run an AJAX handler + // outside of the form element that contains all the repeater + // fields that would normally be used to identify that case + $handler = $this->controller->getAjaxHandler(); + if (!$this->loaded && starts_with($handler, $this->alias . 'Form')) { + // Attempt to get the index of the repeater + $handler = str_after($handler, $this->alias . 'Form'); + preg_match("~^(\d+)~", $handler, $matches); + + if (isset($matches[1])) { + $index = $matches[1]; + $this->makeItemFormWidget($index); + } + } + + if (!$this->childAddItemCalled && $currentValue === null) { $this->formWidgets = []; return; - } + } + + if ($this->childAddItemCalled && !isset($currentValue[$this->childIndexCalled])) { + // If no value is available but a child repeater has added an item, add a "stub" repeater item + $this->makeItemFormWidget($this->childIndexCalled); + } // Ensure that the minimum number of items are preinitialized // ONLY DONE WHEN NOT IN GROUP MODE @@ -233,16 +261,14 @@ class Repeater extends FormWidgetBase } } } - + if (!is_array($currentValue)) { return; } - + collect($currentValue)->each(function ($value, $index) { $this->makeItemFormWidget($index, array_get($value, '_group', null)); }); - - $this->indexCount = max(count($currentValue), $this->indexCount); } /** @@ -260,7 +286,7 @@ class Repeater extends FormWidgetBase $config = $this->makeConfig($configDefinition); $config->model = $this->model; $config->data = $this->getValueFromIndex($index); - $config->alias = $this->alias . 'Form'.$index; + $config->alias = $this->alias . 'Form' . $index; $config->arrayName = $this->getFieldName().'['.$index.']'; $config->isNested = true; if (self::$onAddItemCalled || $this->minItems > 0) { @@ -268,6 +294,7 @@ class Repeater extends FormWidgetBase } $widget = $this->makeWidget('Backend\Widgets\Form', $config); + $widget->previewMode = $this->previewMode; $widget->bindToController(); $this->indexMeta[$index] = [ @@ -300,19 +327,16 @@ class Repeater extends FormWidgetBase public function onAddItem() { - self::$onAddItemCalled = true; - $groupCode = post('_repeater_group'); + $index = $this->getNextIndex(); + $this->prepareVars(); - $this->vars['widget'] = $this->makeItemFormWidget($this->indexCount, $groupCode); - $this->vars['indexValue'] = $this->indexCount; + $this->vars['widget'] = $this->makeItemFormWidget($index, $groupCode); + $this->vars['indexValue'] = $index; $itemContainer = '@#' . $this->getId('items'); - // Increase index count after item is created - ++$this->indexCount; - return [ $itemContainer => $this->makePartial('repeater_item') ]; @@ -333,6 +357,65 @@ class Repeater extends FormWidgetBase return $widget->onRefresh(); } + /** + * Determines the next available index number for assigning to a new repeater item. + * + * @return int + */ + protected function getNextIndex() + { + if ($this->loaded === true) { + $data = post($this->formField->getName()); + + if (is_array($data) && count($data)) { + return (max(array_keys($data)) + 1); + } + } else { + $data = $this->getLoadValue(); + + if (is_array($data)) { + return count($data); + } + } + + return 0; + } + + /** + * Determines the repeater that has triggered an AJAX request to add an item. + * + * @return void + */ + protected function checkAddItemRequest() + { + $handler = $this->getParentForm() + ->getController() + ->getAjaxHandler(); + + if ($handler === null || strpos($handler, '::') === false) { + return; + } + + list($widgetName, $handlerName) = explode('::', $handler); + if ($handlerName !== 'onAddItem') { + return; + } + + if ($this->alias === $widgetName) { + // This repeater has made the AJAX request + self::$onAddItemCalled = true; + } else if (strpos($widgetName, $this->alias) === 0) { + // A child repeater has made the AJAX request + + // Get index from AJAX handler + $handlerSuffix = str_replace($this->alias . 'Form', '', $widgetName); + preg_match('/^[0-9]+/', $handlerSuffix, $matches); + + $this->childAddItemCalled = true; + $this->childIndexCalled = (int) $matches[0]; + } + } + // // Group mode // diff --git a/modules/backend/formwidgets/RichEditor.php b/modules/backend/formwidgets/RichEditor.php index 9387e8d28..c236ca754 100644 --- a/modules/backend/formwidgets/RichEditor.php +++ b/modules/backend/formwidgets/RichEditor.php @@ -1,10 +1,12 @@ addCss('css/richeditor.css', 'core'); $this->addJs('js/build-min.js', 'core'); - $this->addJs('js/build-plugins-min.js', 'core'); + + if (Config::get('develop.decompileBackendAssets', false)) { + $scripts = Backend::decompileAsset($this->getAssetPath('js/build-plugins.js')); + foreach ($scripts as $script) { + $this->addJs($script, 'core'); + } + } else { + $this->addJs('js/build-plugins-min.js', 'core'); + } + $this->addJs('/modules/backend/formwidgets/codeeditor/assets/js/build-min.js', 'core'); if ($lang = $this->getValidEditorLang()) { @@ -217,8 +228,8 @@ class RichEditor extends FormWidgetBase $iterator = function ($links, $level = 0) use (&$iterator) { $result = []; - foreach ($links as $linkUrl => $link) { + foreach ($links as $linkUrl => $link) { /* * Remove scheme and host from URL */ diff --git a/modules/backend/formwidgets/TagList.php b/modules/backend/formwidgets/TagList.php index 5d1e51b28..1163f18c1 100644 --- a/modules/backend/formwidgets/TagList.php +++ b/modules/backend/formwidgets/TagList.php @@ -1,6 +1,7 @@ formField->options(); if (!$options && $this->mode === static::MODE_RELATION) { - $options = $this->getRelationModel()->lists($this->nameFrom); + $options = RelationBase::noConstraints(function () { + $query = $this->getRelationObject()->newQuery(); + + // Even though "no constraints" is applied, belongsToMany constrains the query + // by joining its pivot table. Remove all joins from the query. + $query->getQuery()->getQuery()->joins = []; + + return $query->lists($this->nameFrom); + }); } return $options; @@ -198,9 +207,10 @@ class TagList extends FormWidgetBase protected function getSeparatorCharacter() { switch (strtolower($this->separator)) { - case 'comma': return ','; - case 'space': return ' '; + case 'comma': + return ','; + case 'space': + return ' '; } } - } diff --git a/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css b/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css index 6d97d350a..097f0733e 100644 --- a/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css +++ b/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css @@ -7,21 +7,21 @@ .field-codeeditor.size-huge {min-height:250px} .field-codeeditor.size-giant {min-height:350px} .field-codeeditor .ace_search {font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:14px;color:#333;z-index:13} +.field-codeeditor .ace_search .ace_search_form.ace_nomatch {outline:none !important} +.field-codeeditor .ace_search .ace_search_form.ace_nomatch .ace_search_field {border:.0625rem solid red;-webkit-box-shadow:0 0 .1875rem .125rem red;box-shadow:0 0 .1875rem .125rem red;z-index:1;position:relative} .field-codeeditor .editor-code {-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px} -.field-codeeditor .editor-toolbar {position:absolute;top:-30px;right:35px;z-index:10} +.field-codeeditor .editor-toolbar {position:absolute;padding:0 5px;bottom:10px;right:25px;z-index:10;background:rgba(0,0,0,0.8);border-radius:5px} .field-codeeditor .editor-toolbar >ul, .field-codeeditor .editor-toolbar ul >li {list-style-type:none;padding:0;margin:0} .field-codeeditor .editor-toolbar >ul >li {float:left} .field-codeeditor .editor-toolbar >ul >li .tooltip.left {margin-right:25px} -.field-codeeditor .editor-toolbar >ul >li >a {display:block;height:25px;width:25px;color:#666;font-size:20px;text-align:center;text-decoration:none} +.field-codeeditor .editor-toolbar >ul >li >a {display:block;height:25px;width:25px;color:#666;font-size:20px;text-align:center;text-decoration:none;text-shadow:0 0 5px #000} .field-codeeditor .editor-toolbar >ul >li >a >abbr {position:absolute;font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} -.field-codeeditor .editor-toolbar >ul >li >a >i {opacity:0.7;filter:alpha(opacity=70);display:block} +.field-codeeditor .editor-toolbar >ul >li >a >i {opacity:1;filter:alpha(opacity=100);display:block} .field-codeeditor .editor-toolbar >ul >li >a >i:before {font-size:15px} .field-codeeditor .editor-toolbar >ul >li >a:hover >i, -.field-codeeditor .editor-toolbar >ul >li >a:focus >i {opacity:1;filter:alpha(opacity=100)} +.field-codeeditor .editor-toolbar >ul >li >a:focus >i {opacity:1;filter:alpha(opacity=100);color:#fff} .field-codeeditor.editor-fullscreen {z-index:301;position:fixed !important;top:0;left:0;height:100%;border-width:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0} +.field-codeeditor.editor-fullscreen .editor-toolbar {z-index:302} .field-codeeditor.editor-fullscreen .editor-code {-webkit-border-radius:0;-moz-border-radius:0;border-radius:0} -.field-codeeditor.editor-fullscreen .editor-toolbar {z-index:302;border-radius:0 0 5px 5px;right:25px;background-color:#ddd;border:1px solid #cbcbcb;border-right-color:#cbcbcb;border-right-style:solid;border-right-width:1px;border-top:0 none;max-width:125px;overflow:hidden;margin:0;padding:4px;position:absolute;top:0;white-space:normal} -.field-codeeditor.editor-fullscreen .editor-toolbar >ul >li >a {color:#666} -.field-codeeditor.editor-fullscreen .ace_search {z-index:303} -.field-codeeditor .secondary-tabs .editor-toolbar >ul >li >a {color:#fff} \ No newline at end of file +.field-codeeditor.editor-fullscreen .ace_search {z-index:303} \ No newline at end of file diff --git a/modules/backend/formwidgets/codeeditor/assets/js/build-min.js b/modules/backend/formwidgets/codeeditor/assets/js/build-min.js index f7771f1a4..d651f9765 100644 --- a/modules/backend/formwidgets/codeeditor/assets/js/build-min.js +++ b/modules/backend/formwidgets/codeeditor/assets/js/build-min.js @@ -4586,7 +4586,7 @@ ace.require('ace/config').set('basePath',this.options.vendorPath) editor.setOptions({enableEmmet:options.enableEmmet,enableBasicAutocompletion:options.autocompletion==='basic',enableSnippets:options.enableSnippets,enableLiveAutocompletion:options.autocompletion==='live'}) editor.renderer.setScrollMargin(options.margin,options.margin,0,0) editor.renderer.setPadding(options.margin) -this.$toolbar.find('>ul>li>a').each(function(){var abbr=$(this).find('>abbr'),label=abbr.text(),help=abbr.attr('title'),title=label+' ('+help+')';$(this).attr('title',title)}).tooltip({delay:500,placement:'bottom',html:true});this.$fullscreenDisable.hide() +this.$toolbar.find('>ul>li>a').each(function(){var abbr=$(this).find('>abbr'),label=abbr.text(),help=abbr.attr('title'),title=label+' ('+help+')';$(this).attr('title',title)}).tooltip({delay:500,placement:'top',html:true});this.$fullscreenDisable.hide() this.$fullscreenEnable.on('click.codeeditor','>a',$.proxy(this.toggleFullscreen,this)) this.$fullscreenDisable.on('click.codeeditor','>a',$.proxy(this.toggleFullscreen,this)) this.$searchboxDisable.hide() diff --git a/modules/backend/formwidgets/codeeditor/assets/js/codeeditor.js b/modules/backend/formwidgets/codeeditor/assets/js/codeeditor.js index 337e5856a..2d0ade840 100644 --- a/modules/backend/formwidgets/codeeditor/assets/js/codeeditor.js +++ b/modules/backend/formwidgets/codeeditor/assets/js/codeeditor.js @@ -5,7 +5,7 @@ * - data-control="codeeditor" - enables the code editor plugin * - data-vendor-path="/" - sets the path to find Ace editor files * - data-language="php" - set the coding language used - * - data-theme="textmate" - the colour scheme and theme + * - data-theme="textmate" - the colour scheme and theme * * JavaScript API: * $('textarea').codeEditor({ vendorPath: '/', language: 'php '}) @@ -186,7 +186,7 @@ }) .tooltip({ delay: 500, - placement: 'bottom', + placement: 'top', html: true }) ; @@ -201,7 +201,7 @@ this.$replaceboxDisable.hide() this.$replaceboxEnable.on('click.codeeditor', '>a', $.proxy(this.toggleReplacebox, this)) - this.$replaceboxDisable.on('click.codeeditor', '>a', $.proxy(this.toggleReplacebox, this)) + this.$replaceboxDisable.on('click.codeeditor', '>a', $.proxy(this.toggleReplacebox, this)) /* * Hotkeys @@ -372,11 +372,11 @@ this.editor.resize() this.editor.focus() } - + CodeEditor.prototype.toggleSearchbox = function() { this.$searchboxEnable.toggle() this.$searchboxDisable.toggle() - + this.editor.execCommand("find") this.editor.resize() @@ -386,7 +386,7 @@ CodeEditor.prototype.toggleReplacebox = function() { this.$replaceboxEnable.toggle() this.$replaceboxDisable.toggle() - + this.editor.execCommand("replace") this.editor.resize() @@ -406,7 +406,7 @@ var options = $.extend({}, CodeEditor.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('oc.codeEditor', (data = new CodeEditor(this, options))) if (typeof option == 'string') result = data[option].apply(data, args) - if (typeof result != 'undefined') return false + if (typeof result != 'undefined') return false }) return result ? result : this diff --git a/modules/backend/formwidgets/codeeditor/assets/less/codeeditor.less b/modules/backend/formwidgets/codeeditor/assets/less/codeeditor.less index 367662cc2..f21628639 100644 --- a/modules/backend/formwidgets/codeeditor/assets/less/codeeditor.less +++ b/modules/backend/formwidgets/codeeditor/assets/less/codeeditor.less @@ -1,10 +1,5 @@ @import "../../../../assets/less/core/boot.less"; -@color_1: #666; -@color_2: #fff; -@background_color_1: #ddd; -@border_right_color_1: #cbcbcb; - .field-codeeditor { width: 100%; position: relative; @@ -25,6 +20,19 @@ font-size: 14px; color: @text-color; z-index: @zindex-form + 3; + + // Fixes double focus on search box + .ace_search_form.ace_nomatch { + outline: none !important; + + .ace_search_field { + border: .0625rem solid red; + -webkit-box-shadow: 0 0 .1875rem .125rem red; + box-shadow: 0 0 .1875rem .125rem red; + z-index: 1; + position: relative; + } + } } .editor-code { @@ -33,9 +41,13 @@ .editor-toolbar { position: absolute; - top: -30px; - right: 35px; + padding: 0 5px; + bottom: 10px; + right: 25px; z-index: @zindex-form; + background: rgba(0,0,0,.8); + border-radius: 5px; + > ul, ul > li { list-style-type: none; padding: 0; margin: 0; } > ul > li { float: left; @@ -52,71 +64,40 @@ font-size: 20px; text-align: center; text-decoration: none; + text-shadow: 0 0 5px #000; + > abbr { position: absolute; .text-hide(); } > i { - .opacity(.7); + .opacity(1); display: block; &:before { font-size: 15px; } } &:hover, &:focus { - > i { .opacity(1); } + > i { + .opacity(1); + color: #fff; + } } - } } &.editor-fullscreen { z-index: @zindex-fullscreen + 1; - position: fixed!important; + position: fixed !important; top: 0; left: 0; height: 100%; border-width: 0; .border-radius(0); - .editor-code { .border-radius(0); } .editor-toolbar { z-index: @zindex-fullscreen + 2; - border-radius: 0 0 5px 5px; - right: 25px; - background-color: @background_color_1; - border: 1px solid #cbcbcb; - border-right-color: @border_right_color_1; - border-right-style: solid; - border-right-width: 1px; - border-top: 0 none; - max-width: 125px; - overflow: hidden; - margin: 0; - padding: 4px; - position: absolute; - top: 0; - white-space: normal; - >ul { - >li { - >a { - color: @color_1; - } - } - } } + .editor-code { .border-radius(0); } .ace_search { z-index: @zindex-fullscreen + 3; } } - - .secondary-tabs { - .editor-toolbar { - >ul { - >li { - >a { - color: @color_2; - } - } - } - } - } - -} \ No newline at end of file +} diff --git a/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css b/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css index fc82aaa6c..ee3ac29d0 100644 --- a/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css +++ b/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css @@ -1,151 +1,36 @@ -.field-colorpicker > ul { - list-style-type: none; - padding: 0; - margin: 0; - margin-top: -8px; -} -.field-colorpicker > ul li { - float: left; - text-indent: -9999px; - padding: 3px; - margin: 14px 14px 0 0; - cursor: pointer; - border-radius: 3px; -} -.field-colorpicker > ul li span { - display: block; - width: 25px; - height: 25px; - border: 1px solid #cecece; -} -.field-colorpicker > ul li:hover, -.field-colorpicker > ul li.active { - background-color: #cecece; -} -.field-colorpicker > ul li:hover span, -.field-colorpicker > ul li.active span { - border: 1px solid #ffffff; -} -.field-colorpicker > ul li.custom-color { - position: relative; -} -.field-colorpicker > ul li.custom-color span { - border-style: dashed; -} -.field-colorpicker > ul li.custom-color:hover span, -.field-colorpicker > ul li.custom-color.active span { - border-style: solid; -} -.field-colorpicker > ul li.custom-color:before { - text-indent: 0; - font-family: FontAwesome; - font-weight: normal; - font-style: normal; - text-decoration: inherit; - -webkit-font-smoothing: antialiased; - *margin-right: .3em; - content: "\f0d8"; - display: block; - text-align: center; - color: #000; - background: #e0e0e0; - font-size: 9px; - height: 9px; - line-height: 9px; - width: 9px; - position: absolute; - bottom: 3px; - right: 3px; -} +.field-colorpicker >ul {list-style-type:none;padding:0;margin:0;margin-top:-8px} +.field-colorpicker >ul li {float:left;text-indent:-9999px;padding:3px;margin:14px 14px 0 0;cursor:pointer;border-radius:3px} +.field-colorpicker >ul li span {display:block;width:25px;height:25px;border:1px solid #cecece} +.field-colorpicker >ul li:hover, +.field-colorpicker >ul li.active {background-color:#cecece} +.field-colorpicker >ul li:hover span, +.field-colorpicker >ul li.active span {border:1px solid #fff} +.field-colorpicker >ul li.custom-color {position:relative} +.field-colorpicker >ul li.custom-color span {border-style:dashed} +.field-colorpicker >ul li.custom-color:hover span, +.field-colorpicker >ul li.custom-color.active span {border-style:solid} +.field-colorpicker >ul li.custom-color:before {text-indent:0;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f0d8";display:block;text-align:center;color:#000;background:#e0e0e0;font-size:9px;height:9px;line-height:9px;width:9px;position:absolute;bottom:3px;right:3px} .sp-hue, -.sp-slider { - cursor: row-resize; -} +.sp-slider {cursor:row-resize} .sp-color, -.sp-dragger { - cursor: crosshair; -} +.sp-dragger {cursor:crosshair} .sp-alpha-inner, -.sp-alpha-handle { - cursor: col-resize; -} -.sp-hue { - left: 90%; -} -.sp-color { - right: 15%; -} -.sp-container { - border: none; - border-radius: 3px; - z-index: 10100; - -webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.24); - box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.24); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; -} -.sp-picker-container { - border: none; -} -.sp-alpha-handle { - background-color: #ccc; - border: 1px solid #666; - width: 4px; -} +.sp-alpha-handle {cursor:col-resize} +.sp-hue {left:90%} +.sp-color {right:15%} +.sp-container {border:none;border-radius:3px;z-index:10100;-webkit-box-shadow:0 1px 6px rgba(0,0,0,0.12),0 1px 4px rgba(0,0,0,0.24);box-shadow:0 1px 6px rgba(0,0,0,0.12),0 1px 4px rgba(0,0,0,0.24);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"} +.sp-picker-container {border:none} +.sp-alpha-handle {background-color:#ccc;border:1px solid #666;width:4px} .sp-color, -.sp-hue { - border: 1px solid #ccc; -} -.sp-slider { - background-color: #ccc; - border: 1px solid #666; - height: 3px; - left: -4px; - width: 22px; -} -.sp-dragger { - background: transparent; - box-shadow: 0 0 0 1px #111; -} -.sp-input { - outline: none !important; - -webkit-appearance: none; - border: 1px solid #d1d6d9; - -webkit-box-shadow: inset 0 1px 0 rgba(209, 214, 217, 0.25), 0 1px 0 rgba(255,255,255,.5); - box-shadow: inset 0 1px 0 rgba(209, 214, 217, 0.25), 0 1px 0 rgba(255,255,255,.5); - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.sp-input:focus { - border: 1px solid #d1d6d9; -} -.sp-button-container { - width: 100%; - position: relative; - text-align: right; - padding-top: 4px; -} +.sp-hue {border:1px solid #ccc} +.sp-slider {background-color:#ccc;border:1px solid #666;height:3px;left:-4px;width:22px} +.sp-dragger {background:transparent;box-shadow:0 0 0 1px #111} +.sp-input {outline:none !important;-webkit-appearance:none;border:1px solid #d1d6d9;-webkit-box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px} +.sp-input:focus {border:1px solid #d1d6d9} +.sp-button-container {width:100%;position:relative;text-align:right;padding-top:4px} .sp-container button, .sp-container button:hover, -.sp-container button:active { - text-shadow: none; - font-size: 13px; - text-align: left; - outline: none !important; - font-weight: normal; - -webkit-box-shadow: inset 0 -2px 0 rgba(0,0,0,.15); - box-shadow: inset 0 -2px 0 rgba(0,0,0,.15); - border: 1px solid transparent; - color: #ffffff; - border: none; - background: #656d79; - padding: 1px 7.5px; - font-size: 12px; - line-height: 1.5; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} +.sp-container button:active {text-shadow:none;font-size:13px;text-align:left;outline:none !important;font-weight:normal;-webkit-box-shadow:inset 0 -2px 0 rgba(0,0,0,.15);box-shadow:inset 0 -2px 0 rgba(0,0,0,.15);border:1px solid transparent;color:#fff;border:none;background:#656d79;padding:1px 7.5px;font-size:12px;line-height:1.5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px} .sp-container button:hover, .sp-container button:hover:hover, .sp-container button:active:hover, @@ -160,11 +45,7 @@ .sp-container button:active.active, .open .dropdown-toggle.sp-container button, .open .dropdown-toggle.sp-container button:hover, -.open .dropdown-toggle.sp-container button:active { - color: #ffffff; - background: #1681ba; - border-color: #1681ba; -} +.open .dropdown-toggle.sp-container button:active {color:#fff;background:#1681ba;border-color:#1681ba} .sp-container button:active, .sp-container button:hover:active, .sp-container button:active:active, @@ -173,18 +54,10 @@ .sp-container button:active.active, .open .dropdown-toggle.sp-container button, .open .dropdown-toggle.sp-container button:hover, -.open .dropdown-toggle.sp-container button:active { - background: #126896; - border-color: #105b83; - background-image: none; -} +.open .dropdown-toggle.sp-container button:active {background:#126896;border-color:#105b83;background-image:none} .sp-container button.on, .sp-container button:hover.on, -.sp-container button:active.on { - background: #494f58; - border-color: #40454d; - background-image: none; -} +.sp-container button:active.on {background:#494f58;border-color:#40454d;background-image:none} .sp-container button.disabled, .sp-container button:hover.disabled, .sp-container button:active.disabled, @@ -214,48 +87,16 @@ .sp-container button:active.disabled.active, .sp-container button[disabled].active, .sp-container button:hover[disabled].active, -.sp-container button:active[disabled].active { - background: #656d79; - border-color: #656d79; -} +.sp-container button:active[disabled].active {background:#656d79;border-color:#656d79} .sp-container button .badge, .sp-container button:hover .badge, -.sp-container button:active .badge { - color: #656d79; - background: #ffffff; -} -.sp-cancel { - bottom: -3px; - left: 0; - position: absolute; - text-decoration: none; - font-family: serif; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000000 !important; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; - filter: alpha(opacity=20); -} +.sp-container button:active .badge {color:#656d79;background:#fff} +.sp-cancel {bottom:-3px;left:0;position:absolute;text-decoration:none;font-family:serif;font-size:21px;font-weight:bold;line-height:1;color:#000 !important;text-shadow:0 1px 0 #fff;opacity:0.2;filter:alpha(opacity=20)} .sp-cancel:hover, -.sp-cancel:focus { - text-decoration: none; - cursor: pointer; - opacity: 0.5; - filter: alpha(opacity=50); -} -.sp-palette-container { - border: none; - float: none; - margin: 0; - padding: 5px 10px 0; -} +.sp-cancel:focus {text-decoration:none;cursor:pointer;opacity:0.5;filter:alpha(opacity=50)} +.sp-palette-container {border:none;float:none;margin:0;padding:5px 10px 0} .sp-palette .sp-thumb-el, -.sp-palette .sp-thumb-el:hover { - border: 1px solid rgba(0, 0, 0, 0.9); -} +.sp-palette .sp-thumb-el:hover {border:1px solid rgba(0,0,0,0.9)} .sp-palette .sp-thumb-el:hover, -.sp-palette .sp-thumb-el.sp-thumb-active { - border-color: rgba(0, 0, 0, 0.9); -} +.sp-palette .sp-thumb-el.sp-thumb-active {border-color:rgba(0,0,0,0.9)} +.sp-clear-enabled .sp-clear {left:88%;height:24px;margin-right:-4px} \ No newline at end of file diff --git a/modules/backend/formwidgets/colorpicker/assets/js/colorpicker.js b/modules/backend/formwidgets/colorpicker/assets/js/colorpicker.js index 33977c288..cc625b660 100644 --- a/modules/backend/formwidgets/colorpicker/assets/js/colorpicker.js +++ b/modules/backend/formwidgets/colorpicker/assets/js/colorpicker.js @@ -97,6 +97,10 @@ .siblings().removeClass('active') this.setColor($item.data('hexColor')) + + if($item.data('hexColor').length > 0) { + $item.addClass('sp-clear-display') + } } // COLORPICKER PLUGIN DEFINITION diff --git a/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less b/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less index b64dca607..99189e187 100644 --- a/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less +++ b/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less @@ -186,3 +186,9 @@ .sp-palette .sp-thumb-el.sp-thumb-active { border-color: rgba(0, 0, 0, 0.9); } + +.sp-clear-enabled .sp-clear { + left: 88%; + height: 24px; + margin-right: -4px; +} diff --git a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.js b/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.js index 720097877..e129e1840 100644 --- a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.js +++ b/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.js @@ -304,7 +304,7 @@ updateSelectionPaletteFromStorage(); - offsetElement.bind("click.spectrum touchstart.spectrum", function (e) { + offsetElement.on("click.spectrum touchstart.spectrum", function (e) { if (!disabled) { toggle(); } @@ -325,13 +325,13 @@ // Handle user typed input textInput.change(setFromTextInput); - textInput.bind("paste", function () { + textInput.on("paste", function () { setTimeout(setFromTextInput, 1); }); textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } }); cancelButton.text(opts.cancelText); - cancelButton.bind("click.spectrum", function (e) { + cancelButton.on("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); revert(); @@ -339,7 +339,7 @@ }); clearButton.attr("title", opts.clearText); - clearButton.bind("click.spectrum", function (e) { + clearButton.on("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); isEmpty = true; @@ -352,7 +352,7 @@ }); chooseButton.text(opts.chooseText); - chooseButton.bind("click.spectrum", function (e) { + chooseButton.on("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); @@ -367,7 +367,7 @@ }); toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText); - toggleButton.bind("click.spectrum", function (e) { + toggleButton.on("click.spectrum", function (e) { e.stopPropagation(); e.preventDefault(); @@ -462,9 +462,14 @@ else { set($(e.target).closest(".sp-thumb-el").data("color")); move(); - updateOriginalInput(true); + + // If the picker is going to close immediately, a palette selection + // is a change. Otherwise, it's a move only. if (opts.hideAfterPaletteSelect) { - hide(); + updateOriginalInput(true); + hide(); + } else { + updateOriginalInput(); } } @@ -472,8 +477,8 @@ } var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum"; - paletteContainer.delegate(".sp-thumb-el", paletteEvent, paletteElementClick); - initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, paletteElementClick); + paletteContainer.on(paletteEvent, ".sp-thumb-el", paletteElementClick); + initialColorContainer.on(paletteEvent, ".sp-thumb-el:nth-child(1)", { ignore: true }, paletteElementClick); } function updateSelectionPaletteFromStorage() { @@ -580,13 +585,15 @@ if ((value === null || value === "") && allowEmpty) { set(null); - updateOriginalInput(true); + move(); + updateOriginalInput(); } else { var tiny = tinycolor(value); if (tiny.isValid()) { set(tiny); - updateOriginalInput(true); + move(); + updateOriginalInput(); } else { textInput.addClass("sp-validation-error"); @@ -620,9 +627,9 @@ hideAll(); visible = true; - $(doc).bind("keydown.spectrum", onkeydown); - $(doc).bind("click.spectrum", clickout); - $(window).bind("resize.spectrum", resize); + $(doc).on("keydown.spectrum", onkeydown); + $(doc).on("click.spectrum", clickout); + $(window).on("resize.spectrum", resize); replacer.addClass("sp-active"); container.removeClass("sp-hidden"); @@ -665,9 +672,9 @@ if (!visible || flat) { return; } visible = false; - $(doc).unbind("keydown.spectrum", onkeydown); - $(doc).unbind("click.spectrum", clickout); - $(window).unbind("resize.spectrum", resize); + $(doc).off("keydown.spectrum", onkeydown); + $(doc).off("click.spectrum", clickout); + $(window).off("resize.spectrum", resize); replacer.removeClass("sp-active"); container.addClass("sp-hidden"); @@ -678,6 +685,7 @@ function revert() { set(colorOnShow, true); + updateOriginalInput(true); } function set(color, ignoreFormatChange) { @@ -719,7 +727,7 @@ h: currentHue, s: currentSaturation, v: currentValue, - a: Math.round(currentAlpha * 100) / 100 + a: Math.round(currentAlpha * 1000) / 1000 }, { format: opts.format || currentPreferredFormat }); } @@ -909,7 +917,7 @@ function destroy() { boundElement.show(); - offsetElement.unbind("click.spectrum touchstart.spectrum"); + offsetElement.off("click.spectrum touchstart.spectrum"); container.remove(); replacer.remove(); spectrums[spect.id] = null; @@ -988,17 +996,27 @@ var viewWidth = docElem.clientWidth + $(doc).scrollLeft(); var viewHeight = docElem.clientHeight + $(doc).scrollTop(); var offset = input.offset(); - offset.top += inputHeight; + var offsetLeft = offset.left; + var offsetTop = offset.top; - offset.left -= - Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? - Math.abs(offset.left + dpWidth - viewWidth) : 0); + offsetTop += inputHeight; - offset.top -= - Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? + offsetLeft -= + Math.min(offsetLeft, (offsetLeft + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offsetLeft + dpWidth - viewWidth) : 0); + + offsetTop -= + Math.min(offsetTop, ((offsetTop + dpHeight > viewHeight && viewHeight > dpHeight) ? Math.abs(dpHeight + inputHeight - extraY) : extraY)); - return offset; + return { + top: offsetTop, + bottom: offset.bottom, + left: offsetLeft, + right: offset.right, + width: offset.width, + height: offset.height + }; } /** @@ -1091,7 +1109,7 @@ maxWidth = $(element).width(); offset = $(element).offset(); - $(doc).bind(duringDragEvents); + $(doc).on(duringDragEvents); $(doc.body).addClass("sp-dragging"); move(e); @@ -1103,7 +1121,7 @@ function stop() { if (dragging) { - $(doc).unbind(duringDragEvents); + $(doc).off(duringDragEvents); $(doc.body).removeClass("sp-dragging"); // Wait a tick before notifying observers to allow the click event @@ -1115,7 +1133,7 @@ dragging = false; } - $(element).bind("touchstart mousedown", start); + $(element).on("touchstart mousedown", start); } function throttle(func, wait, debounce) { @@ -1178,7 +1196,7 @@ // Initializing a new instance of spectrum return this.spectrum("destroy").each(function () { - var options = $.extend({}, opts, $(this).data()); + var options = $.extend({}, $(this).data(), opts); var spect = spectrum(this, options); $(this).data(dataID, spect.id); }); @@ -1244,7 +1262,7 @@ this._g = rgb.g, this._b = rgb.b, this._a = rgb.a, - this._roundA = mathRound(100*this._a) / 100, + this._roundA = mathRound(1000 * this._a) / 1000, this._format = opts.format || rgb.format; this._gradientType = opts.gradientType; @@ -1285,7 +1303,7 @@ }, setAlpha: function(value) { this._a = boundAlpha(value); - this._roundA = mathRound(100*this._a) / 100; + this._roundA = mathRound(1000 * this._a) / 1000; return this; }, toHsv: function() { diff --git a/modules/backend/formwidgets/fileupload/assets/css/fileupload.css b/modules/backend/formwidgets/fileupload/assets/css/fileupload.css index 3636e4264..b3d4ba174 100644 --- a/modules/backend/formwidgets/fileupload/assets/css/fileupload.css +++ b/modules/backend/formwidgets/fileupload/assets/css/fileupload.css @@ -16,7 +16,7 @@ .field-fileupload .upload-object .icon-container:after {background-image:url('../../../../../system/assets/ui/images/loader-transparent.svg');position:absolute;content:' ';width:40px;height:40px;left:50%;top:50%;margin-top:-20px;margin-left:-20px;display:block;background-size:40px 40px;background-position:50% 50%;-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite} .field-fileupload .upload-object.is-success .icon-container {opacity:1} .field-fileupload .upload-object.is-success .icon-container:after {opacity:0;-webkit-transition:opacity 0.3s ease;transition:opacity 0.3s ease} -.field-fileupload .upload-object.is-error .icon-container:after {content:"";background:none;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f071";-webkit-animation:none;animation:none;font-size:40px;color:#ab2a1c;margin-top:-20px;margin-left:-20px;text-shadow:2px 2px 0 #fff} +.field-fileupload .upload-object.is-error .icon-container:after {content:"";background:none;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f071";-webkit-animation:none;animation:none;font-size:40px;color:#ab2a1c;margin-top:-20px;margin-left:-20px;text-shadow:2px 2px 0 #fff} .field-fileupload .upload-object.is-loading .icon-container {opacity:.6} .field-fileupload .upload-object.is-loading .icon-container:after {opacity:1;-webkit-transition:opacity 0.3s ease;transition:opacity 0.3s ease} .field-fileupload .upload-object.is-success {cursor:pointer} diff --git a/modules/backend/formwidgets/fileupload/assets/js/fileupload.js b/modules/backend/formwidgets/fileupload/assets/js/fileupload.js index 20c573035..bd4e50b43 100644 --- a/modules/backend/formwidgets/fileupload/assets/js/fileupload.js +++ b/modules/backend/formwidgets/fileupload/assets/js/fileupload.js @@ -103,6 +103,7 @@ clickable: this.$uploadButton.get(0), previewsContainer: this.$filesContainer.get(0), maxFiles: !this.options.isMulti ? 1 : null, + maxFilesize: this.options.maxFilesize, headers: {} } @@ -432,6 +433,7 @@ extraData: {}, paramName: 'file_data', fileTypes: null, + maxFilesize: 256, template: null, errorTemplate: null, isMulti: null, diff --git a/modules/backend/formwidgets/fileupload/partials/_file_multi.htm b/modules/backend/formwidgets/fileupload/partials/_file_multi.htm index 0dceb6e35..e478a7c68 100644 --- a/modules/backend/formwidgets/fileupload/partials/_file_multi.htm +++ b/modules/backend/formwidgets/fileupload/partials/_file_multi.htm @@ -7,6 +7,7 @@ data-error-template="#getId('errorTemplate') ?>" data-sort-handler="getEventHandler('onSortAttachments') ?>" data-unique-id="getId() ?>" + data-max-filesize="" data-config-handler="getEventHandler('onLoadAttachmentConfig') ?>" data-file-types="" > diff --git a/modules/backend/formwidgets/fileupload/partials/_file_single.htm b/modules/backend/formwidgets/fileupload/partials/_file_single.htm index f58ed010b..fbf153051 100644 --- a/modules/backend/formwidgets/fileupload/partials/_file_single.htm +++ b/modules/backend/formwidgets/fileupload/partials/_file_single.htm @@ -6,6 +6,7 @@ data-template="#getId('template') ?>" data-error-template="#getId('errorTemplate') ?>" data-unique-id="getId() ?>" + data-max-filesize="" data-config-handler="getEventHandler('onLoadAttachmentConfig') ?>" data-file-types="" > diff --git a/modules/backend/formwidgets/fileupload/partials/_image_multi.htm b/modules/backend/formwidgets/fileupload/partials/_image_multi.htm index a24b3fbfc..1f43e6cf4 100644 --- a/modules/backend/formwidgets/fileupload/partials/_image_multi.htm +++ b/modules/backend/formwidgets/fileupload/partials/_image_multi.htm @@ -7,6 +7,7 @@ data-error-template="#getId('errorTemplate') ?>" data-sort-handler="getEventHandler('onSortAttachments') ?>" data-unique-id="getId() ?>" + data-max-filesize="" data-config-handler="getEventHandler('onLoadAttachmentConfig') ?>" data-file-types="" > diff --git a/modules/backend/formwidgets/fileupload/partials/_image_single.htm b/modules/backend/formwidgets/fileupload/partials/_image_single.htm index 8d89e14d7..069ff1a32 100644 --- a/modules/backend/formwidgets/fileupload/partials/_image_single.htm +++ b/modules/backend/formwidgets/fileupload/partials/_image_single.htm @@ -8,6 +8,7 @@ data-unique-id="getId() ?>" data-thumbnail-width="" data-thumbnail-height="" + data-max-filesize="" data-config-handler="getEventHandler('onLoadAttachmentConfig') ?>" data-file-types="" > diff --git a/modules/backend/formwidgets/permissioneditor/assets/css/permissioneditor.css b/modules/backend/formwidgets/permissioneditor/assets/css/permissioneditor.css index ee2bce90a..c1316dc40 100644 --- a/modules/backend/formwidgets/permissioneditor/assets/css/permissioneditor.css +++ b/modules/backend/formwidgets/permissioneditor/assets/css/permissioneditor.css @@ -1,110 +1,40 @@ -.permissioneditor { - position: relative; - margin: 0 -20px; -} -.permissioneditor.control-disabled .permissions-overlay { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - background: rgba(255, 255, 255, 0.01); - cursor: not-allowed; -} -.permissioneditor.control-disabled table { - opacity: 0.5; - filter: alpha(opacity=50); -} -.permissioneditor table { - width: 100%; -} -.permissioneditor table th { - padding: 30px 4px 8px 4px; - color: #2a3e51; - font-weight: normal; - border-bottom: 1px solid #dbe1e3; -} -.permissioneditor table th.tab { - font-size: 13px; -} -.permissioneditor table th.permission-type { - text-transform: uppercase; - font-size: 11px; - text-align: center; -} -.permissioneditor table td { - padding: 10px 4px; - vertical-align: top; - border-bottom: 1px solid #ecf0f1; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.permissioneditor table td.permission-value { - text-align: center; -} -.permissioneditor table td.permission-name { - font-size: 13px; - cursor: pointer; - color: #777777; -} -.permissioneditor table td p.comment { - margin-top: 5px; - margin-bottom: 0; -} -.permissioneditor table td p.comment:empty { - display: none; -} -.permissioneditor table tr:hover td { - background: #4da7e8; -} -.permissioneditor table tr:hover td.permission-name { - color: #ffffff !important; -} +.permissioneditor {position:relative;margin:0 -20px} +.permissioneditor.control-disabled .permissions-overlay {position:absolute;left:0;top:0;width:100%;height:100%;background:rgba(255,255,255,0.01);cursor:not-allowed} +.permissioneditor.control-disabled table {opacity:0.5;filter:alpha(opacity=50)} +.permissioneditor table {width:100%} +.permissioneditor table th {padding:30px 4px 8px 4px;color:#2a3e51;font-weight:normal;border-bottom:1px solid #dbe1e3} +.permissioneditor table th.tab {font-size:13px} +.permissioneditor table th.permission-type {text-transform:uppercase;font-size:11px;text-align:center} +.permissioneditor table td {padding:10px 4px;vertical-align:top;border-bottom:1px solid #ecf0f1;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} +.permissioneditor table td.permission-value {text-align:center} +.permissioneditor table td.permission-name {font-size:13px;cursor:pointer;color:#777} +.permissioneditor table td p.comment {margin-top:5px;margin-bottom:0} +.permissioneditor table td p.comment:empty {display:none} +.permissioneditor table tr:hover td {background:#4ea5e0} +.permissioneditor table tr:hover td.permission-name {color:#fff !important} .permissioneditor table th:first-child, -.permissioneditor table td:first-child { - padding-left: 20px; -} +.permissioneditor table td:first-child {padding-left:20px} .permissioneditor table th:last-child, -.permissioneditor table td:last-child { - padding-right: 5px; -} -.permissioneditor table .custom-radio, -.permissioneditor table .custom-checkbox { - display: inline-block; - padding-left: 0; -} +.permissioneditor table td:last-child {padding-right:5px} .permissioneditor table .custom-radio, .permissioneditor table .custom-checkbox, +.permissioneditor table .custom-switch {display:inline-block;padding-left:0} +.permissioneditor table .custom-radio, +.permissioneditor table .custom-checkbox, +.permissioneditor table .custom-switch, .permissioneditor table .custom-radio label, -.permissioneditor table .custom-checkbox label { - margin-bottom: 0; -} +.permissioneditor table .custom-checkbox label, +.permissioneditor table .custom-switch label {margin-bottom:0} .permissioneditor table .custom-radio label, -.permissioneditor table .custom-checkbox label { - padding: 0 0 0 14px; - margin: 0; - top: 0; -} +.permissioneditor table .custom-checkbox label, +.permissioneditor table .custom-switch label {padding:0 0 0 14px;margin:0;top:0} .permissioneditor table .custom-radio label span, -.permissioneditor table .custom-checkbox label span { - text-indent: -10000em; - display: block; -} +.permissioneditor table .custom-checkbox label span, +.permissioneditor table .custom-switch label span {text-indent:-10000em;display:block} .permissioneditor table .custom-radio label:before, -.permissioneditor table .custom-checkbox label:before { - margin-right: 0; -} -.permissioneditor table tr:last-child td { - border-bottom: none; -} -.permissioneditor table tr:first-child th { - padding-top: 0; -} -.permissioneditor table tr.disabled td.permission-name { - color: #AAA; -} -.permissioneditor table tr.last-section-row td { - border-bottom: none; -} +.permissioneditor table .custom-checkbox label:before, +.permissioneditor table .custom-switch label:before {margin-right:0} +.permissioneditor table tr:last-child td {border-bottom:none} +.permissioneditor table tr:first-child th {padding-top:0} +.permissioneditor table tr.disabled td.permission-name {color:#AAA} +.permissioneditor table tr.last-section-row td {border-bottom:none} \ No newline at end of file diff --git a/modules/backend/formwidgets/permissioneditor/assets/less/permissioneditor.less b/modules/backend/formwidgets/permissioneditor/assets/less/permissioneditor.less index c5f575c07..956af038b 100644 --- a/modules/backend/formwidgets/permissioneditor/assets/less/permissioneditor.less +++ b/modules/backend/formwidgets/permissioneditor/assets/less/permissioneditor.less @@ -31,7 +31,7 @@ padding: 30px 4px 8px 4px; color: @color-label; font-weight: normal; - border-bottom: 1px solid @color-permissioneditor-section-border; + border-bottom: 1px solid @color-permissioneditor-section-border; &.tab { font-size: @font-size-base - 1; @@ -47,7 +47,7 @@ td { padding: 10px 4px; vertical-align: top; - border-bottom: 1px solid @color-permissioneditor-permission-border; + border-bottom: 1px solid @color-permissioneditor-permission-border; .user-select(none); &.permission-value{ @@ -91,7 +91,8 @@ } .custom-radio, - .custom-checkbox { + .custom-checkbox, + .custom-switch { display: inline-block;; padding-left: 0; diff --git a/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm b/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm index 4a6377650..2060a21ab 100644 --- a/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm +++ b/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm @@ -3,16 +3,18 @@ mode === 'radio'); ?> $tabPermissions): ?> + - + mode === 'radio') : ?> - + @@ -24,30 +26,41 @@ code, $permissionsData) - ? $permissionsData[$permission->code] - : 0; - } - else { - $isChecked = array_key_exists($permission->code, $permissionsData); + switch ($this->mode) { + case 'radio': + $permissionValue = array_key_exists($permission->code, $permissionsData) + ? $permissionsData[$permission->code] + : 0; + break; + case 'switch': + $isChecked = !((int) @$permissionsData[$permission->code] === -1); + break; + case 'checkbox': + default: + $isChecked = array_key_exists($permission->code, $permissionsData); + break; } + + $allowId = $this->getId('permission-' . $globalIndex . '-allow'); + $inheritId = $this->getId('permission-' . $globalIndex . '-inherit'); + $denyId = $this->getId('permission-' . $globalIndex . '-deny'); ?> + label)) ?> -

comment)) ?>

- - + + mode === 'radio'): ?> +
- +
- -
- - > - - -
- - - - +
type="radio" > - +
@@ -97,10 +95,44 @@ data-radio-color="red" > - +
- + mode === 'switch'): ?> + + + + + + + +
+ + > + + +
+ + diff --git a/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm b/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm index 8ebb2dd70..65878029e 100644 --- a/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm +++ b/modules/backend/formwidgets/recordfinder/partials/_recordfinder.htm @@ -28,6 +28,7 @@ class="btn btn-default clear-record" data-request="getEventHandler('onClearRecord') ?>" data-request-confirm="" + data-request-success="$('#getId() ?>').trigger('change')" aria-label="Remove"> diff --git a/modules/backend/formwidgets/repeater/assets/css/repeater.css b/modules/backend/formwidgets/repeater/assets/css/repeater.css index 604603da0..f12d27377 100644 --- a/modules/backend/formwidgets/repeater/assets/css/repeater.css +++ b/modules/backend/formwidgets/repeater/assets/css/repeater.css @@ -6,9 +6,9 @@ .field-repeater ul.field-repeater-items >li.dragged .repeater-item-remove {opacity:0} .field-repeater ul.field-repeater-items >li.dragged .repeater-item-collapsed-title {top:5px} .field-repeater ul.field-repeater-items >li.placeholder {display:block;position:relative;height:25px;margin-bottom:5px} -.field-repeater ul.field-repeater-items >li.placeholder:before {display:block;position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f054";color:#d35714;left:-10px;top:8px;z-index:2000} +.field-repeater ul.field-repeater-items >li.placeholder:before {display:block;position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f054";color:#d35714;left:-10px;top:8px;z-index:2000} .field-repeater li.field-repeater-item {position:relative;margin:0 0 1em 1em !important;padding:1.5em 1.25em 0 1.25em !important;background:#f5f5f5;border:1px solid #d1d6d9;border-radius:3px;box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);min-height:30px} -.field-repeater li.field-repeater-item:before {color:#bdc3c7;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f111";font-size:8px;position:absolute;left:-18px;top:-2px} +.field-repeater li.field-repeater-item:before {color:#bdc3c7;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f111";font-size:8px;position:absolute;left:-18px;top:-2px} .field-repeater li.field-repeater-item:after {counter-increment:repeater-index-counter;content:counter(repeater-index-counter);display:block;position:absolute;font-size:12px;left:-27px;color:#bdc3c7;top:10px;width:24px;text-align:center} .field-repeater li.field-repeater-item.collapsed .field-repeater-form {display:none} .field-repeater li.field-repeater-item.collapsed .repeater-item-collapse .repeater-item-collapse-one {-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)} @@ -41,7 +41,7 @@ .field-repeater li.field-repeater-item:hover .repeater-item-remove, .field-repeater li.field-repeater-item:active .repeater-item-remove {opacity:1} .field-repeater .field-repeater-add-item {position:relative;margin-top:10px;margin-left:20px;border:2px dotted #e0e0e0;border-radius:5px} -.field-repeater .field-repeater-add-item:before {color:#bdc3c7;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f067";font-size:16px;position:absolute;left:-23px;top:-11px} +.field-repeater .field-repeater-add-item:before {color:#bdc3c7;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f067";font-size:16px;position:absolute;left:-23px;top:-11px} .field-repeater .field-repeater-add-item >a {color:#bdc3c7;text-align:center;display:block;text-decoration:none;padding:13px 15px;text-transform:uppercase;font-weight:600;font-size:12px} .field-repeater .field-repeater-add-item:hover, .field-repeater .field-repeater-add-item:focus {background-color:#4ea5e0;border-color:transparent} diff --git a/modules/backend/formwidgets/repeater/assets/js/repeater.js b/modules/backend/formwidgets/repeater/assets/js/repeater.js index 32d2fdd5e..867befc97 100644 --- a/modules/backend/formwidgets/repeater/assets/js/repeater.js +++ b/modules/backend/formwidgets/repeater/assets/js/repeater.js @@ -211,9 +211,14 @@ $target = $item } - var $textInput = $('input[type=text]:first', $target) + var $textInput = $('input[type=text]:first, select:first', $target).first(); if ($textInput.length) { - return $textInput.val() + switch($textInput.prop("tagName")) { + case 'SELECT': + return $textInput.find('option:selected').text(); + default: + return $textInput.val(); + } } else { var $disabledTextInput = $('.text-field:first > .form-control', $target) if ($disabledTextInput.length) { diff --git a/modules/backend/formwidgets/richeditor/assets/css/richeditor.css b/modules/backend/formwidgets/richeditor/assets/css/richeditor.css index 06a9319b3..aa368cccf 100755 --- a/modules/backend/formwidgets/richeditor/assets/css/richeditor.css +++ b/modules/backend/formwidgets/richeditor/assets/css/richeditor.css @@ -523,6 +523,7 @@ body .fr-popup .fr-input-line textarea + label {background:transparent !importan body .fr-popup .fr-input-line input.fr-not-empty:focus + label, body .fr-popup .fr-input-line textarea.fr-not-empty:focus + label {color:#2a3e51} body .fr-popup .fr-checkbox span {border-color:#d1d6d9} +.fr-element {height:100%} .field-flush .field-richeditor, .field-flush .field-richeditor.editor-focus {border:none} .field-richeditor {border:1px solid #d1d6d9;-webkit-box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);-moz-box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-moz-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-ms-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s} @@ -561,6 +562,6 @@ body .fr-popup .fr-checkbox span {border-color:#d1d6d9} .control-richeditor figure[data-audio]:after {content:attr(data-label)} .control-richeditor figure[data-video]:before, .control-richeditor figure[data-audio]:before {position:static;margin-right:8px} -.control-richeditor figure[data-video]:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f03d"} -.control-richeditor figure[data-audio]:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f028"} +.control-richeditor figure[data-video]:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f03d"} +.control-richeditor figure[data-audio]:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f028"} .fr-quick-insert a.fr-floating-btn {color:rgba(64,82,97,0.8);text-decoration:none} \ No newline at end of file diff --git a/modules/backend/formwidgets/richeditor/assets/js/build-plugins-min.js b/modules/backend/formwidgets/richeditor/assets/js/build-plugins-min.js index 7e6d7bc74..905027eb6 100644 --- a/modules/backend/formwidgets/richeditor/assets/js/build-plugins-min.js +++ b/modules/backend/formwidgets/richeditor/assets/js/build-plugins-min.js @@ -93,15 +93,15 @@ $node.attr('data-label',text) insertElement($node)} function _initUiBlocks(){$('[data-video], [data-audio]',editor.$el).each(function(){$(this).addClass('fr-draggable').attr({'data-ui-block':'true','draggable':'true','tabindex':'0'}).html(' ') this.contentEditable=false})} -function _handleUiBlocksKeydown(ev){if(ev.which==40||ev.which==38||ev.which==8||ev.which==46){var $block=$(editor.selection.element()) +function _handleUiBlocksKeydown(ev){if(ev.key==='ArrowDown'||ev.key==='ArrowUp'||ev.key==='Backspace'||ev.key==='Delete'){var $block=$(editor.selection.element()) if($block.is('br')){$block=$block.parent()} -if(!!$block.length){switch(ev.which){case 38:_handleUiBlockCaretIn($block.prev()) +if(!!$block.length){switch(ev.key){case'ArrowUp':_handleUiBlockCaretIn($block.prev()) break -case 40:_handleUiBlockCaretIn($block.next()) +case'ArrowDown':_handleUiBlockCaretIn($block.next()) break -case 46:_handleUiBlockCaretClearEmpty($block.next(),$block) +case'Delete':_handleUiBlockCaretClearEmpty($block.next(),$block) break -case 8:_handleUiBlockCaretClearEmpty($block.prev(),$block) +case'Backspace':_handleUiBlockCaretClearEmpty($block.prev(),$block) break}}}} function _handleUiBlockCaretClearEmpty($block,$p){if($block.attr('data-ui-block')!==undefined&&$.trim($p.text()).length==0){$p.remove() _handleUiBlockCaretIn($block) @@ -110,17 +110,17 @@ function _handleUiBlockCaretIn($block){if($block.attr('data-ui-block')!==undefin editor.selection.clear() return true} return false} -function _uiBlockKeyDown(ev,block){if(ev.which==40||ev.which==38||ev.which==13||ev.which==8||ev.which==46){switch(ev.which){case 40:_focusUiBlockOrText($(block).next(),true) +function _uiBlockKeyDown(ev,block){if(ev.key==='ArrowDown'||ev.key==='ArrowUp'||ev.key==='Enter'||ev.key==='Backspace'||ev.key==='Delete'){switch(ev.key){case'ArrowDown':_focusUiBlockOrText($(block).next(),true) break -case 38:_focusUiBlockOrText($(block).prev(),false) +case'ArrowUp':_focusUiBlockOrText($(block).prev(),false) break -case 13:var $paragraph=$('


') +case'Enter':var $paragraph=$('


') $paragraph.insertAfter(block) editor.selection.setAfter(block) editor.selection.restore() editor.undo.saveStep() break -case 8:case 46:var $nextFocus=$(block).next(),gotoStart=true +case'Backspace':case'Delete':var $nextFocus=$(block).next(),gotoStart=true if($nextFocus.length==0){$nextFocus=$(block).prev() gotoStart=false} _focusUiBlockOrText($nextFocus,gotoStart) diff --git a/modules/backend/formwidgets/richeditor/assets/js/plugins/figures.js b/modules/backend/formwidgets/richeditor/assets/js/plugins/figures.js index a82ca02e2..fed8db5bc 100644 --- a/modules/backend/formwidgets/richeditor/assets/js/plugins/figures.js +++ b/modules/backend/formwidgets/richeditor/assets/js/plugins/figures.js @@ -85,28 +85,24 @@ } function _handleUiBlocksKeydown(ev) { - if (ev.which == 40 || ev.which == 38 || ev.which == 8 || ev.which == 46) { + if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp' || ev.key === 'Backspace' || ev.key === 'Delete') { var $block = $(editor.selection.element()) if ($block.is('br')) { $block = $block.parent() } if (!!$block.length) { - switch (ev.which) { - case 38: - // Up arrow + switch (ev.key) { + case 'ArrowUp': _handleUiBlockCaretIn($block.prev()) break - case 40: - // Down arrow + case 'ArrowDown': _handleUiBlockCaretIn($block.next()) break - case 46: - // Delete key + case 'Delete': _handleUiBlockCaretClearEmpty($block.next(), $block) break - case 8: - // Backspace key + case 'Backspace': _handleUiBlockCaretClearEmpty($block.prev(), $block) break } @@ -133,27 +129,23 @@ } function _uiBlockKeyDown(ev, block) { - if (ev.which == 40 || ev.which == 38 || ev.which == 13 || ev.which == 8 || ev.which == 46) { - switch (ev.which) { - case 40: - // Down arrow + if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp' || ev.key === 'Enter' || ev.key === 'Backspace' || ev.key === 'Delete') { + switch (ev.key) { + case 'ArrowDown': _focusUiBlockOrText($(block).next(), true) break - case 38: - // Up arrow + case 'ArrowUp': _focusUiBlockOrText($(block).prev(), false) break - case 13: - // Enter key + case 'Enter': var $paragraph = $('


') $paragraph.insertAfter(block) editor.selection.setAfter(block) editor.selection.restore() editor.undo.saveStep() break - case 8: - case 46: - // Delete / Backspace key + case 'Backspace': + case 'Delete': var $nextFocus = $(block).next(), gotoStart = true diff --git a/modules/backend/formwidgets/richeditor/assets/less/richeditor.less b/modules/backend/formwidgets/richeditor/assets/less/richeditor.less index 002d8b539..97d8b7e9e 100755 --- a/modules/backend/formwidgets/richeditor/assets/less/richeditor.less +++ b/modules/backend/formwidgets/richeditor/assets/less/richeditor.less @@ -54,6 +54,14 @@ @btn-active-text: #000; @input-label-color: @color-label; +// +// Make the focus ring and textarea fill the whole rich editor container +// + +.fr-element { + height: 100%; +} + // // Rich Editor // diff --git a/modules/backend/formwidgets/richeditor/partials/_richeditor.htm b/modules/backend/formwidgets/richeditor/partials/_richeditor.htm index 31f2b2646..5b7d0c63c 100755 --- a/modules/backend/formwidgets/richeditor/partials/_richeditor.htm +++ b/modules/backend/formwidgets/richeditor/partials/_richeditor.htm @@ -16,11 +16,11 @@ data-no-wrap-tags="" data-remove-tags="" data-line-breaker-tags="" - data-image-styles="" - data-link-styles="" - data-paragraph-styles="" - data-table-styles="" - data-table-cell-styles="" + data-image-styles="" + data-link-styles="" + data-paragraph-styles="" + data-table-styles="" + data-table-cell-styles="" data-links-handler="getEventHandler('onLoadPageLinksForm') ?>" data-ace-vendor-path="" data-control="richeditor"> diff --git a/modules/backend/formwidgets/taglist/partials/_taglist.htm b/modules/backend/formwidgets/taglist/partials/_taglist.htm index 2e491c6dc..de6e000c7 100644 --- a/modules/backend/formwidgets/taglist/partials/_taglist.htm +++ b/modules/backend/formwidgets/taglist/partials/_taglist.htm @@ -3,20 +3,37 @@ $availableOptions = $useKey ? $fieldOptions : array_unique(array_merge($selectedValues, $fieldOptions)); ?> - +previewMode || $field->readOnly || $field->disabled): ?> +
    readOnly || $field->disabled ? 'disabled="disabled"' : ''; ?>> + $option): ?> + + +
  • + + +
+ readOnly): ?> + + + + + diff --git a/modules/backend/helpers/Backend.php b/modules/backend/helpers/Backend.php index 095207d46..5d86cda82 100644 --- a/modules/backend/helpers/Backend.php +++ b/modules/backend/helpers/Backend.php @@ -8,6 +8,7 @@ use Redirect; use October\Rain\Router\Helper as RouterHelper; use System\Helpers\DateTime as DateTimeHelper; use Backend\Classes\Skin; +use Backend\Helpers\Exception\DecompileException; /** * Backend Helper @@ -118,8 +119,8 @@ class Backend 'timeSince' => false, 'ignoreTimezone' => false, ], $options)); - - if(!$dateTime) { + + if (!$dateTime) { return ''; } @@ -157,4 +158,58 @@ class Backend return ''.e($defaultValue).''.PHP_EOL; } + /** + * Decompiles the compilation asset files + * + * This is used to load each individual asset file, as opposed to using the compilation assets. This is useful only + * for development, to allow developers to test changes without having to re-compile assets. + * + * @param string $file The compilation asset file to decompile + * @param boolean $skinAsset If true, will load decompiled assets from the "skins" directory. + * @throws DecompileException If the compilation file cannot be decompiled + * @return array + */ + public function decompileAsset(string $file, bool $skinAsset = false) + { + if ($skinAsset) { + $assetFile = base_path(substr(Skin::getActive()->getPath($file, true), 1)); + } else { + $assetFile = base_path($file); + } + + if (!file_exists($assetFile)) { + throw new DecompileException('File ' . $file . ' does not exist to be decompiled.'); + } + if (!is_readable($assetFile)) { + throw new DecompileException('File ' . $file . ' cannot be decompiled. Please allow read access to the file.'); + } + + $contents = file_get_contents($assetFile); + + if (!preg_match('/^=require/m', $contents)) { + throw new DecompileException('File ' . $file . ' does not appear to be a compiled asset.'); + } + + // Find all assets that are compiled in this file + preg_match_all('/^=require\s+([A-z0-9-_+\.\/]+)$/m', $contents, $matches, PREG_SET_ORDER); + + if (!count($matches)) { + throw new DecompileException('Unable to extract any asset paths when decompiled file ' . $file . '.'); + } + + // Determine correct asset path + $directory = str_replace(basename($file), '', $file); + + return array_map(function ($match) use ($directory, $skinAsset) { + // Resolve relative asset paths + if ($skinAsset) { + $assetPath = base_path(substr(Skin::getActive()->getPath($directory . $match[1], true), 1)); + } else { + $assetPath = base_path($directory . $match[1]); + } + $realPath = str_replace(base_path(), '', realpath($assetPath)); + + return Url::asset($realPath); + }, $matches); + } } diff --git a/modules/backend/helpers/exception/DecompileException.php b/modules/backend/helpers/exception/DecompileException.php new file mode 100644 index 000000000..08e620c20 --- /dev/null +++ b/modules/backend/helpers/exception/DecompileException.php @@ -0,0 +1,7 @@ + 'إعادة تحميل', 'complete' => 'تم', 'select' => 'تحديد', - 'select_all' => 'تحديد الكل', - 'select_none' => 'إلغاء التحديد', + 'select_all' => 'اختر الكل', + 'select_none' => 'لا تختر شيء', 'select_placeholder' => 'من فضلك اختار', 'insert_row' => 'إضافة سجل', 'return_to_list' => 'عودة للقائمة', diff --git a/modules/backend/lang/be/lang.php b/modules/backend/lang/be/lang.php index 8b76dc9ce..712215754 100644 --- a/modules/backend/lang/be/lang.php +++ b/modules/backend/lang/be/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "Неабходна база дадзеных мець доступ у панэль кіравання. Праверце, што база дадзеных правільна наладжаная, а міграцыі выкананыя, перад тым, як пасправаць зноў", 'cms_link' => "Вярнуцца на хатнюю старонку" ], - 'invalid_token' => [ - 'label' => "Няправільны токен бяспекі" - ] ], 'partial' => [ 'not_found_name' => "Частковы шаблон \":name\" не знойдзены." @@ -240,8 +237,8 @@ return [ 'preview_no_files_message' => "Няма загружаных файлаў", 'preview_no_record_message' => "Няма выбраных запісаў", 'select' => "Выбраць", - 'select_all' => "усё", - 'select_none' => "нічога", + 'select_all' => "абраць усё", + 'select_none' => "выбраць няма", 'select_placeholder' => "Калі ласка, выберыце", 'insert_row' => "Уставіць радок", 'insert_row_below' => "Уставіць радок ніжэй", diff --git a/modules/backend/lang/bg/lang.php b/modules/backend/lang/bg/lang.php index 308d875a9..31f924e44 100644 --- a/modules/backend/lang/bg/lang.php +++ b/modules/backend/lang/bg/lang.php @@ -19,9 +19,6 @@ return [ 'help' => "Вие нямате нужните права за да видите тази страница.", 'cms_link' => 'Върни се към администраторския-панел' ], - 'invalid_token' => [ - 'label' => 'Невалиден код за сигурност (token)' - ] ], 'partial' => [ 'not_found_name' => "Частичната страница ':name' не е намерена." @@ -201,8 +198,8 @@ return [ 'preview_no_files_message' => 'Все още няма качени файлове.', 'preview_no_record_message' => 'Не е избран запис.', 'select' => 'Избери', - 'select_all' => 'всичко', - 'select_none' => 'нищо', + 'select_all' => 'Избери всички', + 'select_none' => 'изберете никой', 'select_placeholder' => 'моля изберете', 'insert_row' => 'Вмъкни ред', 'insert_row_below' => 'Вмъкни ред По-долу', diff --git a/modules/backend/lang/ca/lang.php b/modules/backend/lang/ca/lang.php index 7cfbaf269..5e0a041e4 100644 --- a/modules/backend/lang/ca/lang.php +++ b/modules/backend/lang/ca/lang.php @@ -268,8 +268,8 @@ return [ 'preview_no_media_message' => 'No hi ha medis seleccionats.', 'preview_no_record_message' => 'No hi ha cap registre seleccionat.', 'select' => 'Seleccionar', - 'select_all' => 'tots', - 'select_none' => 'cap', + 'select_all' => 'seleccionar tot', + 'select_none' => 'no selecciona cap', 'select_placeholder' => 'si us plau selecciona', 'insert_row' => 'Inserir fila', 'insert_row_below' => 'Inserir fila a sota', diff --git a/modules/backend/lang/cs/lang.php b/modules/backend/lang/cs/lang.php index a1b230f83..fb2312b5a 100644 --- a/modules/backend/lang/cs/lang.php +++ b/modules/backend/lang/cs/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "K přístupu do administrace je zapotřebí databáze. Zkontrolujte, zda je databáze nakonfigurována a migrována a zkuste to znovu.", 'cms_link' => 'Zpět na úvodní stránku' ], - 'invalid_token' => [ - 'label' => 'Neplatný bezpečnostní token', - ] ], 'partial' => [ 'not_found_name' => "Dílčí šablona ':name' nebyla nalezena." @@ -262,8 +259,8 @@ return [ 'preview_no_media_message' => 'Žádné médium nebylo vybráno.', 'preview_no_record_message' => 'Žádný záznam není vybraný.', 'select' => 'Vybrat', - 'select_all' => 'Vše', - 'select_none' => 'Nic', + 'select_all' => 'vybrat vše', + 'select_none' => 'vyberte žádný', 'select_placeholder' => 'Prosím vyberte', 'insert_row' => 'Vložit řádek', 'insert_row_below' => 'Vložit řádek pod', diff --git a/modules/backend/lang/da/lang.php b/modules/backend/lang/da/lang.php index ca12b906c..03747670e 100644 --- a/modules/backend/lang/da/lang.php +++ b/modules/backend/lang/da/lang.php @@ -24,9 +24,6 @@ return [ 'help' => "En database er påkrævet for at kunne tilgå backenden. Kontroller om databasen er konfigureret og migreret, inden du prøver igen.", 'cms_link' => 'Tilbage til hjemmesiden' ], - 'invalid_token' => [ - 'label' => 'Ugyldig sikkerhedstoken' - ] ], 'partial' => [ 'not_found_name' => "Partialen ':name' kunne ikke findes." @@ -235,8 +232,8 @@ return [ 'preview_no_files_message' => 'Der er ikke uploadet nogle filer.', 'preview_no_record_message' => 'Der er ikke valgt nogen record.', 'select' => 'Vælg', - 'select_all' => 'alle', - 'select_none' => 'ingen', + 'select_all' => 'Vælg alle', + 'select_none' => 'vælg ingen', 'select_placeholder' => 'Vælg venligst', 'insert_row' => 'Indsæt Række', 'insert_row_below' => 'Indsæt Række Nedeunder', diff --git a/modules/backend/lang/de/lang.php b/modules/backend/lang/de/lang.php index 5a170f868..ce2018937 100644 --- a/modules/backend/lang/de/lang.php +++ b/modules/backend/lang/de/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "Eine Datenbank wird benötigt um Zugriff auf das Backend zu haben. Überprüfe die Datenbankkonfiguration und migriere die Datenbank bevor du es noch einmal probierst.", 'cms_link' => 'Zurück zur Homepage' ], - 'invalid_token' => [ - 'label' => 'Ungültiges Sicherheitstoken' - ] ], 'partial' => [ 'not_found_name' => "Das Partial ':name' wurde nicht gefunden.", @@ -94,7 +91,7 @@ return [ 'request_log' => 'Request Log', 'app_birthday' => 'Online seit', ], - 'welcome' => [ + 'welcome' => [ 'widget_title_default' => 'Willkommen', 'welcome_back_name' => 'Willkommen zurück zu :app, :name.', 'welcome_to_name' => 'Willkommen zu :app, :name.', @@ -134,7 +131,7 @@ return [ 'activated' => 'Aktiviert', 'last_login' => 'Letzer login', 'created_at' => 'Erstellt am', - 'updated_at' => 'Aktualisiert am', + 'updated_at' => 'Aktualisiert am', 'group' => [ 'name' => 'Gruppe', 'name_comment' => 'Der Name, der angezeigt wird wenn name is displayed in the group list on the Create/Edit Administrator form.', @@ -245,8 +242,8 @@ return [ 'preview_no_media_message' => 'Es wurde keine Media-Datei ausgewählt.', 'preview_no_record_message' => 'Es ist kein Eintrag ausgewählt.', 'select' => 'Auswählen', - 'select_all' => 'Alle', - 'select_none' => 'Keine', + 'select_all' => 'Wählen Sie Alle', + 'select_none' => 'nichts ausgewählt', 'select_placeholder' => 'Bitte auswählen', 'insert_row' => 'Reihe einfügen', 'insert_row_below' => 'Neue Reihe darunter einfügen', @@ -262,7 +259,7 @@ return [ 'pagelist' => [ 'page_link' => 'Seitenlink', 'select_page' => 'Wähle eine Seite...' - ], + ], 'relation' => [ 'missing_config' => "Verhalten (behaviour) der Verbindung hat keine Konfiguration für ':config'.", 'missing_definition' => "Verhalten (behaviour) der Verbindung umfasst keine Definition für ':field'.", diff --git a/modules/backend/lang/el/lang.php b/modules/backend/lang/el/lang.php index ac89c3af7..71163c21b 100644 --- a/modules/backend/lang/el/lang.php +++ b/modules/backend/lang/el/lang.php @@ -19,9 +19,6 @@ return [ 'help' => "Δεν έχεις τα απαραίτητα δικαιώματα για να δεις αυτήν την σελίδα.", 'cms_link' => 'Επιστροφή στό back-end.', ], - 'invalid_token' => [ - 'label' => 'Μη έγκυρο διακριτικό ασφαλείας', - ], 'no_database' => [ 'label' => 'Δεν βρέθηκε η βάση δεδομένων', 'help' => "Η βάση δεδομένων είναι απαραίτητη για να έχετε πρόσβαση στο back-end. Ελέγξτε εάν η βάση δεδομένων είναι ρυθμισμένη και συνδεδεμένη πριν προσπαθήσετε ξανά.", @@ -235,8 +232,8 @@ return [ 'preview_no_files_message' => 'Δεν υπάρχουν αρχεία που ανέβηκαν.', 'preview_no_record_message' => 'Δεν είναι επιλεγμένη καμία εγγραφή.', 'select' => 'Επιλογή', - 'select_all' => 'όλα', - 'select_none' => 'κανένα', + 'select_all' => 'επιλογή όλων', + 'select_none' => 'επιλέξτε κανένα', 'select_placeholder' => 'παρακαλούμε επιλέξτε', 'insert_row' => 'Προσθήκη Σειράς', 'insert_row_below' => 'Προσθήκη Σειράς από Κάτω', diff --git a/modules/backend/lang/en/lang.php b/modules/backend/lang/en/lang.php index f0795a2b1..5aacad895 100644 --- a/modules/backend/lang/en/lang.php +++ b/modules/backend/lang/en/lang.php @@ -32,9 +32,6 @@ return [ 'help' => "A database is required to access the back-end. Check the database is configured and migrated before trying again.", 'cms_link' => 'Return to the homepage', ], - 'invalid_token' => [ - 'label' => 'Invalid security token', - ], ], 'partial' => [ 'not_found_name' => "The partial ':name' is not found.", @@ -239,6 +236,7 @@ return [ 'remove_file' => 'Remove file' ], 'repeater' => [ + 'add_new_item' => 'Add new item', 'min_items_failed' => ':name requires a minimum of :min items, only :items were provided', 'max_items_failed' => ':name only allows up to :max items, :items were provided', ], @@ -293,8 +291,8 @@ return [ 'preview_no_media_message' => 'There is no media selected.', 'preview_no_record_message' => 'There is no record selected.', 'select' => 'Select', - 'select_all' => 'all', - 'select_none' => 'none', + 'select_all' => 'Select all', + 'select_none' => 'Select none', 'select_placeholder' => 'please select', 'insert_row' => 'Insert Row', 'insert_row_below' => 'Insert Row Below', @@ -364,6 +362,8 @@ return [ 'permissions' => 'Directory :name or its subdirectories is not writable for PHP. Please set corresponding permissions for the webserver on this directory.', 'extension' => 'The PHP extension :name is not installed. Please install this library and activate the extension.', 'plugin_missing' => 'The plugin :name is a dependency but is not installed. Please install this plugin.', + 'debug' => 'Debug mode is enabled. This is not recommended for production installations.', + 'decompileBackendAssets' => 'Assets in the Backend are currently decompiled. This is not recommended for production installations.', ], 'editor' => [ 'menu_label' => 'Editor settings', diff --git a/modules/backend/lang/es-ar/lang.php b/modules/backend/lang/es-ar/lang.php index 1bab0ad4a..ec5d20993 100644 --- a/modules/backend/lang/es-ar/lang.php +++ b/modules/backend/lang/es-ar/lang.php @@ -161,8 +161,8 @@ return [ 'behavior_not_ready' => 'Por favor compruebe que ha llamado a la funcion initForm() en el controlador.', 'preview_no_files_message' => 'Los archivos no fueron cargados.', 'select' => 'Seleccionar', - 'select_all' => 'Todo', - 'select_none' => 'Ninguno', + 'select_all' => 'seleccionar todo', + 'select_none' => 'no seleccionar ninguno', 'select_placeholder' => 'Seleccionar', 'insert_row' => 'Insertar fila', 'delete_row' => 'Eliminar fila', diff --git a/modules/backend/lang/es/lang.php b/modules/backend/lang/es/lang.php index 2fb07690b..2e5764c61 100644 --- a/modules/backend/lang/es/lang.php +++ b/modules/backend/lang/es/lang.php @@ -19,9 +19,6 @@ return [ 'help' => 'No tiene permisos necesarios para ver esta página.', 'cms_link' => 'Volver al panel de administración' ], - 'invalid_token' => [ - 'label' => 'Token de seguridad invalido' - ] ], 'partial' => [ 'not_found_name' => "El parcial ':name' no se encuentra." @@ -214,8 +211,8 @@ return [ 'resetting' => 'Restableciendo', 'resetting_name' => 'Restableciendo :name', 'undefined_tab' => 'Varios', - 'field_off' => 'Off', - 'field_on' => 'On', + 'field_off' => 'Apagar', + 'field_on' => 'Encender', 'add' => 'Agregar', 'apply' => 'Aplicar', 'cancel' => 'Cancelar', @@ -230,8 +227,8 @@ return [ 'preview_no_files_message' => 'Los archivos no se han subido', 'preview_no_record_message' => 'No hay ningún registro seleccionado.', 'select' => 'Seleccionar', - 'select_all' => 'todos', - 'select_none' => 'ninguno', + 'select_all' => 'seleccionar todo', + 'select_none' => 'no seleccionar ninguno', 'select_placeholder' => 'por favor seleccione', 'insert_row' => 'Agregar Fila', 'insert_row_below' => 'Insertar fila debajo', @@ -394,7 +391,7 @@ return [ 'ip_address' => 'IP', 'first_name' => 'Nombre', 'last_name' => 'Apellido', - 'email' => 'Email' + 'email' => 'Correo' ], 'filter' => [ 'all' => 'todo', @@ -474,14 +471,14 @@ return [ ] ], 'permissions' => [ - 'manage_media' => 'Subir y gestionar contenidos multimedia - imágenes, vídeos, sonidos y documentos' + 'manage_media' => 'Subir y gestionar contenidos multimedia - imágenes, vídeos, sonidos y documentos' ], 'mediafinder' => [ 'label' => 'Buscador de multimedia', 'default_prompt' => 'Haga clic en el botón %s para buscar un elemento multimedia', ], 'media' => [ - 'menu_label' => 'Media', + 'menu_label' => 'Medios', 'upload' => 'Subir', 'move' => 'Mover', 'delete' => 'Eliminar', diff --git a/modules/backend/lang/et/lang.php b/modules/backend/lang/et/lang.php index 9d1e0795c..765ce9eb4 100644 --- a/modules/backend/lang/et/lang.php +++ b/modules/backend/lang/et/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "Haldusliidese kasutamiseks peab olemas seadistatud andmebaas. Enne jätkamist palun kontrolli andmebaasi seadistust.", 'cms_link' => 'Tagasi kodulehele' ], - 'invalid_token' => [ - 'label' => 'Vigane turvamärk' - ] ], 'partial' => [ 'not_found_name' => "Koodiblokki ':name' ei leitud." @@ -245,8 +242,8 @@ return [ 'preview_no_media_message' => 'Ühtegi meediafaili pole valitud.', 'preview_no_record_message' => 'Ühtegi kirjet pole valitud.', 'select' => 'Vali', - 'select_all' => 'kõik', - 'select_none' => 'mitte ükski', + 'select_all' => 'Vali kõik', + 'select_none' => 'vali ükski', 'select_placeholder' => 'palun vali', 'insert_row' => 'Lisa rida', 'insert_row_below' => 'Lisa rida alla', diff --git a/modules/backend/lang/fa/lang.php b/modules/backend/lang/fa/lang.php index e8c5f005a..a038ad088 100644 --- a/modules/backend/lang/fa/lang.php +++ b/modules/backend/lang/fa/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "جهت استفاده از بخش مدیریت به یک پایگاه داده نیاز دارید. تنظیمات پایگاه داده را بررسی نموده و از نصب جدولها در آن اطمینان حاصل کنید.", 'cms_link' => 'Return to the homepage' ], - 'invalid_token' => [ - 'label' => 'کلید امنیتی معتبر نمی باشد' - ] ], 'partial' => [ 'not_found_name' => "بخشی با نام ':name' یافت نشد.", @@ -263,8 +260,8 @@ return [ 'preview_no_media_message' => 'رسانه ای انتخاب نشده است.', 'preview_no_record_message' => 'موردی انتخاب نشده است.', 'select' => 'انتخاب', - 'select_all' => 'همه', - 'select_none' => 'هیچ', + 'select_all' => 'انتخاب همه', + 'select_none' => 'هیچ کدام را انتخاب نکنید', 'select_placeholder' => 'لطفا انتخاب نمایید', 'insert_row' => 'افزودن سطر', 'insert_row_below' => 'افزودن سطر بعد از', diff --git a/modules/backend/lang/fi/lang.php b/modules/backend/lang/fi/lang.php index a2739683f..e21e8fe37 100644 --- a/modules/backend/lang/fi/lang.php +++ b/modules/backend/lang/fi/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "Tietokanta on pakollinen. Varmista, että tietokanta on olemassa ja pystyssä kunnes kokeilet uudelleen.", 'cms_link' => 'Palaa kotisivulle' ], - 'invalid_token' => [ - 'label' => 'Token on virheellinen' - ] ], 'partial' => [ 'not_found_name' => "Osiota ':name' ei löydy." @@ -263,8 +260,8 @@ return [ 'preview_no_media_message' => 'Ei valittua mediaa.', 'preview_no_record_message' => 'Ei valittua tietuetta.', 'select' => 'Valitse', - 'select_all' => 'kaikki', - 'select_none' => 'ei yhtään', + 'select_all' => 'Valitse kaikki', + 'select_none' => 'Älä valitse mitään', 'select_placeholder' => 'ole hyvä ja valitse', 'insert_row' => 'Lisää rivi', 'insert_row_below' => 'Lisää rivi alapuolelle', diff --git a/modules/backend/lang/fr/lang.php b/modules/backend/lang/fr/lang.php index 30bd7473c..ffb2bb1ee 100644 --- a/modules/backend/lang/fr/lang.php +++ b/modules/backend/lang/fr/lang.php @@ -27,9 +27,6 @@ return [ 'help' => 'Une base de données est requise pour l’accès à l’interface d’administration. Veuillez vérifier que la base de données existe et que les migrations ont été effectuées avant de ré-essayer.', 'cms_link' => 'Retour à l’accueil' ], - 'invalid_token' => [ - 'label' => 'La clé de sécurité est invalide' - ] ], 'partial' => [ 'not_found_name' => 'Le modèle partiel ":name" est introuvable.' @@ -277,8 +274,8 @@ return [ 'preview_no_media_message' => 'Aucun média sélectionné.', 'preview_no_record_message' => 'Il n’y a aucun enregistrement sélectionné.', 'select' => 'Sélectionner', - 'select_all' => 'tout', - 'select_none' => 'aucun', + 'select_all' => 'tout sélectionner', + 'select_none' => 'Ne rien sélectionner', 'select_placeholder' => 'Sélectionner une valeur', 'insert_row' => 'Insérer une ligne', 'insert_row_below' => 'Insérer une ligne dessous', diff --git a/modules/backend/lang/hu/lang.php b/modules/backend/lang/hu/lang.php index 407759a20..9c326b99b 100644 --- a/modules/backend/lang/hu/lang.php +++ b/modules/backend/lang/hu/lang.php @@ -24,7 +24,7 @@ return [ ], 'access_denied' => [ 'label' => 'Hozzáférés megtagadva', - 'help' => 'Ön nem rendelkezik a szükséges engedélyekkel ennek a lapnak a megtekintéséhez.', + 'help' => 'Nem rendelkezik a szükséges engedélyekkel ennek a lapnak a megtekintéséhez.', 'cms_link' => 'Vissza a látogatói oldalra' ], 'no_database' => [ @@ -32,9 +32,6 @@ return [ 'help' => 'Kérjük ellenőrizze a hozzáférési adatok helyességét majd próbálja újra betölteni az oldalt.', 'cms_link' => 'Vissza a weboldalra' ], - 'invalid_token' => [ - 'label' => 'A biztonsági kód érvényessége lejárt. Kérjük töltse be újra az oldalt.' - ] ], 'partial' => [ 'not_found_name' => "A(z) ':name' részlap nem található.", @@ -45,6 +42,12 @@ return [ 'not_found' => "A(z) ':name' AJAX handler nem található." ], 'account' => [ + 'impersonate' => 'Átjelentkezés a fiókba', + 'impersonate_confirm' => 'Biztos benne, hogy átjelentkezik a felhasználó saját fiókjába? Ezáltal a jelenlegi munkamenetből ki lesz jelentkeztetve.', + 'impersonate_success' => 'Sikeresen átjelentkezett a másik fiókba', + 'impersonate_working' => 'Átjelentkezés...', + 'impersonating' => 'Átjelentkezve mint :full_name', + 'stop_impersonating' => 'Visszajelentkezés', 'signed_in_as' => 'Belépve mint :full_name', 'sign_out' => 'Kijelentkezés', 'login' => 'Belépés', @@ -137,12 +140,12 @@ return [ 'password' => 'Jelszó', 'password_confirmation' => 'Jelszó megerősítése', 'permissions' => 'Engedélyek', - 'account' => 'Fiók', + 'account' => 'Profil', 'superuser' => 'Szuperadmin', 'superuser_comment' => 'Korlátlan hozzáférést biztosít az admin felülethez.', 'send_invite' => 'Meghívó küldése e-mailben', 'send_invite_comment' => 'Csak a belépéshez szükséges adatokat tartalmazza.', - 'delete_confirm' => 'Valóban törölni akarja az admint?', + 'delete_confirm' => 'Valóban törölni akarja ezt a felhasználót?', 'return' => 'Vissza az adminokhoz', 'allow' => 'Engedélyezés', 'inherit' => 'Öröklés', @@ -283,18 +286,18 @@ return [ 'or' => 'vagy', 'confirm_tab_close' => 'Valóban be akarja zárni a fület? El fognak veszni a nem mentett módosítások.', 'behavior_not_ready' => 'Nem történt meg az űrlap viselkedésének inicializálása. Kérjük ellenőrizze, hogy meghívta-e az initForm() függvényt a vezérlőben.', - 'preview_no_files_message' => 'Nincsenek feltöltve fájlok.', - 'preview_no_media_message' => 'Nincs kiválasztva média.', - 'preview_no_record_message' => 'Nincs kiválasztva mező.', + 'preview_no_files_message' => 'Nincs megadva fájl.', + 'preview_no_media_message' => 'Nincs megadva kép.', + 'preview_no_record_message' => 'Nincs megadva mező.', 'select' => 'Kiválaszt', - 'select_all' => 'mind', + 'select_all' => 'mindegyik', 'select_none' => 'egyik sem', 'select_placeholder' => 'válasszon', 'insert_row' => 'Sor beszúrása', 'insert_row_below' => 'Sor beszúrása alá', 'delete_row' => 'Sor törlése', 'concurrency_file_changed_title' => 'A fájl megváltozott', - 'concurrency_file_changed_description' => 'Az Ön által szerkesztett fájlt már egy másik felhasználó módosította. Újratöltheti a fájlt és elveszti a változtatásait, vagy felülírja a fájlt.', + 'concurrency_file_changed_description' => 'A jelenleg szerkesztett fájlt egy másik felhasználó már módosította. Újratöltheti és elveszti a változtatásait, vagy felülírja a fájlt.', 'return_to_list' => 'Vissza a listához' ], 'recordfinder' => [ @@ -353,11 +356,13 @@ return [ 'mass_assignment_failed' => "A tömeges hozzárendelés a(z) ':attribute' modell attribútumhoz nem sikerült." ], 'warnings' => [ - 'tips' => 'Rendszer beállítási tippek', - 'tips_description' => 'Olyan problémák vannak, melyekre figyeljen oda a rendszer megfelelő működése érdekében.', + 'tips' => 'Beállítási tippek', + 'tips_description' => 'Az alábbi dolgokra figyeljen oda a rendszer megfelelő működése érdekében.', 'permissions' => 'A(z) :name könyvtár vagy alkönyvtárai a PHP számára nem írhatóak. Adjon megfelelő engedélyeket a kiszolgálónak erre a könyvtárra.', 'extension' => 'A(z) :name PHP kiterjesztés nincs telepítve. Telepítse ezt a függvénytárat és aktiválja a kiterjesztést.', - 'plugin_missing' => 'A(z) :name bővítményre szükség van, de nincs telepítve. Kérjük telepítse ezt a bővítményt.' + 'plugin_missing' => 'A(z) :name bővítményre szükség van, de nincs telepítve. Kérjük telepítse ezt a bővítményt.', + 'debug' => 'A hibakeresési mód engedélyezve van. Ez nem ajánlott éles weboldal esetén.', + 'decompileBackendAssets' => 'Az admin felülethez tartozó fájlok nem véglegesek. Ez nem ajánlott éles weboldal esetén.' ], 'editor' => [ 'menu_label' => 'Szövegszerkesztő', @@ -618,5 +623,5 @@ return [ 'resize_image' => 'Kép átméretezése', 'image_size' => 'Kép mérete:', 'selected_size' => 'Kiválasztva:' - ], + ] ]; diff --git a/modules/backend/lang/id/lang.php b/modules/backend/lang/id/lang.php index 26a81cd14..b3e7ce491 100644 --- a/modules/backend/lang/id/lang.php +++ b/modules/backend/lang/id/lang.php @@ -180,8 +180,8 @@ return [ 'behavior_not_ready' => 'Behavior borang belum diinisialisasi, periksa apakah Anda telah memanggil initForm() pada controller Anda.', 'preview_no_files_message' => 'Berkas tidak terunggah', 'select' => 'Pilih', - 'select_all' => 'semua', - 'select_none' => 'tiada', + 'select_all' => 'Pilih Semua', + 'select_none' => 'Pilih tidak ada', 'select_placeholder' => 'silakan pilih', 'insert_row' => 'Sisipkan Baris', 'delete_row' => 'Hapus Baris', diff --git a/modules/backend/lang/it/lang.php b/modules/backend/lang/it/lang.php index a84633195..022bdbb33 100644 --- a/modules/backend/lang/it/lang.php +++ b/modules/backend/lang/it/lang.php @@ -19,9 +19,6 @@ return [ 'help' => "Non hai le autorizzazioni necessarie per accedere a questa pagina.", 'cms_link' => 'Ritorna al pannello di controllo' ], - 'invalid_token' => [ - 'label' => 'Token di protezione non valido' - ] ], 'partial' => [ 'not_found_name' => "La vista parziale ':name' non è stata trovata." @@ -230,8 +227,8 @@ return [ 'preview_no_files_message' => 'Non ci sono file caricati.', 'preview_no_record_message' => 'Nessun record selezionato.', 'select' => 'Seleziona', - 'select_all' => 'tutti', - 'select_none' => 'nessuno', + 'select_all' => 'seleziona tutto', + 'select_none' => 'non selezionare niente', 'select_placeholder' => 'seleziona', 'insert_row' => 'Inserisci riga', 'insert_row_below' => 'Inserisci riga sotto', diff --git a/modules/backend/lang/ja/lang.php b/modules/backend/lang/ja/lang.php index e67ec5a8a..affe10b25 100644 --- a/modules/backend/lang/ja/lang.php +++ b/modules/backend/lang/ja/lang.php @@ -176,8 +176,8 @@ return [ 'behavior_not_ready' => 'フォームビヘイビアーは初期化されていません。コントローラーでinitForm()を呼び出しているか確認してください。', 'preview_no_files_message' => 'ファイルはアップロードされません。', 'select' => '選択', - 'select_all' => 'すべて', - 'select_none' => 'なし', + 'select_all' => 'すべて選択', + 'select_none' => 'どれも選択しない', 'select_placeholder' => '選択してください', 'insert_row' => '行を挿入', 'delete_row' => '行を削除', diff --git a/modules/backend/lang/kr/lang.php b/modules/backend/lang/kr/lang.php index e18099611..4a152dce3 100644 --- a/modules/backend/lang/kr/lang.php +++ b/modules/backend/lang/kr/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "백엔드에 접속하기 위해선 DB가 필요합니다. 다시 접속하시기 전에 DB가 정상적으로 설정및 이전되었는지 확인해주세요.", 'cms_link' => '홈페이지로 돌아가기' ], - 'invalid_token' => [ - 'label' => '잘못된 보안 토큰' - ] ], 'partial' => [ 'not_found_name' => "':name' 페이지를 찾을 수 없습니다.", @@ -244,8 +241,8 @@ return [ 'preview_no_media_message' => '선택하신 미디어가 없습니다.', 'preview_no_record_message' => '선택하신 기록이 없습니다.', 'select' => '선택', - 'select_all' => '전체선택', - 'select_none' => '선택없음', + 'select_all' => '모두 선택', + 'select_none' => '없음을 선택하십시오', 'select_placeholder' => '선택해주세요', 'insert_row' => '행 추가', 'insert_row_below' => '아래 행 추가', diff --git a/modules/backend/lang/lt/lang.php b/modules/backend/lang/lt/lang.php index 94ad6c446..cd8ffc1ba 100644 --- a/modules/backend/lang/lt/lang.php +++ b/modules/backend/lang/lt/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "Duomenų bazė reikalinga administracijos prieigai. Patikrinkite duomenų bazės konfigūraciją bei migracijas ir bandykite dar kartą.", 'cms_link' => 'Grįžti į svetainę' ], - 'invalid_token' => [ - 'label' => 'Netinkama saugos žyma' - ] ], 'partial' => [ 'not_found_name' => "Priedėlis ':name' nerastas." @@ -244,8 +241,8 @@ return [ 'preview_no_files_message' => 'Nėra įkeltų failų.', 'preview_no_record_message' => 'Nėra pasirinktų įrašų.', 'select' => 'Pasirinkti', - 'select_all' => 'viską', - 'select_none' => 'nieko', + 'select_all' => 'pasirinkti viską', + 'select_none' => 'neišsirinkite nė vieno', 'select_placeholder' => 'prašome pasirinkti', 'insert_row' => 'Pridėti Eilutę', 'insert_row_below' => 'Pridėti Eilutę Žemiau', diff --git a/modules/backend/lang/lv/lang.php b/modules/backend/lang/lv/lang.php index 6342a91bd..f70737694 100644 --- a/modules/backend/lang/lv/lang.php +++ b/modules/backend/lang/lv/lang.php @@ -19,9 +19,6 @@ return [ 'help' => "Jums nav piekļuves tiesību, lai skatītu šo lapu.", 'cms_link' => 'Atgriezties back-end' ], - 'invalid_token' => [ - 'label' => 'Nederīga drošības atslēga' - ] ], 'partial' => [ 'not_found_name' => "Daļa ':name' nav atrasta." @@ -202,8 +199,8 @@ return [ 'preview_no_files_message' => 'Faili nav augšupielādēti', 'preview_no_record_message' => 'Nav izvēlētu ierakstu.', 'select' => 'Izvēlēties', - 'select_all' => 'visus', - 'select_none' => 'nevienu', + 'select_all' => 'izvēlēties visus', + 'select_none' => 'neizvēlēties nevienu', 'select_placeholder' => 'lūdzu izvēlieties', 'insert_row' => 'Ievietot rindu', 'insert_row_below' => 'Ievietot riendu zemāk', diff --git a/modules/backend/lang/nb-no/lang.php b/modules/backend/lang/nb-no/lang.php index 47f8d2009..717418570 100644 --- a/modules/backend/lang/nb-no/lang.php +++ b/modules/backend/lang/nb-no/lang.php @@ -24,9 +24,6 @@ return [ 'help' => "En database kreves for å koble til backend. Sjekk at databasetilgang er konfigurert og migrert før du prøver igjen.", 'cms_link' => 'Tilbake til hovedsiden' ], - 'invalid_token' => [ - 'label' => 'Ugyldig sikkerhets-token' - ] ], 'partial' => [ 'not_found_name' => "En partial ved navn ':name' ble ikke funnet." @@ -239,8 +236,8 @@ return [ 'preview_no_files_message' => 'Filer er ikke opplastet', 'preview_no_record_message' => 'Det er ingen valgte oppføringer.', 'select' => 'Velg', - 'select_all' => 'alle', - 'select_none' => 'ingen', + 'select_all' => 'Velg alle', + 'select_none' => 'Velg ingen', 'select_placeholder' => 'velg', 'insert_row' => 'Sett inn rad', 'insert_row_below' => 'Sett inn rad under', diff --git a/modules/backend/lang/nl/lang.php b/modules/backend/lang/nl/lang.php index 3f52b7723..fcfdd2e84 100644 --- a/modules/backend/lang/nl/lang.php +++ b/modules/backend/lang/nl/lang.php @@ -32,9 +32,6 @@ return [ 'help' => 'Een database is nodig om toegang te krijgen tot de back-end. Controleer of de database juist is geconfigureerd en probeer het opnieuw.', 'cms_link' => 'Terug naar homepagina', ], - 'invalid_token' => [ - 'label' => 'Ongeldig token', - ], ], 'partial' => [ 'not_found_name' => "Het sjabloon (partial) ':name' is niet gevonden.", @@ -45,6 +42,12 @@ return [ 'not_found' => "Het AJAX verzoek ':name' kon niet worden gevonden." ], 'account' => [ + 'impersonate' => 'Inloggen als', + 'impersonate_confirm' => 'Je gaat inloggen als deze gebruiker. Weet je het zeker? Keer terug door uit te loggen.', + 'impersonate_success' => 'Je bent nu ingelogd als deze gebruiker', + 'impersonate_working' => 'Inloggen als...', + 'impersonating' => 'Inloggen als :full_name', + 'stop_impersonating' => 'Terugkeren', 'signed_in_as' => 'Ingelogd als :full_name', 'sign_out' => 'Uitloggen', 'login' => 'Inloggen', @@ -287,8 +290,8 @@ return [ 'preview_no_media_message' => 'Er zijn geen media geselecteerd.', 'preview_no_record_message' => 'Er zijn geen records geselecteerd.', 'select' => 'Selecteer', - 'select_all' => 'alles', - 'select_none' => 'niets', + 'select_all' => 'selecteer alles', + 'select_none' => 'selecteer niets', 'select_placeholder' => 'selecteer', 'insert_row' => 'Rij invoegen', 'insert_row_below' => 'Rij onder invoegen', @@ -358,6 +361,8 @@ return [ 'permissions' => 'De map :name of de submappen zijn niet schrijfbaar voor PHP. Zet de bijhorende rechten voor de webserver in deze map.', 'extension' => 'De PHP extensie :name is niet geïnstalleerd. Installeer deze bibliotheek en activeer de extensie.', 'plugin_missing' => 'De plugin :name is een afhankelijkheid maar is niet geïnstalleerd. Installeer deze plugin a.u.b.', + 'debug' => 'Debug modus is ingeschakeld. Dit wordt niet aanbevolen op een productie-omgeving.', + 'decompileBackendAssets' => 'Assets van de back-end worden momenteel gedecompileerd. Dit wordt aanbevolen op een productie-omgeving.', ], 'editor' => [ 'menu_label' => 'Editor instellingen', @@ -442,6 +447,7 @@ return [ 'navigation' => 'Navigatie', 'menu_mode' => 'Menustijl', 'menu_mode_inline' => 'Inline-mode', + 'menu_mode_inline_no_icons' => 'Inline-mode (zonder iconen)', 'menu_mode_tile' => 'Tegels', 'menu_mode_collapsed' => 'Ingeklapt', ], @@ -553,6 +559,7 @@ return [ 'mediafinder' => [ 'label' => 'Media zoeker', 'default_prompt' => 'Klik op de %s knop om een media item te vinden', + 'no_image' => 'De afbeelding kan niet gevonden worden', ], 'media' => [ 'menu_label' => 'Media', diff --git a/modules/backend/lang/pl/lang.php b/modules/backend/lang/pl/lang.php index 91fa42c7b..a0e67dd32 100644 --- a/modules/backend/lang/pl/lang.php +++ b/modules/backend/lang/pl/lang.php @@ -24,9 +24,6 @@ return [ 'help' => 'Baza danych jest wymagana do dostępu do panelu administracyjnego. Sprawdz czy baza danych jest prawidłowo skonfigurowana i zmigrowana przed ponowną próbą.', 'cms_link' => 'Powrót do strony głównej', ], - 'invalid_token' => [ - 'label' => 'Nieprawidłowy żeton bezpieczeństwa', - ], ], 'partial' => [ 'not_found_name' => "Blok ':name' nie został odnaleziony.", @@ -212,7 +209,7 @@ return [ 'update_title' => 'Edytuj :name', 'preview_title' => 'Podgląd :name', 'create_success' => ':name został stworzony pomyślnie', - 'update_success' => ':name został stworzony pomyślnie', + 'update_success' => ':name został zaktualizowany pomyślnie', 'delete_success' => ':name został usunięty pomyślnie', 'reset_success' => 'Resetowanie zostało zakończone', 'missing_id' => 'ID rekordu formularza nie zostało znalezione.', @@ -253,8 +250,8 @@ return [ 'preview_no_files_message' => 'Brak wgranych plików.', 'preview_no_record_message' => 'Brak zaznaczonych elementów.', 'select' => 'Zaznacz', - 'select_all' => 'wszystkie', - 'select_none' => 'żadne', + 'select_all' => 'Zaznacz wszystko', + 'select_none' => 'Wybierz brak', 'select_placeholder' => 'proszę zaznacz', 'insert_row' => 'Wstaw wiersz', 'insert_row_below' => 'Wstaw wiersz poniżej', diff --git a/modules/backend/lang/pt-br/lang.php b/modules/backend/lang/pt-br/lang.php index 2a2e4105e..039d6d649 100644 --- a/modules/backend/lang/pt-br/lang.php +++ b/modules/backend/lang/pt-br/lang.php @@ -7,7 +7,9 @@ return [ ], 'field' => [ 'invalid_type' => 'Tipo de campo inválido :type.', + 'options_method_invalid_model' => 'O atributo ":field" não resolve a classe. Tente especificar as opções do método para o modelo :model.', 'options_method_not_exists' => 'A classe :model deve definir um método :method() retornando opções para o campo ":field".', + 'colors_method_not_exists' => 'A classe de modelo :model deve definir um método :method() retornando códigos HEX de cor html para o campo de formulário ":field".' ], 'widget' => [ 'not_registered' => 'Uma classe de widget com o nome ":name" não foi definida', @@ -15,19 +17,39 @@ return [ ], 'page' => [ 'untitled' => 'Sem Título', + '404' => [ + 'label' => 'Página não encontrada', + 'help' => "Pesquisamos e pesquisamos, mas a URL solicitada simplesmente não foi encontrada. Será que você estava procurando outra coisa?", + 'back_link' => 'Volte para a página anterior', + ], 'access_denied' => [ 'label' => 'Acesso negado', 'help' => 'Você não tem as permissões necessárias para visualizar esta página.', 'cms_link' => 'Retornar à área administrativa', ], - 'invalid_token' => [ - 'label' => 'Token de segurança inválido' - ] + 'no_database' => [ + 'label' => 'Banco de dados ausente', + 'help' => "Um banco de dados é necessário para acessar o back-end. Verifique se o banco de dados está configurado e migrou antes de tentar novamente.", + 'cms_link' => 'Retornar para a página inicial', + ], ], 'partial' => [ 'not_found_name' => 'O bloco ":name" não foi encontrado.', + 'invalid_name' => 'Nome do bloco é inválido: :name.', + ], + 'ajax_handler' => [ + 'invalid_name' => 'Nome do manipulador AJAX inválido: :name.', + 'not_found' => "Manipulador AJAX ':name' não foi encontrado." ], 'account' => [ + 'impersonate' => 'Representar usuário', + 'impersonate_confirm' => 'Tem certeza de que deseja se passar por esse usuário? Você pode reverter para o seu estado original fazendo logout.', + 'impersonate_success' => 'Você está se passando por esse usuário', + 'impersonate_working' => 'Representando...', + 'impersonating' => 'Representando :full_name', + 'stop_impersonating' => 'Pare de representar', + 'signed_in_as' => 'Assinado como :full_name', + 'remember_me' => 'Permaneça logado', 'sign_out' => 'Sair', 'login' => 'Entrar', 'reset' => 'Redefinir', @@ -73,6 +95,8 @@ return [ 'make_default' => 'Definir como padrão', 'make_default_confirm' => 'Definir o painel atual como padrão?', 'make_default_success' => 'Painel atual agora é o padrão', + 'collapse_all' => 'Recolher tudo', + 'expand_all' => 'Expandir tudo', 'status' => [ 'widget_title_default' => 'Status do Sistema', 'update_available' => '{0} atualizações disponíveis!|{1} atualização disponível!|[2,Inf] atualizações disponíveis!', @@ -108,6 +132,8 @@ return [ 'last_name' => 'Sobrenome', 'full_name' => 'Nome Completo', 'email' => 'E-mail', + 'role_field' => 'Função', + 'role_comment' => 'As funções definem as permissões do usuário, que podem ser substituídas no nível do usuário, na guia Permissões.', 'groups' => 'Grupos', 'groups_comment' => 'Defina a quais grupos essa pessoa pertence.', 'avatar' => 'Foto', @@ -124,6 +150,12 @@ return [ 'allow' => 'Permitir', 'inherit' => 'Herdar', 'deny' => 'Negar', + 'activated' => 'Ativado', + 'last_login' => 'Último login', + 'created_at' => 'Criado em', + 'updated_at' => 'Atualizado em', + 'deleted_at' => 'Excluído em', + 'show_deleted' => 'Mostrar excluído', 'group' => [ 'name' => 'Grupo', 'name_comment' => 'O nome é exibido na lista de grupos ao se criar/alterar um administrador.', @@ -140,9 +172,25 @@ return [ 'return' => 'Voltar para a lista de grupos', 'users_count' => 'Usuários' ], + 'role' => [ + 'name' => 'Função', + 'name_field' => 'Nome', + 'name_comment' => 'O nome é exibido na lista de funções no formulário Administrador.', + 'description_field' => 'Descrição', + 'code_field' => 'Código', + 'code_comment' => 'Digite um código exclusivo se quiser acessar o objeto de função com a API.', + 'menu_label' => 'Gerenciar Funções', + 'list_title' => 'Gerenciar Funções', + 'new' => 'Nova Função', + 'delete_confirm' => 'Excluir esta função de administrador?', + 'return' => 'Retornar para lista de funções', + 'users_count' => 'Usuários' + ], 'preferences' => [ 'not_authenticated' => 'Nenhum usuário autenticado para carregar as preferências.', ], + 'trashed_hint_title' => 'Esta conta foi excluída', + 'trashed_hint_desc' => 'Esta conta foi excluida e não poderá ser acessada. Para restaurá-la, clique no ícone de restauração do usuário no canto inferior direito', ], 'list' => [ 'default_title' => 'Lista', @@ -156,6 +204,8 @@ return [ 'behavior_not_ready' => 'Lista não foi inicializada. Confira se você chamou makeLists() no controller.', 'invalid_column_datetime' => 'Valor da coluna ":column" não é um objeto DateTime, você esqueceu registrar \$dates no Model?', 'pagination' => 'Registros exibidos: :from-:to de :total', + 'first_page' => 'Primeira página', + 'last_page' => 'Última página', 'prev_page' => 'Anterior', 'next_page' => 'Próxima', 'refresh' => 'Atualizar', @@ -185,6 +235,10 @@ return [ 'remove_confirm' => 'Você tem certeza?', 'remove_file' => 'Remover arquivo' ], + 'repeater' => [ + 'min_items_failed' => ':name requer um mínimo de :min itens, apenas :items foram fornecidos', + 'max_items_failed' => ':name requer um máximo de :max itens, apenas :items foram fornecidos', + ], 'form' => [ 'create_title' => 'Novo :name', 'update_title' => 'Editar :name', @@ -211,6 +265,9 @@ return [ 'confirm_delete' => 'Você realmente deseja apagar este registro?', 'confirm_delete_multiple' => 'Você realmente deseja apagar os registros selecionados?', 'deleting_name' => 'Apagando :name...', + 'restore' => 'Restaurar', + 'restoring' => 'Restaurando', + 'confirm_restore' => 'Tem certeza de que deseja restaurar este registro?', 'reset_default' => 'Redefinir para o padrão', 'resetting' => 'Redefinindo', 'resetting_name' => 'Redefinindo :name', @@ -229,10 +286,11 @@ return [ 'confirm_tab_close' => 'Tem certeza que deseja fechar essa aba? As alterações que não foram salvas serão perdidas', 'behavior_not_ready' => 'O formulário não foi inicializado. Confira se você chamou initForm() no controller.', 'preview_no_files_message' => 'Os arquivos não foram carregados', + 'preview_no_media_message' => 'Não há mídia selecionada.', 'preview_no_record_message' => 'Nenhum registro selecionado.', 'select' => 'Selecionar', - 'select_all' => 'todos', - 'select_none' => 'nenhum', + 'select_all' => 'Selecionar tudo', + 'select_none' => 'Selecione nenhum', 'select_placeholder' => 'por favor, selecione', 'insert_row' => 'Inserir linha', 'insert_row_below' => 'Inserir linha abaixo', @@ -242,7 +300,9 @@ return [ 'return_to_list' => 'Retornar à lista', ], 'recordfinder' => [ - 'find_record' => 'Localizar Registro' + 'find_record' => 'Localizar Registro', + 'invalid_model_class' => 'A classe de modelo fornecida ":modelClass" para o recordfinder é inválida', + 'cancel' => 'Cancelar', ], 'relation' => [ 'missing_config' => 'Comportamento relation não tem uma configuração para ":config".', @@ -295,6 +355,7 @@ return [ 'tips_description' => 'Há itens que demandam atenção para configurar o sistema corretamente.', 'permissions' => 'Diretório :name ou seus subdiretórios não são graváveis pelo PHP. Por favor, defina permissões de escrita para o servidor neste diretório.', 'extension' => 'A extensão PHP :name não está instalada. Por favor, instale esta biblioteca para ativar a extensão.', + 'plugin_missing' => 'O plugin :name é uma dependência, mas não está instalado. Por favor, instale este plugin.', ], 'editor' => [ 'menu_label' => 'Definições do Editor', @@ -341,6 +402,10 @@ return [ 'remove_tags' => 'Excluir etiqueta', 'remove_tags_comment' => 'Lista de etiquetas que serão exclídas juntas com seu conteúdo.', 'theme' => 'Esquema de cores', + 'line_breaker_tags' => 'Tags de quebra de linha', + 'line_breaker_tags_comment' => 'A lista de tags usadas para colocar um elemento em quebra de linha.', + 'toolbar_buttons' => 'Botões da barra de ferramentas', + 'toolbar_buttons_comment' => 'Os botões da barra de ferramentas a serem exibidos no Rich Editor por padrão. [fullscreen, bold, italic, underline, strikeThrough, subscript, superscript, fontFamily, fontSize, |, color, emoticons, inlineStyle, paragraphStyle, |, paragraphFormat, align, formatOL, formatUL, outdent, indent, quote, insertHR, -, insertLink, insertImage, insertVideo, insertAudio, insertFile, insertTable, undo, redo, clearFormatting, selectAll, html]', ], 'tooltips' => [ 'preview_website' => 'Visualizar a página' @@ -360,6 +425,8 @@ return [ 'brand' => 'Marca', 'logo' => 'Logo', 'logo_description' => 'Fazer upload de uma logo para usar na área administrativa.', + 'favicon' => 'Favicon', + 'favicon_description' => 'Carregar um favicon personalizado para usar no back-end', 'app_name' => 'Nome do Aplicativo', 'app_name_description' => 'Este nome é mostrado no título da área administrativa.', 'app_tagline' => 'Slogan do Aplicativo', @@ -373,6 +440,7 @@ return [ 'navigation' => 'Navegação', 'menu_mode' => 'Estilo de menu', 'menu_mode_inline' => 'Em linha', + 'menu_mode_inline_no_icons' => 'Em linha (sem ícones)', 'menu_mode_tile' => 'Blocos', 'menu_mode_collapsed' => 'Colapsados' ], @@ -390,7 +458,9 @@ return [ 'hint' => 'Este registro mostra a lista de acessos dos administradores. Os registros são mantidos por um período de :days dias.', 'menu_label' => 'Registro de Acesso', 'menu_description' => 'Veja a lista de acessos à administração.', + 'id' => 'ID', 'created_at' => 'Data & Hora', + 'type' => 'Tipo', 'login' => 'Login', 'ip_address' => 'Endereço IP', 'first_name' => 'Nome', @@ -400,11 +470,13 @@ return [ 'filter' => [ 'all' => 'todos', 'options_method_not_exists' => "A classe modelo :model deve definir um método :method() retornando opções para o filtro ':filter'.", - 'date_all' => 'todo o período' + 'date_all' => 'todo o período', + 'number_all' => 'todos os números', ], 'import_export' => [ 'upload_csv_file' => '1. Enviar arquivo CSV', 'import_file' => 'Importar arquivo', + 'row' => 'Linha :row', 'first_row_contains_titles' => 'Primeira linha contém títulos das colunas', 'first_row_contains_titles_desc' => 'Deixe marcado se primeira linha do CSV é utilizada como títulos das colunas.', 'match_columns' => '2. Associar as colunas do arquivo a campos do banco de dados', @@ -478,7 +550,9 @@ return [ 'manage_media' => 'Gerenciar mídias' ], 'mediafinder' => [ - 'default_prompt' => 'Clique no botão %s para localizar um arquivo de mídia' + 'label' => 'Localizador de Mídia', + 'default_prompt' => 'Clique no botão %s para localizar um arquivo de mídia', + 'no_image' => 'A imagem não foi encontrada' ], 'media' => [ 'menu_label' => 'Mídias', @@ -509,6 +583,9 @@ return [ 'uploading_error' => 'Falha no envio', 'type_blocked' => 'O tipo de arquivo utilizado é bloqueado por motivos de segurança.', 'order_by' => 'Ordenar por', + 'direction' => 'Direção', + 'direction_asc' => 'Ascendente', + 'direction_desc' => 'Descendente', 'folder' => 'Pasta', 'no_files_found' => 'Nenhum arquivo encontrado.', 'delete_empty' => 'Por favor, selecione um item para excluir.', diff --git a/modules/backend/lang/pt-pt/lang.php b/modules/backend/lang/pt-pt/lang.php index 203d4edbb..9c1b46da3 100644 --- a/modules/backend/lang/pt-pt/lang.php +++ b/modules/backend/lang/pt-pt/lang.php @@ -3,11 +3,13 @@ return [ 'auth' => [ 'title' => 'Área Administrativa', + 'invalid_login' => 'As credenciais que introduziu não são válidas. Por favor verifique os seus dados e tente novamente.', ], 'field' => [ 'invalid_type' => 'Invalid field type used :type.', 'options_method_invalid_model' => "O atributo ':field' não é resolvido para um modelo válidol. Tente especificar o método de opções para a classe de modelo :model explicitamente.", - 'options_method_not_exists' => "A classe de modelo :model deve definir um método :method() retornando opções para o campo de formulário ':field'." + 'options_method_not_exists' => "A classe de modelo :model deve definir um método :method() que retorne opções para o campo de formulário ':field'.", + 'colors_method_not_exists' => "A classe de modelo :model deve definir um método :method() que retorne códigos de côr hexadecimais html para o campo de formulário':field'.", ], 'widget' => [ 'not_registered' => 'Uma classe de widget com o nome ":name" não foi registada', @@ -15,6 +17,11 @@ return [ ], 'page' => [ 'untitled' => 'Sem título', + '404' => [ + 'label' => 'Página não encontrada', + 'help' => "Por muito que procuremos, o URL pedido não existe. Talvez esteja à procura de outra coisa?", + 'back_link' => 'Voltar à página anterior', + ], 'access_denied' => [ 'label' => 'Acesso negado', 'help' => 'Não tem as permissões necessárias para visualizar esta página.', @@ -23,22 +30,32 @@ return [ 'no_database' => [ 'label' => 'Base de dados não existente', 'help' => "Uma base de dados é necessária para acesso ao back-end. Verifique se a base dados se encontra configurada e migrada antes de tentar novamente.", - 'cms_link' => 'Regressar á página inicial' + 'cms_link' => 'Regressar á página inicial', ], - 'invalid_token' => [ - 'label' => 'Token de segurança inválido' - ] ], 'partial' => [ - 'not_found_name' => 'O bloco ":name" não foi encontrado.', + 'not_found_name' => 'O bloco parcial ":name" não foi encontrado.', + 'invalid_name' => 'Nome de bloco parcial inválido: :name.', + ], + 'ajax_handler' => [ + 'invalid_name' => 'Nome de processador AJAX inválido: :name.', + 'not_found' => "O processador AJAX ':name' não foi encontrado.", ], 'account' => [ + 'impersonate' => 'Personificar utilizador', + 'impersonate_confirm' => 'Tem a certeza de que quer personificar este utilizador? Pode voltar ao seu estado inicial fazendo log out.', + 'impersonate_success' => 'Está agora a personificar este utilizador', + 'impersonate_working' => 'A personificar...', + 'impersonating' => 'A personificar :full_name', + 'stop_impersonating' => 'Parar de personificar', + 'signed_in_as' => 'Sessão iniciada como :full_name', 'sign_out' => 'Sair', 'login' => 'Entrar', 'reset' => 'Redefinir', 'restore' => 'Restaurar', 'login_placeholder' => 'Utilizador', 'password_placeholder' => 'Senha', + 'remember_me' => 'Manter sessão activa', 'forgot_password' => 'Esqueceu sua senha?', 'enter_email' => 'Coloque o seu email', 'enter_login' => 'Coloque o nome de utilizador', @@ -58,26 +75,26 @@ return [ 'dashboard' => [ 'menu_label' => 'Painel', 'widget_label' => 'Widget', - 'widget_width' => 'Width', - 'full_width' => 'full width', - 'manage_widgets' => 'Manage widgets', - 'add_widget' => 'Add widget', - 'widget_inspector_title' => 'Widget configuration', - 'widget_inspector_description' => 'Configure the report widget', - 'widget_columns_label' => 'Width :columns', - 'widget_columns_description' => 'The widget width, a number between 1 and 10.', - 'widget_columns_error' => 'Please enter the widget width as a number between 1 and 10.', - 'columns' => '{1} column|[2,Inf] columns', - 'widget_new_row_label' => 'Force new row', - 'widget_new_row_description' => 'Put the widget in a new row.', - 'widget_title_label' => 'Widget title', - 'widget_title_error' => 'The Widget Title is required.', - 'reset_layout' => 'Reset layout', - 'reset_layout_confirm' => 'Reset layout back to default?', - 'reset_layout_success' => 'Layout has been reset', - 'make_default' => 'Make default', - 'make_default_confirm' => 'Set the current layout as the default?', - 'make_default_success' => 'Current layout is now the default', + 'widget_width' => 'Largura', + 'full_width' => 'Largura completa', + 'manage_widgets' => 'Gerir widgets', + 'add_widget' => 'Adicionar widget', + 'widget_inspector_title' => 'Configuração de widgets', + 'widget_inspector_description' => 'Configurar o widget', + 'widget_columns_label' => 'Largura :columns', + 'widget_columns_description' => 'A largura do widget, um número entre 1 e 12.', + 'widget_columns_error' => 'Por favor introduza a largura do widget como um número entre 1 e 12.', + 'columns' => '{1} coluna|[2,Inf] colunas', + 'widget_new_row_label' => 'Forçar nova linha', + 'widget_new_row_description' => 'Pôr o widget numa linha nova.', + 'widget_title_label' => 'Título do widget', + 'widget_title_error' => 'O título do widget é obrigatório.', + 'reset_layout' => 'Reinicializar layout', + 'reset_layout_confirm' => 'Reinicializar o layout para a configuração de origem?', + 'reset_layout_success' => 'O layout foi reinicializado', + 'make_default' => 'Guardar como pré-definido', + 'make_default_confirm' => 'Guardar o layout actual como o pré-definido?', + 'make_default_success' => 'O layout actual é agora o pré-definido', 'collapse_all' => 'Contrair todos', 'expand_all' => 'Expandir todos ', 'status' => [ @@ -96,7 +113,7 @@ return [ ], 'welcome' => [ 'widget_title_default' => 'Bem-vindo', - 'welcome_back_name' => 'Seja bem vindo no seu regresso ao :app, :name.', + 'welcome_back_name' => 'Seja bem vindo de volta ao :app, :name.', 'welcome_to_name' => 'Bem vindo ao :app, :name.', 'first_sign_in' => 'Esta é a primeira vez que acede à área administrativa.', 'last_sign_in' => 'O último acesso foi em', @@ -105,36 +122,40 @@ return [ ] ], 'user' => [ - 'name' => 'Administrator', - 'menu_label' => 'Administrators', - 'menu_description' => 'Manage back-end administrator users, groups and permissions.', - 'list_title' => 'Manage Administrators', - 'new' => 'New Administrator', + 'name' => 'Administrador', + 'menu_label' => 'Administradores', + 'menu_description' => 'Gerir utilizadores, grupos, e permissões de administração.', + 'list_title' => 'Gerir Administradores', + 'new' => 'Novo Administrator', 'login' => 'Login', - 'first_name' => 'First Name', - 'last_name' => 'Last Name', - 'full_name' => 'Full Name', + 'first_name' => 'Nome Próprio', + 'last_name' => 'Apelido', + 'full_name' => 'Nome Completo', 'email' => 'Email', - 'groups' => 'Groups', - 'groups_comment' => 'Specify which groups the account should belong to. Groups define user permissions, which can be overriden on the user level, on the Permissions tab.', + 'role_field' => 'Papel', + 'role_comment' => 'Papéis definem permissões de utilizador, que podem ser sobrepostas para cada utilizador no separador Permissões.', + 'groups' => 'Grupos', + 'groups_comment' => 'Defina os grupos a que esta conta deve pertencer. Os grupos definem permissões, que podem ser sobrepostas para cada utilizador no separador Permissões.', 'avatar' => 'Avatar', - 'password' => 'Password', - 'password_confirmation' => 'Confirm Password', - 'permissions' => 'Permissions', - 'account' => 'Account', - 'superuser' => 'Super User', - 'superuser_comment' => 'Grants this account unlimited access to all areas of the system. Super users can add and manage other users. ', - 'send_invite' => 'Send invitation by email', - 'send_invite_comment' => 'Sends a welcome message containing login and password information.', - 'delete_confirm' => 'Delete this administrator?', - 'return' => 'Return to admin list', - 'allow' => 'Allow', - 'inherit' => 'Inherit', - 'deny' => 'Deny', + 'password' => 'Senha', + 'password_confirmation' => 'Confirmar Senha', + 'permissions' => 'Permissões', + 'account' => 'Conta', + 'superuser' => 'Super Utilizador', + 'superuser_comment' => 'Permite accesso completo a todas as àreas do sistema para esta conta. Super utilizadores podem criar e gerir outros utilizadores.', + 'send_invite' => 'Enviar convite por email', + 'send_invite_comment' => 'Envia uma mensagem de boas-vindas com a informação de login e password.', + 'delete_confirm' => 'Apagar este administrador?', + 'return' => 'Voltar à lista de administradores', + 'allow' => 'Permitir', + 'inherit' => 'Herdar', + 'deny' => 'Negar', 'activated' => 'Activado', 'last_login' => 'Última entrada', 'created_at' => 'Criado em', 'updated_at' => 'Modificado em', + 'deleted_at' => 'Apagado em', + 'show_deleted' => 'Mostrar apagados', 'group' => [ 'name' => 'Grupo', 'name_comment' => 'O nome é exibido na lista de grupos ao criar/alterar um administrador.', @@ -149,11 +170,27 @@ return [ 'new' => 'Novo grupo administrador', 'delete_confirm' => 'Você realmente deseja apagar este grupo?', 'return' => 'Voltar para a lista de grupos', - 'users_count' => 'Utilizadores' + 'users_count' => 'Utilizadores', + ], + 'role' => [ + 'name' => 'Papel', + 'name_field' => 'Nome', + 'name_comment' => 'O nome é exibido na lista de grupos ao criar/alterar um administrador.', + 'description_field' => 'Descrição', + 'code_field' => 'Código', + 'code_comment' => 'Insira um código exclusivo se quiser utilizar o mesmo com a API.', + 'menu_label' => 'Gerir Papéis', + 'list_title' => 'Gerir Papéis', + 'new' => 'Novo Papel', + 'delete_confirm' => 'Você realmente deseja apagar este papel?', + 'return' => 'Voltar para a lista de papéis', + 'users_count' => 'Utilizadores', ], 'preferences' => [ 'not_authenticated' => 'Nenhum utilizador autenticado para carregar as preferências.', ], + 'trashed_hint_title' => 'Esta conta foi apagada', + 'trashed_hint_desc' => 'Esta conta foi apagada e não pode iniciar sessão. Para a restaurar, clique no botão Restaurar Utilizador no canto inferior direito.', ], 'list' => [ 'default_title' => 'Lista', @@ -184,7 +221,7 @@ return [ 'delete_selected_confirm' => 'Apagar os registos selecionados?', 'delete_selected_success' => 'Registos seleccionados apagados com sucesso.', 'column_switch_true' => 'Sim', - 'column_switch_false' => 'Não' + 'column_switch_false' => 'Não', ], 'fileupload' => [ 'attachment' => 'Anexo', @@ -196,7 +233,11 @@ return [ 'upload_file' => 'Enviar ficheiro', 'upload_error' => 'Erro ao enviar', 'remove_confirm' => 'Tem a certeza?', - 'remove_file' => 'Remover ficheiro' + 'remove_file' => 'Remover ficheiro', + ], + 'repeater' => [ + 'min_items_failed' => ':name requer no mínimo :min itens, apenas :items foram introduzidos', + 'max_items_failed' => ':name requer no máximo :min itens, :items foram introduzidos', ], 'form' => [ 'create_title' => 'Novo :name', @@ -205,6 +246,7 @@ return [ 'create_success' => ':name foi criado com sucesso', 'update_success' => ':name foi actualizado com sucesso', 'delete_success' => ':name foi apagado com sucesso', + 'restore_success' => ':name foi restaurado com sucesso', 'reset_success' => 'Reinicialização completa', 'missing_id' => 'O ID do registo não foi fornecido', 'missing_model' => 'Formulário utilizado na classe :class não tem um modelo definido.', @@ -224,6 +266,9 @@ return [ 'confirm_delete' => 'Realmente deseja apagar este registo?', 'confirm_delete_multiple' => 'Realmente deseja apagar os registos seleccionados?', 'deleting_name' => 'Apagando :name...', + 'restore' => 'Restaurar', + 'restoring' => 'Restaurando', + 'confirm_restore' => 'Realmente deseja restaurar este registo?', 'reset_default' => 'Redefinir para o padrão', 'resetting' => 'Redefinindo', 'resetting_name' => 'Redefinindo :name', @@ -245,8 +290,8 @@ return [ 'preview_no_media_message' => 'Nenhum conteúdo selecionado.', 'preview_no_record_message' => 'Nenhum registo selecionado.', 'select' => 'Selecionar', - 'select_all' => 'todos', - 'select_none' => 'nenhum', + 'select_all' => 'Selecionar tudo', + 'select_none' => 'Selecione nenhum', 'select_placeholder' => 'por favor, selecione', 'insert_row' => 'Inserir linha', 'insert_row_below' => 'Inserir linha abaixo', @@ -257,11 +302,12 @@ return [ ], 'recordfinder' => [ 'find_record' => 'Localizar Registo', + 'invalid_model_class' => 'A classe modelo ":modelClass" definida para o localizador de registos é inválida.', 'cancel' => 'Cancelar', ], 'pagelist' => [ 'page_link' => 'Ligação de página', - 'select_page' => 'Escolha uma página...' + 'select_page' => 'Escolha uma página...', ], 'relation' => [ 'missing_config' => 'Comportamento da relação não tem uma configuração para ":config".', @@ -315,6 +361,8 @@ return [ 'permissions' => 'Diretoria :name ou suas subdiretorias não são graváveis pelo PHP. Por favor, defina permissões de escrita para o servidor nesta diretoria.', 'extension' => 'A extensão PHP :name não está instalada. Por favor, instale esta biblioteca para activar a extensão.', 'plugin_missing' => 'A extensão :name é uma dependência mas não está instalada. Por favor instale esta extensão.', + 'debug' => 'O modo de depuração está activo. Isto não é recomendado em abientes de produção.', + 'decompileBackendAssets' => 'Os recursos do backend não estao compilados. Isto não é recomendado em ambientes de produção.', ], 'editor' => [ 'menu_label' => 'Definições do Editor', @@ -361,9 +409,13 @@ return [ 'no_wrap_comment' => 'Lista de etiquetas que não devem ser envolvidas num bloco de etiquetas.', 'remove_tags' => 'Apagar etiquetas', 'remove_tags_comment' => 'Lista de etiquetas que serão excluídas incluíndo o conteúdo.', + 'line_breaker_tags' => 'Etiquetas de quebra de linha', + 'line_breaker_tags_comment' => 'Lista de etiquetas entre as quais é inserida uma quebra de linha.', + 'toolbar_buttons' => 'Botões da barra de ferramentas', + 'toolbar_buttons_comment' => 'Botões da barra de ferramentas do editor rico a serem mostradas por defeito. [fullscreen, bold, italic, underline, strikeThrough, subscript, superscript, fontFamily, fontSize, |, color, emoticons, inlineStyle, paragraphStyle, |, paragraphFormat, align, formatOL, formatUL, outdent, indent, quote, insertHR, -, insertLink, insertImage, insertVideo, insertAudio, insertFile, insertTable, undo, redo, clearFormatting, selectAll, html]', ], 'tooltips' => [ - 'preview_website' => 'Prévisualizar a página' + 'preview_website' => 'Prévisualizar a página', ], 'mysettings' => [ 'menu_label' => 'As minhas configurações', @@ -372,7 +424,7 @@ return [ 'myaccount' => [ 'menu_label' => 'Minha Conta', 'menu_description' => 'Actualizar detalhes da sua conta, como nome, e-mail e senha.', - 'menu_keywords' => 'login de segurança' + 'menu_keywords' => 'login de segurança', ], 'branding' => [ 'menu_label' => 'Personalização', @@ -380,6 +432,8 @@ return [ 'brand' => 'Marca', 'logo' => 'Logo', 'logo_description' => 'Fazer carregamento de um logótipo para usar na área administrativa.', + 'favicon' => 'Favicon', + 'favicon_description' => 'Carrege um favicon personalizado a usar na área administrativa', 'app_name' => 'Nome da Aplicação', 'app_name_description' => 'Este nome é mostrado no título da área administrativa.', 'app_tagline' => 'Slogan do Aplicativo', @@ -393,8 +447,9 @@ return [ 'navigation' => 'Navegação', 'menu_mode' => 'Estilo de menu', 'menu_mode_inline' => 'Em linha', + 'menu_mode_inline_no_icons' => 'Em linha (sem icons)', 'menu_mode_tile' => 'Blocos', - 'menu_mode_collapsed' => 'Colapsados' + 'menu_mode_collapsed' => 'Colapsados', ], 'backend_preferences' => [ 'menu_label' => 'Preferências da Administração', @@ -410,21 +465,25 @@ return [ 'hint' => 'Este registo mostra a lista de acessos dos administradores. Os registros são mantidos por um período de :days dias.', 'menu_label' => 'Registo de Acesso', 'menu_description' => 'Veja a lista de acessos à administração.', + 'id' => 'ID', 'created_at' => 'Data & Hora', + 'type' => 'Tipo', 'login' => 'Entrada', 'ip_address' => 'Endereço IP', 'first_name' => 'Nome', - 'last_name' => 'Sobrenome', + 'last_name' => 'Apelido', 'email' => 'E-mail', ], 'filter' => [ 'all' => 'todos', 'options_method_not_exists' => "A classe modelo :model deve definir um método :method() retornando opções para o filtro ':filter'.", - 'date_all' => 'todo o período' + 'date_all' => 'todo o período', + 'number_all' => 'todos os números', ], 'import_export' => [ 'upload_csv_file' => '1. Enviar ficheiro CSV', 'import_file' => 'Importar ficheiro', + 'row' => 'Linha :row', 'first_row_contains_titles' => 'Primeira linha contém títulos das colunas', 'first_row_contains_titles_desc' => 'Deixe marcado se primeira linha do CSV é utilizada como títulos das colunas.', 'match_columns' => '2. Associar as colunas do ficheiro a campos do base de dados', @@ -491,15 +550,16 @@ return [ 'iso_8859_14' => 'ISO-8859-14 (Latin-8, Celtic)', 'iso_8859_15' => 'ISO-8859-15 (Latin-9, Western European revision with euro sign)', 'windows_1251' => 'Windows-1251 (CP1251)', - 'windows_1252' => 'Windows-1252 (CP1252)' - ] + 'windows_1252' => 'Windows-1252 (CP1252)', + ], ], 'permissions' => [ - 'manage_media' => 'Gerir conteúdo multimédia' + 'manage_media' => 'Gerir conteúdo multimédia', ], 'mediafinder' => [ 'label' => 'Localizador de multimédia', - 'default_prompt' => 'Clique no botão %s para localizar um ficheiro multimédia' + 'default_prompt' => 'Clique no botão %s para localizar um ficheiro multimédia', + 'no_image' => 'A imagem não foi encontrada', ], 'media' => [ 'menu_label' => 'Conteúdos', @@ -530,6 +590,9 @@ return [ 'uploading_error' => 'Falha no envio', 'type_blocked' => 'O tipo de ficheiro utilizado é bloqueado por motivos de segurança.', 'order_by' => 'Ordenar por', + 'direction' => 'Direção', + 'direction_asc' => 'Ascendente', + 'direction_desc' => 'Descendente', 'folder' => 'Pasta', 'no_files_found' => 'Nenhum ficheiro encontrado.', 'delete_empty' => 'Por favor, selecione itens para apagar.', @@ -559,6 +622,6 @@ return [ 'selection_mode' => 'Modo de seleção', 'resize_image' => 'Redimensionar imagem', 'image_size' => 'Tamanho da imagem:', - 'selected_size' => 'Selecionado:' + 'selected_size' => 'Selecionado:', ], ]; diff --git a/modules/backend/lang/ro/lang.php b/modules/backend/lang/ro/lang.php index 8728f30cb..294ffb075 100644 --- a/modules/backend/lang/ro/lang.php +++ b/modules/backend/lang/ro/lang.php @@ -149,8 +149,8 @@ return [ 'behavior_not_ready' => 'Setarile initiale ale formularului nu au fost definite, verificati existenta functiei initForm() in controller.', 'preview_no_files_message' => 'Fisierele nu au fost incarcate', 'select' => 'Selectare', - 'select_all' => 'toate', - 'select_none' => 'niciunul', + 'select_all' => 'Selectează tot', + 'select_none' => 'Selectați niciuna', ], 'relation' => [ 'missing_definition' => "Relatia nu contine definitii pentru campul ':field'.", diff --git a/modules/backend/lang/ru/lang.php b/modules/backend/lang/ru/lang.php index 040c8fdd3..1d5e63f84 100644 --- a/modules/backend/lang/ru/lang.php +++ b/modules/backend/lang/ru/lang.php @@ -27,9 +27,6 @@ return [ 'help' => "Для доступа к серверу требуется база данных. Проверьте, что база данных настроена и перенесена, прежде чем повторять попытку.", 'cms_link' => 'Вернуться на главную страницу' ], - 'invalid_token' => [ - 'label' => 'Неверный токен безопасности' - ], ], 'partial' => [ 'not_found_name' => 'Не удалось найти шаблон (partial) с именем :name.' @@ -269,8 +266,8 @@ return [ 'preview_no_media_message' => 'Нет выбраного медиа.', 'preview_no_record_message' => 'Нет выбранных записей.', 'select' => 'Выбрать', - 'select_all' => 'все', - 'select_none' => 'ничего', + 'select_all' => 'выбрать все', + 'select_none' => 'выберите ни одного', 'select_placeholder' => 'Пожалуйста, выберите', 'insert_row' => 'Вставить строку', 'insert_row_below' => 'Вставить строку ниже', diff --git a/modules/backend/lang/sk/lang.php b/modules/backend/lang/sk/lang.php index f2dd887d9..3741cea89 100644 --- a/modules/backend/lang/sk/lang.php +++ b/modules/backend/lang/sk/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "Pre prístup do administrácie je potrebná databáza. Zkontrolujte, či je databáza nakonfigurovaná a zmigrovaná a skúste to znova.", 'cms_link' => 'Späť na úvodnú stránku' ], - 'invalid_token' => [ - 'label' => 'Neplatný bezpečnostný token' - ] ], 'partial' => [ 'not_found_name' => "Čiastočná šablona ':name' nebola nájdená." @@ -263,8 +260,8 @@ return [ 'preview_no_media_message' => 'Žiadne médium nebolo vybrané.', 'preview_no_record_message' => 'Žiadny záznam nie je vybraný', 'select' => 'Vybrať', - 'select_all' => 'všetko', - 'select_none' => 'nič', + 'select_all' => 'vybrať všetko', + 'select_none' => 'nevyber nič', 'select_placeholder' => 'prosím vyberte', 'insert_row' => 'Vložiť riadok', 'insert_row_below' => 'Vložiť riadok pod', @@ -297,7 +294,7 @@ return [ 'cancel' => 'Zrušiť', 'close' => 'Zatvoriť', 'add_name' => 'Pridať :name', - 'create' => 'Vytvorť', + 'create' => 'Vytvoriť', 'create_name' => 'Vytvoriť :name', 'update' => 'Upraviť', 'update_name' => 'Upraviť :name', diff --git a/modules/backend/lang/sv/lang.php b/modules/backend/lang/sv/lang.php index c62992356..2557d7fd7 100644 --- a/modules/backend/lang/sv/lang.php +++ b/modules/backend/lang/sv/lang.php @@ -19,9 +19,6 @@ return [ 'help' => "Du har inte behörighet att visa den här sidan.", 'cms_link' => "Gå till CMS backend", ], - 'invalid_token' => [ - 'label' => 'Ogiltig säkerhetstoken' - ], ], 'partial' => [ 'not_found_name' => "En partial med namn ':name' kunde inte hittas", @@ -193,8 +190,8 @@ return [ 'preview_no_files_message' => 'Filen är inte uppladdad', 'preview_no_record_message' => 'Ingen rad är vald.', 'select' => 'Välj', - 'select_all' => 'alla', - 'select_none' => 'ingen', + 'select_all' => 'Välj alla', + 'select_none' => 'Välj ingen', 'select_placeholder' => 'Vänligen välj', 'insert_row' => 'Lägg till rad', 'delete_row' => 'Radera rad', diff --git a/modules/backend/lang/th/lang.php b/modules/backend/lang/th/lang.php new file mode 100644 index 000000000..e1faaab90 --- /dev/null +++ b/modules/backend/lang/th/lang.php @@ -0,0 +1,513 @@ + [ + 'title' => 'ส่วนการจัดการ', + 'invalid_login' => 'รายละเอียดที่คุณเพิ่มเข้ามาไม่ตรงกับข้อมูลของเรา กรุณาตรวจสอบและลองอีกครั้ง', + ], + 'field' => [ + 'invalid_type' => 'ประเภทของฟิลด์ไม่ถูกต้อง :type.', + ], + 'widget' => [ + 'not_registered' => "วิดเจทชื่อ ':name' ยังไม่ได้ถูกลงทะเบียน", + 'not_bound' => "วิดเจทชื่อ ':name' ยังไม่ได้ถูกผูกกับคอนโทรลเลอร์", + ], + 'page' => [ + 'untitled' => 'ไม่มีชื่อเรื่อง', + '404' => [ + 'label' => 'ไม่พบหน้านี้', + 'help' => "เราพยายามค้นหาแล้ว แต่ไม่พบ URL ที่ร้องขอ บางทีคุณกำลังหาอย่างอื่นอยู่หรือเปล่า?", + 'back_link' => 'กลับไปยังหน้าที่แล้ว', + ], + 'access_denied' => [ + 'label' => 'ไม่อนุญาตให้เข้าถึง', + 'help' => "คุณไม่มีสิทธิ์ที่จำเป็นในการดูหน้านี้", + 'cms_link' => 'กลับสู่หน้าเว็บหลังบ้าน', + ], + 'no_database' => [ + 'label' => 'ไม่พบฐานข้อมูล', + 'help' => "จำเป็นต้องมีฐานข้อมูลในการเข้าถึงหน้าเว็บหลังบ้าน ตรวจสอบว่าฐานข้อมูลได้ถูกตั้งค่าและโอนย้ายก่อนลองอีกครั้ง", + 'cms_link' => 'กลับสู่หน้าเว็บหลัก', + ], + ], + 'partial' => [ + 'not_found_name' => "ไม่พบส่วนย่อย ':name'", + 'invalid_name' => 'ส่วนย่อยชื่อ: :name ไม่ถูกต้อง', + ], + 'ajax_handler' => [ + 'invalid_name' => 'ชื่อผู้จัดการ AJAX: :name ไม่ถูกต้อง', + 'not_found' => "หาผู้จัดการ AJAX ':name' ไม่พบ", + ], + 'account' => [ + 'impersonate' => 'ปลอมตัวเป็นผู้ใช้', + 'impersonate_confirm' => 'คุณแน่ใจว่าต้องการปลอมตัวเป็นผู้ใช้คนนี้? คุณสามารถย้อนกลับโดยการล็อกเอาท์', + 'impersonate_success' => 'คุณกำลังปลอมตัวเป็นผู้ใช้คนนี้', + 'impersonate_working' => 'กำลังปลอมตัว...', + 'impersonating' => 'กำลังปลอมตัว :full_name', + 'stop_impersonating' => 'หยุดการปลอมตัว', + 'signed_in_as' => 'ลงชื่อเข้าโดยใช้ชื่อ :full_name', + 'sign_out' => 'ลงชื่อออก', + 'login' => 'ล็อกอิน', + 'reset' => 'ตั้งใหม่', + 'login_placeholder' => 'ชื่อล็อกอิน', + 'restore' => 'กู้คืน', + 'password_placeholder' => 'รหัสผ่าน', + 'remember_me' => 'ให้ล็อกอินค้างไว้', + 'forgot_password' => 'ลืมรหัสผ่านของคุณ?', + 'enter_email' => 'ใส่ข้อมูลอีเมลของคุณ', + 'enter_login' => 'ใส่ข้อมูลล็อกอินของคุณ', + 'email_placeholder' => 'อีเมล', + 'enter_new_password' => 'ใส่ข้อมูลรหัสผ่านใหม่', + 'password_reset' => 'ตั้งค่ารหัสผ่านใหม่', + 'restore_success' => 'ได้ส่งข้อความไปทางอีเมลของคุณพร้อมด้วยขั้นตอนการทำงาน', + 'restore_error' => "ไม่เจอผู้ใช้ชื่อ ':login'", + 'reset_success' => 'ตั้งรหัสผ่านใหม่แล้ว คุณสามารถลงชื่อเข้าใช้ได้', + 'reset_error' => 'ข้อมูลที่ใช้ตั้งรหัสผ่านไม่ถูกต้อง กรุณาลองใหม่!', + 'reset_fail' => 'ไม่สามารถตั้งค่ารหัสผ่านของคุณใหม่ได้!', + 'apply' => 'นำไปใช้', + 'cancel' => 'ยกเลิก', + 'delete' => 'ลบ', + 'ok' => 'ตกลง', + ], + 'dashboard' => [ + 'menu_label' => 'แผงควบคุม', + 'widget_label' => 'วิดเจ็ท', + 'widget_width' => 'ความกว้าง', + 'full_width' => 'ความกว้างเต็มหน้า', + 'manage_widgets' => 'จัดการวิดเจ็ท', + 'add_widget' => 'เพิ่มวิดเจ็ท', + 'widget_inspector_title' => 'การปรับแต่งค่าวิดเจ็ท', + 'widget_inspector_description' => 'ปรับแต่งค่าวิดเจ็ทแบบรายงาน', + 'widget_columns_label' => 'กว้าง :columns', + 'widget_columns_description' => 'ความกว้างของวิดเจ็ท, ตัวเลขระหว่าง 1 ถึง 10', + 'widget_columns_error' => 'กรุณาใส่ค่าความกว้างของวิดเจ็ทเป็นตัวเลขระหว่าง 1 ถึง 10', + 'columns' => '{1} คอลัมน์|[2,Inf] คอลัมน์', + 'widget_new_row_label' => 'บังคับขึ้นแถวใหม่', + 'widget_new_row_description' => 'วางวิดเจ็ทไว้ในแถวใหม่', + 'widget_title_label' => 'หัวเรื่องวิดเจ็ท', + 'widget_title_error' => 'ต้องมีหัวเรื่องวิดเจ็ท', + 'reset_layout' => 'ตั้งค่าเริ่มต้นการวางโครงร่าง', + 'reset_layout_confirm' => 'ตั้งค่าการวางโครงร่างกลับสู่ค่าเริ่มต้น?', + 'reset_layout_success' => 'ตั้งค่าการวางโครงร่างเป็นค่าเริ่มต้นแล้ว', + 'make_default' => 'ทำให้เป็นค่าเริ่มต้น', + 'make_default_confirm' => 'ตั้งการวางโครงร่างปัจจุบันเป็นค่าเริ่มต้น?', + 'make_default_success' => 'ตั้งการวางโครงร่างปัจจุบันเป็นค่าเริ่มต้นแล้ว', + 'collapse_all' => 'ย่อทั้งหมด', + 'expand_all' => 'ขยายทั้งหมด', + 'status' => [ + 'widget_title_default' => 'สถานะระบบ', + 'updates_pending' => 'รอการอัพเดทซอฟต์แวร์', + 'updates_nil' => 'ซอฟต์แวร์เป็นปัจจุบันแล้ว', + 'updates_link' => 'อัพเดท', + 'warnings_pending' => 'มีปัญหาบางอย่างต้องตรวจสอบ', + 'warnings_nil' => 'ไม่มีคำเตือนให้แสดง', + 'warnings_link' => 'ดู', + 'core_build' => 'ระบบ', + 'event_log' => 'บันทึกเหตุการณ์', + 'request_log' => 'บันทึกการขอเข้าใช้งาน', + 'app_birthday' => 'ออนไลน์ตั้งแต่', + ], + 'welcome' => [ + 'widget_title_default' => 'ยินดีต้อนรับ', + 'welcome_back_name' => 'ยินดีต้อนรับกลับมาสู่ :app, :name.', + 'welcome_to_name' => 'ยินดีต้อนรับสู่ :app, :name.', + 'first_sign_in' => 'นี่เป็นครั้งแรกที่คุณได้ลงชื่อเข้า', + 'last_sign_in' => 'คุณได้ลงชื่อเข้าครั้งสุดท้ายเมื่อ', + 'view_access_logs' => 'ดูบันทึกการเข้าถึง', + 'nice_message' => 'ขอให้มีวันที่ยอดเยี่ยม!', + ], + ], + 'user' => [ + 'name' => 'ผู้ดูแลระบบ', + 'menu_label' => 'ผู้ดูแลระบบ', + 'menu_description' => 'กำหนด ผู้ใช้ กลุ่ม และสิทธิ์การใช้งาน ของหน้าเว็บหลังบ้าน', + 'list_title' => 'จัดการผู้ดูแลระบบ', + 'new' => 'สร้างผู้ดูแลระบบใหม่', + 'login' => 'ชื่อล็อกอิน', + 'first_name' => 'ชื่อ', + 'last_name' => 'นามสกุล', + 'full_name' => 'ชื่อเต็ม', + 'email' => 'อีเมล', + 'role_field' => 'บทบาท', + 'role_comment' => 'บทบาทกำหนดสิทธิ์การใช้งานของผู้ใช้ ซึ่งสามารถกำหนดทับเป็นรายผู้ใช้ได้ ที่แท็บสิทธิ์การใช้งาน', + 'groups' => 'กลุ่ม', + 'groups_comment' => 'กำหนดว่ามีกลุ่มไหนบ้างที่ผู้ใช้เป็นสมาชิก', + 'avatar' => 'อวตาร', + 'password' => 'รหัสผ่าน', + 'password_confirmation' => 'ยืนยันรหัสผ่าน', + 'permissions' => 'สิทธิ์การใช้งาน', + 'account' => 'บัญชีผู้ใช้', + 'superuser' => 'Super User', + 'superuser_comment' => 'อนุญาตให้บัญชีผู้ใช้นี้สามารถเข้าถึงได้ทุกระบบ Super user สามารถเพิ่มหรือจัดการผู้ใช้คนอื่นได้', + 'send_invite' => 'ส่งคำเชิญทางอีเมล', + 'send_invite_comment' => 'ส่งข้อความยินดีต้อนรับ พร้อมข้อมูลล็อกอินและรหัสผ่าน', + 'delete_confirm' => 'ลบผู้ดูแลระบบนี้?', + 'return' => 'กลับสู่รายชื่อผู้ดูแลระบบ', + 'allow' => 'อนุญาต', + 'inherit' => 'สืบต่อ', + 'deny' => 'ปฏิเสธ', + 'activated' => 'เริ่มการทำงานแล้ว', + 'last_login' => 'ล็อกอินครั้งสุดท้าย', + 'created_at' => 'สร้างเมื่อ', + 'updated_at' => 'อัพเดทเมื่อ', + 'deleted_at' => 'ลบเมื่อ', + 'show_deleted' => 'แสดงคนที่ถูกลบ', + 'group' => [ + 'name' => 'กลุ่ม', + 'name_field' => 'ชื่อ', + 'name_comment' => 'เป็นชื่อที่ถูกแสดงผลในรายการกลุ่มในหน้าผู้ดูแลระบบ', + 'description_field' => 'รายละเอียด', + 'is_new_user_default_field_label' => 'เข้ากลุ่มอัตโนมัติ', + 'is_new_user_default_field_comment' => 'เพิ่มผู้ดูแลระบบใหม่เข้ากลุ่มนี้โดยอัตโนมัติ', + 'code_field' => 'รหัส', + 'code_comment' => 'ใส่รหัสที่ไม่ซ้ำ ถ้าคุณต้องการเข้าถึงกลุ่มผ่านทาง API', + 'menu_label' => 'จัดการกลุ่ม', + 'list_title' => 'จัดการกลุ่ม', + 'new' => 'กลุ่มใหม่', + 'delete_confirm' => 'ลบกลุ่มนี้?', + 'return' => 'กลับสู่รายชื่อกลุ่ม', + 'users_count' => 'จำนวนผู้ใช้', + ], + 'role' => [ + 'name' => 'บทบาท', + 'name_field' => 'ชื่อ', + 'name_comment' => 'เป็นชื่อที่ถูกแสดงผลในรายการบทบาทในหน้าผู้ดูแลระบบ', + 'description_field' => 'รายละเอียด', + 'code_field' => 'รหัส', + 'code_comment' => 'ใส่รหัสที่ไม่ซ้ำ ถ้าคุณต้องการเข้าถึงบทบาทผ่านทาง API', + 'menu_label' => 'จัดการบทบาท', + 'list_title' => 'จัดการบทบาท', + 'new' => 'บทบาทใหม่', + 'delete_confirm' => 'ลบบทบาทนี้?', + 'return' => 'กลับสู่รายชื่อบทบาท', + 'users_count' => 'จำนวนผู้ใช้', + ], + 'preferences' => [ + 'not_authenticated' => 'ไม่พบผู้ใช้ที่ได้รับการยืนยันแล้วสำหรับอ่านหรือบันทึกค่าที่เลือก', + ], + 'trashed_hint_title' => 'บัญชีผู้ใช้นี้ถูกลบไปแล้ว', + 'trashed_hint_desc' => 'บัญชีผู้ใช้นี้ถูกลบไปแล้ว และจะไม่สามารถลงชื่อเข้าระบบได้ หากต้องการกู้คืน, คลิกไอคอน restore user ที่มุมล่างขวา', + ], + 'list' => [ + 'default_title' => 'รายการ', + 'search_prompt' => 'ค้นหา...', + 'no_records' => 'ไม่มีข้อมูลในหน้านี้', + 'missing_column' => 'ไม่มีการกำหนดค่าคอลัมน์ :columns.', + 'pagination' => 'แสดงข้อมูล: :from-:to จาก :total', + 'first_page' => 'หน้าแรก', + 'last_page' => 'หน้าสุดท้าย', + 'prev_page' => 'หน้าก่อน', + 'next_page' => 'หน้าถัดไป', + 'refresh' => 'รีเฟรช', + 'updating' => 'กำลังอัพเดท...', + 'loading' => 'กำลังโหลด...', + 'setup_title' => 'กำหนดค่ารายการ', + 'setup_help' => 'เลือก checkbox เพื่อเลือกคอลัมน์ที่คุณต้องการแสดงผลในตาราง คุณสามารถย้ายตำแหน่งของคอลัมน์โดยลากคอลัมน์ขึ้นหรือลง', + 'records_per_page' => 'ข้อมูลต่อหน้า', + 'records_per_page_help' => 'เลือกจำนวนข้อมูลต่อหน้าในการแสดงผล การเลือกจำนวนต่อหน้ามากๆทำให้ประสิทธิภาพลดลงได้', + 'check' => 'ตรวจสอบ', + 'delete_selected' => 'ลบรายการที่เลือกไว้', + 'delete_selected_empty' => 'ไม่มีรายการที่เลือกไว้', + 'delete_selected_confirm' => 'ต้องการลบรายการที่เลือกไว้?', + 'delete_selected_success' => 'ลบรายการที่เลือกไว้แล้ว', + 'column_switch_true' => 'ใช่', + 'column_switch_false' => 'ไม่', + ], + 'fileupload' => [ + 'attachment' => 'สิ่งที่แนบมา', + 'help' => 'เพิ่มหัวเรื่องและรายละเอียดสำหรับไฟล์แนบนี้', + 'title_label' => 'หัวเรื่อง', + 'description_label' => 'รายละเอียด', + 'default_prompt' => 'คลิก %s หรือลากไฟล์มาวางที่นี่เพื่ออัพโหลด', + 'attachment_url' => 'URL ไฟล์แนบ', + 'upload_file' => 'อัพโหลดไฟล์', + 'upload_error' => 'อัพโหลดผิดพลาด', + 'remove_confirm' => 'คุณแน่ใจ?', + 'remove_file' => 'ลบไฟล์', + ], + 'form' => [ + 'create_title' => 'สร้าง :name', + 'update_title' => 'แก้ไข :name', + 'preview_title' => 'ดูตัวอย่าง :name', + 'create_success' => 'สร้าง :name แล้ว', + 'update_success' => 'แก้ไข :name แล้ว', + 'delete_success' => 'ลบ :name แล้ว', + 'restore_success' => 'กู้คืน :name แล้ว', + 'reset_success' => 'ตั้งค่าเริ่มต้นสำเร็จ', + 'action_confirm' => 'คุณแน่ใจหรือไม่?', + 'create' => 'สร้าง', + 'create_and_close' => 'สร้าง และ ปิด', + 'creating' => 'กำลังสร้าง...', + 'creating_name' => 'กำลังสร้าง :name...', + 'save' => 'บันทึก', + 'save_and_close' => 'บันทึก และ ปิด', + 'saving' => 'กำลังบันทึก...', + 'saving_name' => 'กำลังบันทึก :name...', + 'delete' => 'ลบ', + 'deleting' => 'กำลังลบ...', + 'confirm_delete' => 'ลบข้อมูล?', + 'confirm_delete_multiple' => 'ลบข้อมูลที่เลือกไว้?', + 'deleting_name' => 'กำลังลบ :name...', + 'restore' => 'กู้คืน', + 'restoring' => 'กำลังกู้คืน', + 'confirm_restore' => 'คุณแน่ใจว่าต้องการกู้คืนข้อมูลนี้?', + 'reset_default' => 'ตั้งค่าเริ่มต้น', + 'resetting' => 'กำลังตั้งค่าเริ่มต้น', + 'resetting_name' => 'กำลังตั้งค่าเริ่มต้น :name', + 'undefined_tab' => 'อื่นๆ', + 'field_off' => 'ปิด', + 'field_on' => 'เปิด', + 'add' => 'เพิ่ม', + 'apply' => 'นำไปใช้', + 'cancel' => 'ยกเลิก', + 'close' => 'ปิด', + 'confirm' => 'ยืนยัน', + 'reload' => 'Reload', + 'complete' => 'เสร็จสมบูรณ์', + 'ok' => 'ตกลง', + 'or' => 'หรือ', + 'confirm_tab_close' => 'ปิดแท็บนี้? การเปลี่ยนแปลงที่ยังไม่ได้บันทึกจะสูญหาย', + 'preview_no_files_message' => 'ไม่มีไฟล์ถูกอัพโหลด', + 'preview_no_media_message' => 'ไม่มีสื่อบันทึกที่ถูกเลือก', + 'select' => 'เลือก', + 'select_all' => 'เลือกทั้งหมด', + 'select_none' => 'ไม่เลือกเลย', + 'select_placeholder' => 'กรุณาเลือก', + 'insert_row' => 'เพิ่มบรรทัด', + 'insert_row_below' => 'เพิ่มบรรทัดด้านล่าง', + 'delete_row' => 'ลบบรรทัด', + 'concurrency_file_changed_title' => 'ไฟล์ได้ถูกแก้ไข', + 'concurrency_file_changed_description' => "ไฟล์ที่คุณกำลังแก้ไขได้ถูกเปลี่ยนแปลงโดยผู้ใช้ท่านอื่น คุณสามารถโหลดไฟล์ใหม่แต่สิ่งที่แก้ไขไว้จะหายไป หรือบันทึกการแก้ไขทับ", + 'return_to_list' => 'กลับสู่หน้ารายการ', + ], + 'recordfinder' => [ + 'cancel' => 'ยกเลิก', + ], + 'pagelist' => [ + 'page_link' => 'ลิงก์หน้าเว็บ', + 'select_page' => 'เลือกหน้า...', + ], + 'relation' => [ + 'add' => 'เพิ่ม', + 'add_selected' => 'เพิ่มรายการที่เลือก', + 'add_a_new' => 'เพิ่มใหม่ :name', + 'link_selected' => 'ลิงก์รายการที่เลือก', + 'link_a_new' => 'ลิงก์ใหม่ :name', + 'cancel' => 'ยกเลิก', + 'close' => 'ปิด', + 'add_name' => 'เพิ่ม :name', + 'create' => 'สร้าง', + 'create_name' => 'สร้าง :name', + 'update' => 'อัพเดท', + 'update_name' => 'อัพเดท :name', + 'preview' => 'ดูตัวอย่าง', + 'preview_name' => 'ดูตัวอย่าง :name', + 'remove' => 'เอาออก', + 'remove_name' => 'เอาออก :name', + 'delete' => 'ลบ', + 'delete_name' => 'ลบ :name', + 'delete_confirm' => 'คุณแน่ใจหรือไม่?', + 'link' => 'ลิงก์', + 'link_name' => 'ลิงก์ :name', + 'unlink' => 'ยกเลิกลิงก์', + 'unlink_name' => 'ยกเลิกลิงก์ :name', + 'unlink_confirm' => 'คุณแน่ใจหรือไม่?', + ], + 'warnings' => [ + 'tips' => 'เคล็ดลับการตั้งค่าระบบ', + 'tips_description' => 'ประเด็นต่างๆที่ท่านต้องให้ความสนใจเพื่อตั้งค่าระบบได้อย่างเหมาะสม', + 'permissions' => 'โฟลเดอร์ :name หรือโฟลเดอร์ย่อยของมัน ไม่สามารถเขียนได้โดย PHP กรุณาตั้งสิทธิ์ที่สอดคล้องกับเว็บเซิร์ฟเวอร์ที่โฟลเดอร์นี้', + 'extension' => 'ส่วนต่อเติม PHP :name ไม่ถูกติดตั้ง กรุณาติดตั้งส่วนต่อเติมนี้และเปิดการใช้งานส่วนต่อเติม', + 'plugin_missing' => 'ปลั๊กอิน :name เป็น dependency แต่ไม่ถูกติดตั้ง กรุณาติดตั้งปลั๊กอินนี้', + ], + 'editor' => [ + 'menu_label' => 'การตั้งค่าตัวแก้ไข', + 'menu_description' => 'ปรับแก้ตามความชอบตัวแก้ไข เช่น ขนาดฟอนท์ และแผนผังสี', + 'font_size' => 'ขนาดฟอนท์', + 'tab_size' => 'ขนาดแท็บ', + 'use_hard_tabs' => 'ย่อหน้าโดยใช้แท็บ', + 'code_folding' => 'พับย่อโค้ด', + 'code_folding_begin' => 'ทำเครื่องหมายที่บรรทัดเริ่มต้น', + 'code_folding_begin_end' => 'ทำเครื่องหมายที่บรรทัดเริ่มต้นและสุดท้าย', + 'autocompletion' => 'การเติมคำโดยอัตโนมัติ', + 'word_wrap' => 'ขึ้นบรรทัดใหม่', + 'highlight_active_line' => 'ไฮไลท์บรรทัดที่เลือก', + 'auto_closing' => 'ปิดแท็กโดยอัตโนมัติ', + 'show_invisibles' => 'แสดงตัวอักษรที่มองไม่เห็น', + 'show_gutter' => 'แสดงเลขบรรทัด', + 'basic_autocompletion' => 'การเติมคำอัตโนมัติพื้นฐาน (คอนโทรล + สเปซ)', + 'live_autocompletion' => 'การเติมคำอัตโนมัติทันที', + 'enable_snippets' => 'เปิดใช้งาน code snippets (แท็บ)', + 'display_indent_guides' => 'แสดงแนวเส้นย่อหน้า', + 'show_print_margin' => 'แสดงขอบการพิมพ์', + 'mode_off' => 'ปิด', + 'mode_fluid' => 'ลื่นไหล', + '40_characters' => '40 ตัวอักษร', + '80_characters' => '80 ตัวอักษร', + 'theme' => 'แผนผังสี', + 'markup_styles' => 'รูปแบบ', + 'custom_styles' => 'กำหนด stylesheet เอง', + 'custom styles_comment' => 'รูปแบบกำหนดเองที่จะเพิ่มในตัวแก้ไข HTML', + 'markup_classes' => 'คลาส', + 'paragraph' => 'ย่อหน้า', + 'link' => 'ลิงก์', + 'table' => 'ตาราง', + 'table_cell' => 'เซลล์ตาราง', + 'image' => 'รูปภาพ', + 'label' => 'ป้ายชื่อ', + 'class_name' => 'ชื่อคลาส', + 'markup_tags' => 'แท็ก', + 'allowed_empty_tags' => 'แท็กเปล่าที่อนุญาตให้ใช้ได้', + 'allowed_empty_tags_comment' => 'รายการแท็กที่ไม่ถูกเอาออกถ้าไม่มีเนื้อหาภายในแท็ก', + 'allowed_tags' => 'แท็กที่ใช้ได้', + 'allowed_tags_comment' => 'รายการแท็กที่อนุญาตให้ใช้', + 'no_wrap' => 'แท็กที่ไม่ควรห่อ', + 'no_wrap_comment' => 'รายการแท็กที่ไม่ควรถูกห่ออยู่ในบล๊อก', + 'remove_tags' => 'เอาแท็กออก', + 'remove_tags_comment' => 'รายการของแท็กที่ถูกเอาออกรวมถึงเนื้อหาข้างใน', + 'line_breaker_tags' => 'แท็กสำหรับขึ้นบรรทัดใหม่', + 'line_breaker_tags_comment' => 'รายการของแท็กที่ให้ตัวขึ้นบรรทัดใหม่วางคั่นได้', + 'toolbar_buttons' => 'ปุ่มในแถบเครื่องมือ', + 'toolbar_buttons_comment' => 'ปุ่มในแถบเครื่องมือที่จะแสดงในหน้าแก้ไขโดยอัตโนมัติ [fullscreen, bold, italic, underline, strikeThrough, subscript, superscript, fontFamily, fontSize, |, color, emoticons, inlineStyle, paragraphStyle, |, paragraphFormat, align, formatOL, formatUL, outdent, indent, quote, insertHR, -, insertLink, insertImage, insertVideo, insertAudio, insertFile, insertTable, undo, redo, clearFormatting, selectAll, html]', + ], + 'tooltips' => [ + 'preview_website' => 'ดูตัวอย่างเว็บไซต์', + ], + 'mysettings' => [ + 'menu_label' => 'การตั้งค่าของฉัน', + 'menu_description' => 'การตั้งค่าที่เกี่ยวกับบัญชีผู้ดูแลระบบของท่าน', + ], + 'myaccount' => [ + 'menu_label' => 'บัญชีของฉัน', + 'menu_description' => 'อัพเดทรายละเอียดบัญชีผู้ใช้ของท่านเช่น ชื่อ อีเมล และรหัสผ่าน', + ], + 'branding' => [ + 'menu_label' => 'ปรับแก้หน้าเว็บหลังบ้าน', + 'menu_description' => 'ปรับแก้ส่วนจัดการระบบ เช่น ชื่อ สี และโลโก้', + 'brand' => 'เครื่องหมายการค้า', + 'logo' => 'โลโก้', + 'logo_description' => 'อัพโหลดโลโก้สำหรับใช้ในหน้าเว็บหลังบ้าน', + 'favicon' => 'ไอคอนสำหรับเว็บไซต์ (Favicon)', + 'favicon_description' => 'อัพโหลดไอคอนสำหรับเว็บไซต์สำหรับใช้ในหน้าเว็บหลังบ้าน', + 'app_name' => 'ชื่อแอป', + 'app_name_description' => 'ชื่อนี้จะถูกแสดงที่หัวเรื่องของหน้าเว็บหลังบ้าน', + 'app_tagline' => 'สโลแกนของแอป', + 'app_tagline_description' => 'สโลแกนนี้จะถูกแสดงในหน้าลงชื่อเข้าระบบของเว็บหลังบ้าน', + 'colors' => 'สี', + 'primary_color' => 'สีหลัก', + 'secondary_color' => 'สีรอง', + 'accent_color' => 'สีเน้น', + 'styles' => 'รูปแบบ', + 'custom_stylesheet' => 'กำหนด stylesheet เอง', + 'navigation' => 'ตัวนำทาง', + 'menu_mode' => 'รูปแบบเมนู', + 'menu_mode_inline' => 'อยู่ในบรรทัดเดียว', + 'menu_mode_inline_no_icons' => 'อยู่ในบรรทัดเดียว (ไม่มีไอคอน)', + 'menu_mode_tile' => 'กระเบื้อง', + 'menu_mode_collapsed' => 'ย่อ', + ], + 'backend_preferences' => [ + 'menu_label' => 'หน้าเว็บหลังบ้านตามใจชอบ', + 'menu_description' => 'จัดการบัญชีผู้ใช้ของคุณตามใจชอบ เช่นเลือกภาษาที่ต้องการ', + 'region' => 'ภูมิภาค', + 'code_editor' => 'ตัวแก้ไขโค้ด', + 'timezone' => 'เขตเวลา (Timezone)', + 'timezone_comment' => 'ปรับแก้การแสดงวันเวลาตามเขตเวลานี้', + 'locale' => 'ภาษาท้องถิ่น (Locale)', + 'locale_comment' => 'เลือกภาษาท้องถิ่นที่ต้องการ', + ], + 'access_log' => [ + 'hint' => 'บันทึกนี้แสดงรายการการล็อกอินที่สำเร็จโดยผู้ดูแลระบบ บันทึกจะถูกเก็บไว้เป็นเวลา :days วัน', + 'menu_label' => 'บันทึกการเข้าถึง', + 'menu_description' => 'แสดงรายการของการลงชื่อที่สำเร็จของผู้ใช้หลังบ้าน', + 'id' => 'ไอดี', + 'created_at' => 'วันเวลา', + 'type' => 'ประเภท', + 'login' => 'ชื่อล็อกอิน', + 'ip_address' => 'ไอพีแอดเดรส', + 'first_name' => 'ชื่อ', + 'last_name' => 'นามสกุล', + 'email' => 'อีเมล', + ], + 'filter' => [ + 'all' => 'ทั้งหมด', + 'date_all' => 'ช่วงเวลาทั้งหมด', + 'number_all' => 'เลขทั้งหมด', + ], + 'import_export' => [ + 'upload_csv_file' => '1. อัพโหลดไฟล์ CSV', + 'created' => 'สร้างแล้ว', + 'updated' => 'อัพเดทแล้ว', + ], + 'permissions' => [ + 'manage_media' => 'อัพโหลดและจัดการเนื้อหาสื่อบันทึก - รูปภาพ, วิดีโอ, เสียง, เอกสาร', + ], + 'mediafinder' => [ + 'label' => 'ตัวค้นหาคลังข้อมูล', + 'default_prompt' => 'คลิกปุ่ม %s เพื่อค้นหา', + 'no_image' => 'หารูปภาพไม่พบ', + ], + 'media' => [ + 'menu_label' => 'สื่อบันทึก', + 'upload' => 'อัพโหลด', + 'move' => 'ย้าย', + 'delete' => 'ลบ', + 'add_folder' => 'เพิ่มโฟลเดอร์', + 'search' => 'ค้นหา', + 'display' => 'แสดงผล', + 'filter_everything' => 'ทุกอย่าง', + 'filter_images' => 'รูปภาพ', + 'filter_video' => 'วิดีโอ', + 'filter_audio' => 'เสียง', + 'filter_documents' => 'เอกสาร', + 'library' => 'คลัง', + 'size' => 'ขนาด', + 'title' => 'หัวเรื่อง', + 'last_modified' => 'แก้ไขครั้งสุดท้าย', + 'public_url' => 'URL', + 'click_here' => 'คลิกที่นี่', + 'thumbnail_error' => 'การสร้างรูปย่อผิดพลาด', + 'return_to_parent' => 'กลับสู่โฟลเดอร์บน', + 'return_to_parent_label' => 'ไปข้างบน ..', + 'nothing_selected' => 'ไม่มีรายการที่ถูกเลือก', + 'multiple_selected' => 'มีหลายรายการถูกเลือก', + 'uploading_file_num' => 'กำลังอัพโหลด :number ไฟล์...', + 'uploading_complete' => 'อัพโหลดสำเร็จ', + 'uploading_error' => 'อัพโหลดไม่สำเร็จ', + 'type_blocked' => 'ประเภทไฟล์นี้ถูกบล๊อกเพื่อความปลอดภัย', + 'order_by' => 'เรียงลำดับโดย', + 'direction' => 'ทิศทาง', + 'direction_asc' => 'จากน้อยไปมาก', + 'direction_desc' => 'จากมากไปน้อย', + 'folder' => 'โฟลเดอร์', + 'no_files_found' => 'ไม่พบไฟล์', + 'delete_empty' => 'กรุณาเลือกไฟล์หรือโฟลเดอร์ที่ต้องการลบ', + 'delete_confirm' => 'ลบไฟล์หรือโฟลเดอร์ที่ถูกเลือก?', + 'error_renaming_file' => 'มีปัญหาการเปลี่ยนชื่อ', + 'new_folder_title' => 'โฟลเดอร์ใหม่', + 'folder_name' => 'ชื่อโฟลเดอร์', + 'error_creating_folder' => 'มีข้อผิดพลาดในการสร้างโฟลเดอร์', + 'folder_or_file_exist' => 'ไฟล์หรือโฟลเดอร์ตามชื่อนี้มีอยู่แล้ว', + 'move_empty' => 'กรุณาเลือกไฟล์หรือโฟลเดอร์ที่ต้องการย้าย', + 'move_popup_title' => 'ย้ายไฟล์หรือโฟลเดอร์', + 'move_destination' => 'โฟลเดอร์ปลายทาง', + 'please_select_move_dest' => 'กรุณาเลือกโฟลเดอร์ปลายทาง', + 'move_dest_src_match' => 'กรุณาเลือกโฟลเดอร์ปลายทางอื่น', + 'empty_library' => 'มันดูว่างๆไปหน่อยนะ เริ่มด้วยการอัพโหลดไฟล์ หรือสร้างโฟลเดอร์เลย', + 'insert' => 'แทรก', + 'crop_and_insert' => 'หั่น & แทรก', + 'select_single_image' => 'กรุณาเลือกไฟล์รูปภาพไฟล์เดียว', + 'selection_not_image' => 'รายการที่ถูกเลือกไม่ใช่รูปภาพ', + 'restore' => 'ยกเลิกการเปลี่ยนแปลง', + 'resize' => 'เปลี่ยนขนาด...', + 'selection_mode_normal' => 'ปกติ', + 'selection_mode_fixed_ratio' => 'อัตราส่วนคงที่', + 'selection_mode_fixed_size' => 'ขนาดคงที่', + 'height' => 'ความสูง', + 'width' => 'ความกว้าง', + 'selection_mode' => 'โหมดการเลือก', + 'resize_image' => 'เปลี่ยนขนาดรูปภาพ', + 'image_size' => 'ขนาดรูปภาพ:', + 'selected_size' => 'พื้นที่เลือก:', + ], +]; diff --git a/modules/backend/lang/tr/lang.php b/modules/backend/lang/tr/lang.php index 8c6101549..685a3e549 100644 --- a/modules/backend/lang/tr/lang.php +++ b/modules/backend/lang/tr/lang.php @@ -27,9 +27,6 @@ return [ 'help' => "Yönetim paneline erişebilmeniz için geçerli bir veritabanı yapılandırması yapmalısınız. Lütfen ayarların kontrol edin.", 'cms_link' => 'Anasayfaya dön', ], - 'invalid_token' => [ - 'label' => 'Geçersiz güvenlik anahtarı' - ], ], 'partial' => [ 'not_found_name' => "':name' bölümü bulunamadı.", @@ -277,8 +274,8 @@ return [ 'preview_no_media_message' => 'Seçilmiş medya yok.', 'preview_no_record_message' => 'Seçili kayıt yok.', 'select' => 'Seç', - 'select_all' => 'tümü', - 'select_none' => 'hiçbiri', + 'select_all' => 'hepsini seç', + 'select_none' => 'hiçbir şey seçilmedi', 'select_placeholder' => 'lütfen seçin', 'insert_row' => 'Kayıt Ekle', 'insert_row_below' => 'Alt Satıra Kayıt Ekle', diff --git a/modules/backend/lang/uk/lang.php b/modules/backend/lang/uk/lang.php index 186e62d8c..4972a1830 100644 --- a/modules/backend/lang/uk/lang.php +++ b/modules/backend/lang/uk/lang.php @@ -25,9 +25,6 @@ return [ 'help' => 'Для доступу до back-end - потрібна база данних. Перевірте, налаштування та міграції бази данних, перш ніж спробувати знову.', 'cms_link' => 'Повернутися на домашню сторінку' ], - 'invalid_token' => [ - 'label' => 'Неправильний токен безпеки' - ], ], 'partial' => [ 'not_found_name' => "Частину ':name' не знайдено.", @@ -262,8 +259,8 @@ return [ 'preview_no_media_message' => 'Немає обраного файла.', 'preview_no_record_message' => 'Немає обраних записів.', 'select' => 'Обрати', - 'select_all' => 'всі', - 'select_none' => 'жоден', + 'select_all' => 'вибрати все', + 'select_none' => 'вибрати жоден', 'select_placeholder' => 'будь ласка, оберіть', 'insert_row' => 'Вставити рядок', 'insert_row_below' => 'Вставити рядок нижче', diff --git a/modules/backend/lang/vn/lang.php b/modules/backend/lang/vn/lang.php index 5b31171b4..3eb139430 100644 --- a/modules/backend/lang/vn/lang.php +++ b/modules/backend/lang/vn/lang.php @@ -25,9 +25,6 @@ return [ 'help' => "Bắt buộc phải có 1 database để truy cập vào trang quản trị. Kiểm tra lại cấu hình database và migrated trước khi thử lại.", 'cms_link' => 'Quay lại trang chủ' ], - 'invalid_token' => [ - 'label' => 'Security token không hợp lệ' - ] ], 'partial' => [ 'not_found_name' => "Không tìm thấy partial ':name'." @@ -263,8 +260,8 @@ return [ 'preview_no_media_message' => 'Không có file media nào được chọn.', 'preview_no_record_message' => 'Không có bản ghi nào được chọn.', 'select' => 'Select', - 'select_all' => 'Tất cả', - 'select_none' => 'none', + 'select_all' => 'chọn tất cả', + 'select_none' => 'không chọn', 'select_placeholder' => 'Vui lòng lựa chọn', 'insert_row' => 'Thêm mới Row', 'insert_row_below' => 'Thêm Row dưới đây', diff --git a/modules/backend/lang/zh-cn/lang.php b/modules/backend/lang/zh-cn/lang.php index 07f142a05..eccff193a 100644 --- a/modules/backend/lang/zh-cn/lang.php +++ b/modules/backend/lang/zh-cn/lang.php @@ -2,12 +2,14 @@ return [ 'auth' => [ - 'title' => '管理' + 'title' => '管理', + 'invalid_login' => '无法匹配到您输入的相关内容,请检查后重试。' ], 'field' => [ 'invalid_type' => '不合法的字段类型 :type.', 'options_method_invalid_model' => "属性 ':field' 不能解析为有效模型. 请尝试为模型类:model指定一个可选方法.", - 'options_method_not_exists' => "模型 :model 必须定义一个返回 ':field' 表单字段选项的方法 :method()。" + 'options_method_not_exists' => "模型 :model 必须定义一个返回 ':field' 表单字段选项的方法 :method()。", + 'colors_method_not_exists' => "模型 :model 必须定义一个返回十六进制格式的颜色值 ':field' 字段的 :method() 方法。 " ], 'widget' => [ 'not_registered' => "未注册部件 ':name' ", @@ -15,6 +17,11 @@ return [ ], 'page' => [ 'untitled' => '未命名', + '404' => [ + 'label' => '找不到页面', + 'help' => "无法访问到您请求的URL资源,试试其它的?", + 'back_link' => '返回上个页面' + ], 'access_denied' => [ 'label' => '拒绝访问', 'help' => "您没有访问该页面所需的权限.", @@ -25,20 +32,24 @@ return [ 'help' => "需要数据库以访问后端。请在再次尝试之前检查数据库的配置和迁移。", 'cms_link' => '返回首页' ], - 'invalid_token' => [ - 'label' => '非法安全令牌' - ] ], 'partial' => [ - 'not_found_name' => "未找到部件 ':name' " + 'not_found_name' => "未找到部件 ':name' ", + 'invalid_name' => '未知的部件名称 :name ' + ], + 'ajax_handler' => [ + 'invalid_name' => '未知的AJAX处理方法 :name.', + 'not_found' => "无法找到AJAX处理方法 ':name' " ], 'account' => [ + 'signed_in_as' => '以 :full_name 身份登陆', 'sign_out' => '登出', 'login' => '登录', 'reset' => '重置', 'restore' => '还原', 'login_placeholder' => '登录', 'password_placeholder' => '密码', + 'remember_me' => '保持登录状态', 'forgot_password' => '忘记密码?', 'enter_email' => '输入邮件地址', 'enter_login' => '输入账号', @@ -115,6 +126,8 @@ return [ 'last_name' => '姓', 'full_name' => '全名', 'email' => '邮件', + 'role_field' => '角色', + 'role_comment' => '角色指定了用户的权限,您可以在权限栏中进行修改。', 'groups' => '团队', 'groups_comment' => '指定成员所归属的组.', 'avatar' => '头像', @@ -135,6 +148,8 @@ return [ 'last_login' => '最后登陆', 'created_at' => '创建时间', 'updated_at' => '更新时间', + 'deleted_at' => '删除时间', + 'show_deleted' => '显示已删除', 'group' => [ 'name' => '组', 'name_comment' => '该名字将在群组列表中展示', @@ -151,9 +166,25 @@ return [ 'return' => '返回组列表', 'users_count' => '用户' ], + 'role' => [ + 'name' => '角色', + 'name_field' => '名称', + 'name_comment' => '名称会显示在管理员菜单下的角色列表中', + 'description_field' => '描述', + 'code_field' => '角色代码', + 'code_comment' => '如果你想通过 API 访问角色对象,请输入一个唯一的角色代码', + 'menu_label' => '管理角色', + 'list_title' => '管理角色', + 'new' => '新建角色', + 'delete_confirm' => '确定删除该角色?', + 'return' => '返回角色列表', + 'users_count' => '用户' + ], 'preferences' => [ 'not_authenticated' => '无认证用户加载或保存设置.' - ] + ], + 'trashed_hint_title' => '该账户已经被删除', + 'trashed_hint_desc' => '该账户已经被删除而无法登录。你可以点击右下角的恢复按钮进行恢复。' ], 'list' => [ 'default_title' => '列表', @@ -198,6 +229,10 @@ return [ 'remove_confirm' => '你确定吗?', 'remove_file' => '删除文件' ], + 'repeater' => [ + 'min_items_failed' => ':name 需要大于 :min ', + 'max_items_failed' => ':name 需要小于 :max ' + ], 'form' => [ 'create_title' => '新 :name', 'update_title' => '编辑 :name', @@ -205,7 +240,8 @@ return [ 'create_success' => '成功创建 :name', 'update_success' => '成功更新 :name', 'delete_success' => '成功删除 :name', - 'reset_success' => '成功恢复默认', + 'restore_success' => '成功恢复 :name', + 'reset_success' => '重置成功', 'missing_id' => '未指定表单记录ID.', 'missing_model' => ':class 中使用的表单无定义模型.', 'missing_definition' => "表单无字段 ':field'.", @@ -224,7 +260,10 @@ return [ 'confirm_delete' => '您确定删除记录?', 'confirm_delete_multiple' => '确认删除选中的的记录?', 'deleting_name' => '删除 :name...', - 'reset_default' => '重置默认', + 'restore' => '恢复', + 'restoring' => '恢复中', + 'confirm_restore' => '你确定恢复这条记录?', + 'reset_default' => '重置为默认', 'resetting' => '重置', 'resetting_name' => '重置 :name', 'undefined_tab' => '杂项', @@ -245,8 +284,8 @@ return [ 'preview_no_media_message' => '无选中媒体.', 'preview_no_record_message' => '无选择记录。', 'select' => '选择', - 'select_all' => '全部', - 'select_none' => '无', + 'select_all' => '全选', + 'select_none' => '选择无', 'select_placeholder' => '请选择', 'insert_row' => '插入行', 'insert_row_below' => '在下面插入行', @@ -257,6 +296,7 @@ return [ ], 'recordfinder' => [ 'find_record' => '查找记录', + 'invalid_model_class' => '提供的 ":modelClass" 不可用', 'cancel' => '取消', ], 'pagelist' => [ @@ -360,7 +400,9 @@ return [ 'no_wrap' => '无法包裹标签', 'no_wrap_comment' => '所列标签无法包裹于快级标签中.', 'remove_tags' => '移除标签', - 'remove_tags_comment' => '所列标签将与其包裹的内容一起删除.' + 'remove_tags_comment' => '所列标签将与其包裹的内容一起删除.', + 'toolbar_buttons' => '工具栏按钮', + 'toolbar_buttons_comment' => '默认在富文本编辑器中显示的工具栏按钮。例如: [fullscreen, bold, italic, underline, strikeThrough, subscript, superscript, fontFamily, fontSize, |, color, emoticons, inlineStyle, paragraphStyle, |, paragraphFormat, align, formatOL, formatUL, outdent, indent, quote, insertHR, -, insertLink, insertImage, insertVideo, insertAudio, insertFile, insertTable, undo, redo, clearFormatting, selectAll, html]' ], 'tooltips' => [ 'preview_website' => '预览网站' @@ -380,6 +422,8 @@ return [ 'brand' => '品牌', 'logo' => '图标', 'logo_description' => '上传自定义图标到后台.', + 'favicon' => '浏览器favicon', + 'favicon_description' => '上传自定义后台浏览器的favicon', 'app_name' => '站点名称', 'app_name_description' => '这个名称显示在后台的标题区域.', 'app_tagline' => '站点标语', @@ -393,6 +437,7 @@ return [ 'navigation' => '导航', 'menu_mode' => '菜单样式', 'menu_mode_inline' => '行内', + 'menu_mode_inline_no_icons' => '行内(无图标)', 'menu_mode_tile' => '标题', 'menu_mode_collapsed' => '已折叠' ], @@ -410,8 +455,10 @@ return [ 'hint' => '此日志显示管理员成功登录信息。记录将保存 :days 天。', 'menu_label' => '访问日志', 'menu_description' => '查看已登陆的后台用户日志。', + 'id' => 'ID', 'created_at' => '日期 & 时间', 'login' => '登录', + 'type' => '类型', 'ip_address' => 'IP地址', 'first_name' => '名', 'last_name' => '姓', @@ -420,11 +467,13 @@ return [ 'filter' => [ 'all' => '全部', 'options_method_not_exists' => "模型 :model 必须定义方法 :method() 并为过滤器 ':filter'返回可选项.", - 'date_all' => '所有日期' + 'date_all' => '所有日期', + 'number_all' => '任意数值' ], 'import_export' => [ 'upload_csv_file' => '1. 上传一个 CSV 文件', 'import_file' => '导入文件', + 'row' => ':row 行', 'first_row_contains_titles' => '第一行包含列标题', 'first_row_contains_titles_desc' => '若 CSV 首行作为栏标题使用,请点选此项。', 'match_columns' => '2. 将文件列与数据库字段匹配', @@ -501,7 +550,7 @@ return [ 'label' => '媒体查找器', 'default_prompt' => '点击 %s 按钮查找媒体项' ], - 'media' => [ + 'media' => [ 'menu_label' => '媒体', 'upload' => '上传', 'move' => '移动', @@ -527,7 +576,12 @@ return [ 'multiple_selected' => '多选.', 'uploading_file_num' => '上传 :number 文件...', 'uploading_complete' => '上传完毕', + 'uploading_error' => '上传失败', + 'type_blocked' => '该文件类型因安全问题被禁止使用。', 'order_by' => '排序', + 'direction' => '升降序', + 'direction_asc' => '升序', + 'direction_desc' => '降序', 'folder' => '文件夹', 'no_files_found' => '未找到您所请求的文件.', 'delete_empty' => '请选择删除项.', diff --git a/modules/backend/lang/zh-tw/lang.php b/modules/backend/lang/zh-tw/lang.php index a2ef7075b..35ab3510b 100644 --- a/modules/backend/lang/zh-tw/lang.php +++ b/modules/backend/lang/zh-tw/lang.php @@ -181,8 +181,8 @@ return [ 'behavior_not_ready' => '表單還沒初始化, 確保您調用了控制器中的 initForm()', 'preview_no_files_message' => '檔案沒有上傳', 'select' => '選擇', - 'select_all' => 'all', - 'select_none' => 'none', + 'select_all' => '全選', + 'select_none' => '選擇無', 'select_placeholder' => '請選擇', 'insert_row' => '插入行', 'delete_row' => '刪除行', @@ -306,7 +306,7 @@ return [ 'label' => 'Media Finder', 'default_prompt' => 'Click the %s button to find a media item' ], - 'media' => [ + 'media' => [ 'menu_label' => '媒體', 'upload' => '上傳', 'move' => '移動', diff --git a/modules/backend/layouts/_head.htm b/modules/backend/layouts/_head.htm index 5414360d7..d4b2518b8 100644 --- a/modules/backend/layouts/_head.htm +++ b/modules/backend/layouts/_head.htm @@ -13,32 +13,62 @@ - - + + - - + + + + + + makeAssets() ?> makeLayoutPartial('custom_styles') ?> diff --git a/modules/backend/layouts/_mainmenu.htm b/modules/backend/layouts/_mainmenu.htm index 0577c2c7e..bdcae757f 100644 --- a/modules/backend/layouts/_mainmenu.htm +++ b/modules/backend/layouts/_mainmenu.htm @@ -24,7 +24,9 @@ iconSvg): ?> - + @@ -51,6 +53,7 @@ @@ -59,7 +62,9 @@ - + diff --git a/modules/cms/lang/ar/lang.php b/modules/cms/lang/ar/lang.php index 295a5218f..aca084296 100644 --- a/modules/cms/lang/ar/lang.php +++ b/modules/cms/lang/ar/lang.php @@ -27,7 +27,7 @@ return [ ], 'editor' => [ 'settings' => 'الإعدادات', - ], + ], 'asset' => [ 'menu_label' => 'الأصول', ], diff --git a/modules/cms/lang/es-ar/lang.php b/modules/cms/lang/es-ar/lang.php index ff24128f5..4c5d62fe2 100644 --- a/modules/cms/lang/es-ar/lang.php +++ b/modules/cms/lang/es-ar/lang.php @@ -154,7 +154,7 @@ return [ 'not_found' => "El componente ':name' no se encuentra.", 'method_not_found' => "El componente ':name' no contiene un método ':method'.", ], - 'template' => [ + 'template' => [ 'invalid_type' => "Tipo de plantilla Desconocido.", 'not_found' => "No se encontró la plantilla solicitada.", 'saved'=> "La plantilla se ha guardado correctamente." diff --git a/modules/cms/lang/nl/lang.php b/modules/cms/lang/nl/lang.php index ece2a7a11..af2818fce 100644 --- a/modules/cms/lang/nl/lang.php +++ b/modules/cms/lang/nl/lang.php @@ -186,6 +186,14 @@ return [ 'close_searchbox' => 'Sluit zoekveld', 'open_replacebox' => 'Open vervang veld', 'close_replacebox' => 'Sluit vervang veld', + 'commit' => 'Commit', + 'reset' => 'Reset', + 'commit_confirm' => 'Weet je zeker dat je je wijzigingen wilt opslaan op het bestandssysteem? Deze actie zal het huidige bestand overschrijven.', + 'reset_confirm' => 'Weet je zeker dat je wilt terugkeren naar de vorige versie? Deze actie zal de vorige versie terugzetten en je wijzigingen zullen verloren gaan.', + 'committing' => 'Committen', + 'resetting' => 'Resetten', + 'commit_success' => ':type is ge-commit naar het bestandssysteem', + 'reset_success' => ':type is ge-reset op het bestandssysteem', ], 'asset' => [ 'menu_label' => 'Middelen', diff --git a/modules/cms/lang/pt-br/lang.php b/modules/cms/lang/pt-br/lang.php index 2ef24ed20..d9555fe90 100644 --- a/modules/cms/lang/pt-br/lang.php +++ b/modules/cms/lang/pt-br/lang.php @@ -19,10 +19,12 @@ return [ 'online' => 'online', 'maintenance' => 'em manutenção', 'manage_themes' => 'Gerenciar temas', + 'customize_theme' => 'Customizar tema' ] ], 'theme' => [ 'not_found_name' => 'O tema ":name" não foi encontrado.', + 'by_author' => 'Por :name', 'active' => [ 'not_set' => 'O tema ativo não foi definido.', 'not_found' => 'O tema ativo não foi encontrado.', @@ -45,6 +47,8 @@ return [ 'homepage_placeholder' => 'URL do site', 'code_label' => 'Código', 'code_placeholder' => 'Um código exclusivo para esse tema a ser usado para distribuição', + 'preview_image_label' => 'Imagem de pré-visualização', + 'preview_image_placeholder' => 'O caminho da imagem de pré-visualização do tema.', 'dir_name_label' => 'Nome do diretório', 'dir_name_create_label' => 'O diretório-alvo de temas', 'theme_label' => 'Tema', @@ -95,6 +99,7 @@ return [ 'settings_menu_description' => 'Configurar modo de manutenção e a página exibida.', 'is_enabled' => 'Ativar modo de manutenção', 'is_enabled_comment' => 'Quando ativado visitantes do site vão ver a página selecionada.', + 'hint' => 'O modo de manutenção exibirá a página de manutenção para visitantes que não estão conectados à área de back-end.' ], 'page' => [ 'not_found_name' => 'A página ":name" não foi encontrada', @@ -114,6 +119,10 @@ return [ 'delete_confirm_multiple' => 'Você realmente deseja excluir as páginas selecionadas?', 'delete_confirm_single' => 'Você realmente deseja excluir esta página?', 'no_layout' => '-- sem esboço --', + 'cms_page' => 'Página do CMS', + 'title' => 'Título da página', + 'url' => 'URL da página', + 'file_name' => 'Nome do arquivo da página' ], 'layout' => [ 'not_found_name' => 'O esboço ":name" não foi encontrado', @@ -176,7 +185,15 @@ return [ 'open_searchbox' => 'Abrir caixa de busca', 'close_searchbox' => 'Fechar caixa de busca', 'open_replacebox' => 'Abrir caixa de substituição', - 'close_replacebox' => 'Fechar caixa de substituição' + 'close_replacebox' => 'Fechar caixa de substituição', + 'commit' => 'Persistir', + 'reset' => 'Redefinir', + 'commit_confirm' => 'Tem certeza de que deseja enviar suas alterações para este arquivo no sistema de arquivos? Isso sobrescreverá o arquivo existente no sistema de arquivos', + 'reset_confirm' => 'Tem certeza de que deseja redefinir este arquivo para a cópia que está no sistema de arquivos? Isso irá substituí-lo completamente com o arquivo que está no sistema de arquivos', + 'committing' => 'Persistindo', + 'resetting' => 'Redefinindo', + 'commit_success' => 'O :type foi confirmado no sistema de arquivos', + 'reset_success' => 'O :type foi redefinido para a versão do sistema de arquivos', ], 'asset' => [ 'menu_label' => 'Arquivos', @@ -218,6 +235,8 @@ return [ 'error_moving_file' => 'Erro ao mover arquivo :file', 'error_moving_directory' => 'Erro ao mover diretório :dir', 'error_deleting_directory' => 'Erro ao excluir o diretório original :dir', + 'no_list_records' => 'Nenhum arquivo encontrado', + 'delete_confirm' => 'Excluir arquivos ou diretórios selecionados?', 'path' => 'Caminho', ], 'component' => [ @@ -236,6 +255,9 @@ return [ 'invalid_type' => 'Tipo de modelo desconhecido.', 'not_found' => 'O modelo solicitado não foi encontrado.', 'saved'=> 'O modelo foi salvo com sucesso.', + 'no_list_records' => 'Nenhum registro foi encontrado', + 'delete_confirm' => 'Excluir modelos selecionados?', + 'order_by' => 'Ordenar por' ], 'permissions' => [ 'name' => 'Cms', @@ -246,4 +268,33 @@ return [ 'manage_partials' => 'Gerenciar blocos', 'manage_themes' => 'Gerenciar temas', ], + 'theme_log' => [ + 'hint' => 'Esses registros exibe as alterações feitas no tema pelos administradores na área de backend.', + 'menu_label' => 'Registros do tema', + 'menu_description' => 'Veja as alterações feitas no tema ativo.', + 'empty_link' => 'Registros do tema vazio', + 'empty_loading' => 'Esvaziando registros de tema...', + 'empty_success' => 'Registros do tema esvaziado', + 'return_link' => 'Retornar aos registros deo tema', + 'id' => 'ID', + 'id_label' => 'Registro ID', + 'created_at' => 'Data & Hora', + 'user' => 'Usuário', + 'type' => 'Tipo', + 'type_create' => 'Criado', + 'type_update' => 'Atualizado', + 'type_delete' => 'Apagado', + 'theme_name' => 'Tema', + 'theme_code' => 'Código do tema', + 'old_template' => 'Modelo (antigo)', + 'new_template' => 'Template (novo)', + 'template' => 'Modelo', + 'diff' => 'Mudanças', + 'old_value' => 'Valor antigo', + 'new_value' => 'Novo valor', + 'preview_title' => 'Mudanças no modelo', + 'template_updated' => 'Modelo foi atualizado', + 'template_created' => 'Modelo foi criado', + 'template_deleted' => 'O modelo foi apagado', + ], ]; diff --git a/modules/cms/lang/th/lang.php b/modules/cms/lang/th/lang.php new file mode 100644 index 000000000..c3c4b7123 --- /dev/null +++ b/modules/cms/lang/th/lang.php @@ -0,0 +1,296 @@ + [ + 'invalid_file' => 'ชื่อไฟล์ไม่ถูกต้อง: :name ชื่อไฟล์สามารถใส่ ตัวอักษรภาษาอังกฤษ ตัวเลข และสัญลักษณ์ _-. ตัวอย่างชื่อไฟล์ที่ถูกต้อง: page.htm, page, subdirectory/page', + 'invalid_property' => "คุณสมบัติ ':name' ไม่สามารถตั้งค่าได้", + 'file_already_exists' => "ไฟล์ ':name' มีอยู่แล้ว", + 'error_saving' => "การบันทึกไฟล์ผิดพลาด ':name' กรุณาตรวจสอบสิทธิ์การเขียนไฟล์", + 'error_creating_directory' => 'การสร้างโฟลเดอร์ผิดพลาด :name. กรุณาตรวจสอบสิทธิ์การเขียนไฟล์', + 'invalid_file_extension' => 'นามสกุลไฟล์ไม่ถูกต้อง: :invalid นามสกุลที่อนุญาตให้ใช้ได้มีดังนี้: :allowed', + 'error_deleting' => "การลบไฟล์แม่แบบผิดพลาด ':name' กรุณาตรวจสอบสิทธิ์การเขียนไฟล์", + 'delete_success' => 'ลบแม่แบบจำนวน: :count แม่แบบ', + 'file_name_required' => 'ต้องใส่ช่องชื่อไฟล์', + 'safe_mode_enabled' => 'โหมดความปลอดภัยกำลังถูกใช้งาน', + ], + 'dashboard' => [ + 'active_theme' => [ + 'widget_title_default' => 'เว็บไซต์', + 'online' => 'ออนไลน์', + 'maintenance' => 'กำลังปรับปรุง', + 'manage_themes' => 'จัดการธีม', + 'customize_theme' => 'ปรับแก้ธีม', + ], + ], + 'theme' => [ + 'not_found_name' => "ไม่พบธีม ':name'", + 'by_author' => 'โดย :name', + 'active' => [ + 'not_set' => 'ยังไม่ได้เปิดใช้งานธีม', + 'not_found' => 'ไม่พบธีมที่เปิดใช้งาน', + ], + 'edit' => [ + 'not_set' => 'ธีมที่แก้ไขไม่สามารถตั้งค่าได้', + 'not_found' => 'หาธีมที่แก้ไขไม่พบ', + 'not_match' => "สิ่งที่คุณกำลังพยายามเข้าถึงไม่อยู่ในธีมที่กำลังแก้ไขอยู่ กรุณารีโหลดหน้าเว็บ", + ], + 'settings_menu' => 'ธีมหน้าบ้าน (Theme)', + 'settings_menu_description' => 'จัดการธีมหน้าบ้าน และตัวเลือกในการปรับแก้อื่นๆ', + 'default_tab' => 'คุณสมบัติ', + 'name_label' => 'ชื่อ', + 'name_create_placeholder' => 'ชื่อธีมใหม่', + 'author_label' => 'ผู้สร้าง', + 'author_placeholder' => 'ชื่อบุคคลหรือบริษัท', + 'description_label' => 'รายละเอียด', + 'description_placeholder' => 'รายละเอียดธีม', + 'homepage_label' => 'โฮมเพจ', + 'homepage_placeholder' => 'URL ของเว็บไซต์', + 'code_label' => 'รหัส', + 'code_placeholder' => 'รหัสที่ไม่ซ้ำสำหรับเอาธีมไปแจกจ่าย', + 'preview_image_label' => 'ภาพตัวอย่าง', + 'preview_image_placeholder' => 'ชื่อไฟล์และโฟลเดอร์ภาพตัวอย่างของธีม', + 'dir_name_label' => 'ชื่อโฟลเดอร์', + 'dir_name_create_label' => 'ชื่อโฟลเดอร์ของธีม', + 'theme_label' => 'ธีม', + 'theme_title' => 'ธีม', + 'activate_button' => 'เปิดการใช้งาน', + 'active_button' => 'ใช้งานอยู่', + 'customize_theme' => 'ปรับแก้ธีม', + 'customize_button' => 'ปรับแก้', + 'duplicate_button' => 'ทำซ้ำ', + 'duplicate_title' => 'ทำซ้ำธีม', + 'duplicate_theme_success' => 'ทำซ้ำธีมแล้ว!', + 'manage_button' => 'จัดการ', + 'manage_title' => 'จัดการธีม', + 'edit_properties_title' => 'ธีม', + 'edit_properties_button' => 'แก้ไขคุณสมบัติ', + 'save_properties' => 'บันทึกคุณสมบัติ', + 'import_button' => 'อิมพอร์ต', + 'import_title' => 'อิมพอร์ตธีม', + 'import_theme_success' => 'อิมพอร์ดธีมแล้ว!', + 'import_uploaded_file' => 'ไฟล์ธีม', + 'import_overwrite_label' => 'เขียนทับไฟล์ที่มีอยู่แล้ว', + 'import_overwrite_comment' => 'เอาติ๊กออกจะอิมพอร์ทเฉพาะไฟล์ใหม่', + 'import_folders_label' => 'โฟลเดอร์', + 'import_folders_comment' => 'กรุณาเลือกโฟลเดอร์ของธีมที่คุณต้องการอิมพอร์ท', + 'export_button' => 'เอ็กซพอร์ต', + 'export_title' => 'เอ็กซพอร์ตธีม', + 'export_folders_label' => 'โฟลเดอร์', + 'export_folders_comment' => 'กรุณาเลือกโฟลเดอร์ของธีมที่ท่านต้องการเอ็กซพอร์ต', + 'delete_button' => 'ลบ', + 'delete_confirm' => 'ลบธีมนี้? ไม่สามารถย้อนกลับได้!', + 'delete_active_theme_failed' => 'ไม่สามารถลบธีมที่กำลังใช้งานอยู่, เปิดการใช้งานธีมอื่นก่อนลบ', + 'delete_theme_success' => 'ลบธีมแล้ว!', + 'create_title' => 'สร้างธีม', + 'create_button' => 'สร้าง', + 'create_new_blank_theme' => 'สร้างธีมใหม่', + 'create_theme_success' => 'สร้างธีมแล้ว!', + 'create_theme_required_name' => 'กรุณากำหนดชื่อธีม', + 'new_directory_name_label' => 'ชื่อโฟลเดอร์ธีม', + 'new_directory_name_comment' => 'กำหนดโฟลเดอร์ใหม่สำหรับธีมที่จะทำซ้ำ', + 'dir_name_invalid' => 'ชื่อโฟลเดอร์ธีม สามารถใช้ตัวเลข อักษรภาษาอังกฤษและเครื่องหมาย: _-', + 'dir_name_taken' => 'ชื่อโฟลเดอร์ธีมมีอยู่แล้ว', + 'find_more_themes' => 'หาธีมอื่นๆ', + 'saving' => 'กำลังบันทึกธีม...', + 'return' => 'กลับสู่หน้ารายการธีม', + ], + 'maintenance' => [ + 'settings_menu' => 'โหมดการบำรุงรักษา', + 'settings_menu_description' => 'ปรับแก้หน้าโหมดการบำรุงรักษา และเลือกการตั้งค่า', + 'is_enabled' => 'เปิดใช้โหมดการบำรุงรักษา', + 'is_enabled_comment' => 'เลือกหน้าที่ต้องการให้แสดงเมื่อเปิดใช้งานโหมดการบำรุงรักษา', + 'hint' => 'โหลดการบำรุงรักษาจะแสดงหน้าบำรุงรักษาสำหรับผู้เยี่ยมชมที่ไม่ได้ลงชื่อเข้าใช้หน้าเว็บหลังบ้าน', + ], + 'page' => [ + 'not_found_name' => "หาหน้าเว็บ ':name' ไม่พบ", + 'not_found' => [ + 'label' => 'ไม่พบหน้าเว็บ', + 'help' => 'หาหน้าเว็บที่ร้องขอไม่พบ', + ], + 'custom_error' => [ + 'label' => 'หน้าเว็บผิดพลาด', + 'help' => "ขอโทษครับ, มีบางอย่างผิดพลาดและไม่สามารถแสดงหน้าเว็บได้", + ], + 'menu_label' => 'หน้าเว็บ', + 'unsaved_label' => 'หน้าเว็บที่ยังไม่บันทึก', + 'no_list_records' => 'ไม่พบหน้าเว็บ', + 'new' => 'หน้าใหม่', + 'invalid_url' => 'รูปแบบ URL ไม่ถูกต้อง, URL ควรเริ่มต้นด้วยเครื่องหมาย / และสามารถมีตัวเลข, ตัวอักษรภาษาอังกฤษและเครื่องหมาย: ._-[]:?|/+*^$', + 'delete_confirm_multiple' => 'ลบหน้าที่ถูกเลือกไว้?', + 'delete_confirm_single' => 'ลบหน้านี้?', + 'no_layout' => '-- ไม่มีโครงร่าง --', + 'cms_page' => 'หน้าเว็บ CMS', + 'title' => 'หัวเรื่องหน้าเว็บ', + 'url' => 'URL หน้าเว็บ', + 'file_name' => 'ชื่อไฟล์หน้าเว็บ', + ], + 'layout' => [ + 'not_found_name' => "หาโครงร่าง ':name' ไม่พบ", + 'menu_label' => 'โครงร่าง (Layout)', + 'unsaved_label' => 'โครงร่างที่ยังไม่บันทึก', + 'no_list_records' => 'ไม่พบโครงร่าง', + 'new' => 'โครงร่างใหม่', + 'delete_confirm_multiple' => 'ลบโครงร่างที่เลือกไว้?', + 'delete_confirm_single' => 'ลบโครงร่างนี้?', + ], + 'partial' => [ + 'not_found_name' => "ไม่พบส่วนย่อย ':name'", + 'invalid_name' => 'ส่วนย่อยชื่อ: :name ไม่ถูกต้อง', + 'menu_label' => 'ส่วนย่อย', + 'unsaved_label' => 'ส่วนย่อยที่ยังไม่บันทึก', + 'no_list_records' => 'ไม่พบส่วนย่อย', + 'delete_confirm_multiple' => 'ลบส่วนย่อยที่เลือกไว้?', + 'delete_confirm_single' => 'ลบส่วนย่อยนี้?', + 'new' => 'ส่วนย่อยใหม่', + ], + 'content' => [ + 'not_found_name' => "ไม่พบไฟล์เนื้อหา ':name'", + 'menu_label' => 'เนื้อหา', + 'unsaved_label' => 'เนื้อหาที่ยังไม่ได้บันทึก', + 'no_list_records' => 'หาไฟล์เนื้อหาไม่พบ', + 'delete_confirm_multiple' => 'ลบไฟล์เนื้อหาหรือโฟลเดอร์ที่เลือกไว้?', + 'delete_confirm_single' => 'ลบไฟล์เนื้อหานี้?', + 'new' => 'ไฟล์เนื้อหาใหม่', + ], + 'ajax_handler' => [ + 'invalid_name' => 'ชื่อผู้จัดการ AJAX: :name ไม่ถูกต้อง', + 'not_found' => "หาผู้จัดการ AJAX ':name' ไม่พบ", + ], + 'cms' => [ + 'menu_label' => 'CMS', + ], + 'sidebar' => [ + 'add' => 'เพิ่ม', + 'search' => 'ค้นหา...', + ], + 'editor' => [ + 'settings' => 'การตั้งค่า', + 'title' => 'หัวเรื่อง', + 'new_title' => 'หัวเรื่องหน้าใหม่', + 'url' => 'URL', + 'filename' => 'ชื่อไฟล์', + 'layout' => 'โครงร่าง', + 'description' => 'รายละเอียด', + 'preview' => 'ดูตัวอย่าง', + 'meta' => 'ข้อมูลย่อย', + 'meta_title' => 'หัวเรื่อง', + 'meta_description' => 'รายละเอียด', + 'markup' => 'Markup', + 'code' => 'Code', + 'content' => 'เนื้อหา', + 'hidden' => 'ซ่อน', + 'hidden_comment' => 'เพจที่ถูกซ่อนสามารถเข้าถึงได้โดยผู้ใช้หลังบ้านที่ล็อกอินเท่านั้น', + 'enter_fullscreen' => 'เข้าสู่โหมดเต็มหน้าจอ', + 'exit_fullscreen' => 'ออกโหมดเต็มหน้าจอ', + 'open_searchbox' => 'เปิดกล่องค้นหา', + 'close_searchbox' => 'ปิดกล่องค้นหา', + 'open_replacebox' => 'เปลี่ยนกล่องแทนที่', + 'close_replacebox' => 'ปิดกล่องแทนที่', + 'reset' => 'ตั้งค่าเริ่มต้น', + 'reset_confirm' => 'คุณแน่ใจว่าต้องการตั้งค่าเริ่มต้นไฟล์นี้ตามข้อมูลที่อยู่ในระบบไฟล์? ไฟล์นี้จะถูกเขียนทับด้วยไฟล์ที่อยู่ในระบบไฟล์', + 'resetting' => 'กำลังตั้งค่าเริ่มต้น', + ], + 'asset' => [ + 'menu_label' => 'สินทรัพย์', + 'unsaved_label' => 'สินทรัพย์ที่ยังไม่ได้บันทึก', + 'drop_down_add_title' => 'เพิ่ม...', + 'drop_down_operation_title' => 'การทำงาน...', + 'upload_files' => 'อัพโหลดไฟล์', + 'create_file' => 'สร้างไฟล์', + 'create_directory' => 'สร้างโฟลเดอร์', + 'directory_popup_title' => 'โฟลเดอร์ใหม่', + 'directory_name' => 'ชื่อโฟลเดอร์', + 'rename' => 'เปลี่ยนชื่อ', + 'delete' => 'ลบ', + 'move' => 'ย้าย', + 'select' => 'เลือก', + 'new' => 'ไฟล์ใหม่', + 'rename_popup_title' => 'เปลี่ยนชื่อ', + 'rename_new_name' => 'ชื่อใหม่', + 'invalid_path' => 'path สามารถใส่ได้เฉพาะ ตัวเลข ตัวอักษรภาษาอังกฤษ ช่องว่าง และสัญลักษณ์ต่อไปนี้: ._-/', + 'error_deleting_file' => 'การลบไฟล์ :name ผิดพลาด', + 'error_deleting_dir_not_empty' => 'การลบโฟลเดอร์ผิดพลาด :name โฟลเดอร์นี้ไม่ว่างเปล่า', + 'error_deleting_dir' => 'การลบโฟลเดอร์ :name ผิดพลาด', + 'invalid_name' => 'ชื่อ สามารถใส่ได้เฉพาะ ตัวเลข ตัวอักษรภาษาอังกฤษ ช่องว่าง และสัญลักษณ์ต่อไปนี้: ._-', + 'original_not_found' => 'หาไฟล์หรือโฟลเดอร์ต้นทางไม่พบ', + 'already_exists' => 'ชื่อไฟล์หรือโฟลเดอร์นี้มีอยู่แล้ว', + 'error_renaming' => 'การเปลี่ยนชื่อไฟล์หรือโฟลเดอร์ผิดพลาด', + 'name_cant_be_empty' => 'ให้ชื่อว่างเปล่าไม่ได้', + 'too_large' => 'ไฟล์ที่ต้องการอัพโหลดใหญ่เกินไป ขนาดไฟล์สูงสุดที่อนุญาตคือ :max_size', + 'type_not_allowed' => 'อนุญาตเฉพาะประเภทไฟล์ต่อไปนี้: :allowed_types', + 'file_not_valid' => 'ไฟล์ไม่ถูกต้อง', + 'error_uploading_file' => "อัพโหลดไฟล์ ':name' ผิดพลาด: :error", + 'move_please_select' => 'กรุณาเลือก', + 'move_destination' => 'โฟลเดอร์ปลายทาง', + 'move_popup_title' => 'ย้ายสินทรัพย์', + 'move_button' => 'ย้าย', + 'selected_files_not_found' => 'ไม่พบไฟล์ที่เลือกไว้', + 'select_destination_dir' => 'กรุณาเลือกโฟลเดอร์ปลายทาง', + 'destination_not_found' => 'ไม่พบโฟลเดอร์ปลายทาง', + 'error_moving_file' => 'การย้ายไฟล์ :file ผิดพลาด', + 'error_moving_directory' => 'การย้ายโฟลเดอร์ :dir ผิดพลาด', + 'error_deleting_directory' => 'การลบโฟลเดอร์ต้นทาง :dir ผิดพลาด', + 'no_list_records' => 'ไม่พบไฟล์', + 'delete_confirm' => 'ลบไฟล์หรือโฟลเดอร์ที่เลือกไว้?', + 'path' => 'Path', + ], + 'component' => [ + 'menu_label' => 'ส่วนประกอบ', + 'unnamed' => 'ไม่มีชื่อ', + 'no_description' => 'ไม่มีรายละเอียดมาด้วย', + 'alias' => 'นามแฝง', + 'alias_description' => 'ชื่อที่ไม่ซ้ำที่กำหนดให้กับส่วนประกอบนี้สำหรับใช้ในโค้ดหน้าเว็บหรือโครงร่าง', + 'validation_message' => 'ต้องใส่นามแฝงส่วนประกอบ และสามารถใส่ได้เฉพาะ ตัวอักษรภาษาอังกฤษ ตัวเลข และเครื่องหมาย _ นามแฝงควรเริ่มต้นด้วยตัวอักษรภาษาอังกฤษ', + 'invalid_request' => 'แม่แบบไม่สามารถถูกบันทึกเนื่องจากข้อมูลส่วนประกอบไม่ถูกต้อง', + 'no_records' => 'ไม่พบส่วนประกอบ', + 'not_found' => "หาส่วนประกอบ ':name' ไม่พบ", + 'method_not_found' => "ส่วนประกอบ ':name' ไม่มี method ชื่อ ':method'.", + ], + 'template' => [ + 'invalid_type' => 'ไม่รู้จักประเภทแม่แบบ', + 'not_found' => 'ไม่พบแม่แบบ', + 'saved' => 'บันทึกแม่แบบแล้ว', + 'no_list_records' => 'ไม่พบข้อมูล', + 'delete_confirm' => 'ลบแม่แบบที่เลือกไว้?', + 'order_by' => 'เรียงลำดับโดย', + ], + 'permissions' => [ + 'name' => 'CMS', + 'manage_content' => 'จัดการไฟล์เนื้อหาเว็บไซต์', + 'manage_assets' => 'จัดการสินทรัพย์ของเว็บไซต์ - รูปภาพ, ไฟล์ JavaScript, ไฟล์ CSS', + 'manage_pages' => 'สร้าง, แก้ไข และลบ หน้าเว็บไซต์', + 'manage_layouts' => 'สร้าง, แก้ไข และลบ โครงร่าง', + 'manage_partials' => 'สร้าง, แก้ไข และลบ ส่วนย่อย', + 'manage_themes' => 'เปิด, ปิดการใช้งาน และปรับแต่งค่า ธีม', + 'manage_theme_options' => 'ปรับแต่งค่าตัวเลือกต่างๆ สำหรับธีมที่ใช้งานอยู่', + ], + 'theme_log' => [ + 'hint' => 'บันทึกนี้แสดงการเปลี่ยนแปลงที่เกี่ยวกับธีมโดยผู้ดูแลระบบในส่วนหน้าเว็บหลังบ้าน', + 'menu_label' => 'บันทึกธีม', + 'menu_description' => 'ดูการเปลี่ยนแปลงของธีมที่กำลังใช้งานอยู่', + 'empty_link' => 'ลบบันทึกธีมทั้งหมด', + 'empty_loading' => 'กำลังลบบันทึกธีม...', + 'empty_success' => 'ลบบันทึกธีมเรียบร้อยแล้ว', + 'return_link' => 'กลับสู่หน้าบันทึกธีม', + 'id' => 'ไอดี', + 'id_label' => 'ไอดีบันทึก', + 'created_at' => 'วันเวลา', + 'user' => 'ผู้ใช้', + 'type' => 'ประเภท', + 'type_create' => 'สร้าง', + 'type_update' => 'อัพเดท', + 'type_delete' => 'ลบ', + 'theme_name' => 'ชื่อธีม', + 'theme_code' => 'รหัสธีม', + 'old_template' => 'แม่แบบเก่า', + 'new_template' => 'แม่แบบใหม่', + 'template' => 'แม่แบบ (Template)', + 'diff' => 'การเปลี่ยนแปลง', + 'old_value' => 'ค่าเก่า', + 'new_value' => 'ค่าใหม่', + 'preview_title' => 'Template changes', + 'template_updated' => 'อัพเดทแม่แบบแล้ว', + 'template_created' => 'สร้างแม่แบบแล้ว', + 'template_deleted' => 'ลบแม่แบบแล้ว', + ], +]; diff --git a/modules/cms/lang/zh-cn/lang.php b/modules/cms/lang/zh-cn/lang.php index 1124b483a..074fbe3d0 100644 --- a/modules/cms/lang/zh-cn/lang.php +++ b/modules/cms/lang/zh-cn/lang.php @@ -15,12 +15,16 @@ return [ ], 'dashboard' => [ 'active_theme' => [ + 'widget_title_default' => 'Website', 'online' => '在线', 'maintenance' => '维护', + 'manage_themes' => '管理主题', + 'customize_theme' => '自定义主题', ] ], 'theme' => [ 'not_found_name' => "未找到主题 ':name'", + 'by_author' => '作者 :name', 'active' => [ 'not_set' => '未设置活动主题', 'not_found' => '无法找到活动主题' @@ -43,6 +47,8 @@ return [ 'homepage_placeholder' => '网站地址', 'code_label' => '代码', 'code_placeholder' => '主题发行唯一码', + 'preview_image_label' => '预览图', + 'preview_image_placeholder' => '预览图路径.', 'dir_name_label' => '目录名', 'dir_name_create_label' => '目标主题目录', 'theme_label' => '主题', @@ -112,7 +118,11 @@ return [ 'invalid_url' => '不合法的URL格式. URL可以以正斜杠开头, 包含数字, 拉丁字母和下面的字符: ._-[]:?|/+*^$', 'delete_confirm_multiple' => '真的想要删除选择的页面吗?', 'delete_confirm_single' => '真的想要删除这个页面吗?', - 'no_layout' => '-- 无布局 --' + 'no_layout' => '-- 无布局 --', + 'cms_page' => 'CMS 页面', + 'title' => '页面标题', + 'url' => '页面URL', + 'file_name' => '页面文件名' ], 'layout' => [ 'not_found_name' => "布局 ':name' 找不到", @@ -171,7 +181,19 @@ return [ 'hidden' => '隐藏', 'hidden_comment' => '隐藏页面只能被已登录的后台用户访问.', 'enter_fullscreen' => '进入全屏模式', - 'exit_fullscreen' => '退出全屏模式' + 'exit_fullscreen' => '退出全屏模式', + 'open_searchbox' => '打开搜索框', + 'close_searchbox' => '关闭搜索框', + 'open_replacebox' => '打开替换框', + 'close_replacebox' => '关闭替换框', + 'commit' => '提交', + 'reset' => '重置', + 'commit_confirm' => '您是否确认保存对文件的修改?这将会对原有的文件内容进行覆盖', + 'reset_confirm' => '您是否确认重置对文件的修改?这将会完全恢复文件到原来的内容', + 'committing' => '提交中...', + 'resetting' => '重置中...', + 'commit_success' => ' :type 保存成功', + 'reset_success' => ' :type 重置成功', ], 'asset' => [ 'menu_label' => '资源', @@ -213,6 +235,8 @@ return [ 'error_moving_file' => '移动文件 :file 错误', 'error_moving_directory' => '移动目录 :dir 错误', 'error_deleting_directory' => '删除原始目录 :dir 错误', + 'no_list_records' => '资源文件为空', + 'delete_confirm' => '确定删除选中的文件或文件夹?', 'path' => '路径' ], 'component' => [ @@ -230,7 +254,10 @@ return [ 'template' => [ 'invalid_type' => '未知模板类型.', 'not_found' => '无法找到所请求的模板', - 'saved'=> '模板保存成功.' + 'saved'=> '模板保存成功.', + 'no_list_records' => '模板文件为空', + 'delete_confirm' => '确认删除选中的模板?', + 'order_by' => '排序方式' ], 'permissions' => [ 'name' => 'CMS', @@ -240,5 +267,35 @@ return [ 'manage_layouts' => '管理布局', 'manage_partials' => '管理部件', 'manage_themes' => '管理主题', + 'manage_theme_options' => '管理主题的自定义选项', + ], + 'theme_log' => [ + 'hint' => '显示管理员在后台对主题的所有操作日志', + 'menu_label' => '主题操作日志', + 'menu_description' => '查看对激活主题的操作日志.', + 'empty_link' => '清空操作日志', + 'empty_loading' => '清空主题操作日志中...', + 'empty_success' => '主题操作日志清空成功', + 'return_link' => '返回主题操作日志', + 'id' => '序号', + 'id_label' => '日志 序号', + 'created_at' => '日志生成时间', + 'user' => '用户名', + 'type' => '操作类型', + 'type_create' => '创建', + 'type_update' => '更新', + 'type_delete' => '删除', + 'theme_name' => '主题名', + 'theme_code' => '主题code', + 'old_template' => '文件名 (Old)', + 'new_template' => '文件名 (New)', + 'template' => '文件名', + 'diff' => '文件修改对比', + 'old_value' => '文件修改前', + 'new_value' => '文件修改后', + 'preview_title' => '文件修改详情', + 'template_updated' => '文件已更新', + 'template_created' => '文件已创建', + 'template_deleted' => '文件已删除', ], ]; diff --git a/modules/cms/models/ThemeData.php b/modules/cms/models/ThemeData.php index e06f1a859..49b7e4c01 100644 --- a/modules/cms/models/ThemeData.php +++ b/modules/cms/models/ThemeData.php @@ -69,7 +69,7 @@ class ThemeData extends Model } /** - * Clear asset cache after saving to ensure `assetVar` form fields take + * Clear asset cache after saving to ensure `assetVar` form fields take * immediate effect. */ public function afterSave() @@ -77,7 +77,8 @@ class ThemeData extends Model try { CombineAssets::resetCache(); } - catch (Exception $ex) {} + catch (Exception $ex) { + } } /** @@ -151,7 +152,6 @@ class ThemeData extends Model */ public function initFormFields() { - } /** @@ -227,7 +227,7 @@ class ThemeData extends Model { $theme = CmsTheme::getActiveTheme(); - if (!$theme){ + if (!$theme) { return; } diff --git a/modules/cms/models/ThemeExport.php b/modules/cms/models/ThemeExport.php index c568a7736..72b947557 100644 --- a/modules/cms/models/ThemeExport.php +++ b/modules/cms/models/ThemeExport.php @@ -54,6 +54,16 @@ class ThemeExport extends Model ] ]; + /** + * Import / Export model classes are helpers and are not to write to the database + * + * @return void + */ + public function save(array $options = null, $sessionKey = null) + { + throw new ApplicationException(sprintf("The % model is not intended to be saved, please use %s instead", get_class($this), 'ThemeData')); + } + public function getFoldersOptions() { return [ @@ -110,7 +120,6 @@ class ThemeExport extends Model File::deleteDirectory($tempPath); } catch (Exception $ex) { - if (strlen($tempPath) && File::isDirectory($tempPath)) { File::deleteDirectory($tempPath); } diff --git a/modules/cms/models/ThemeImport.php b/modules/cms/models/ThemeImport.php index d9e6f9274..12bb509ba 100644 --- a/modules/cms/models/ThemeImport.php +++ b/modules/cms/models/ThemeImport.php @@ -59,6 +59,16 @@ class ThemeImport extends Model ] ]; + /** + * Import / Export model classes are helpers and are not to write to the database + * + * @return void + */ + public function save(array $options = null, $sessionKey = null) + { + throw new ApplicationException(sprintf("The % model is not intended to be saved, please use %s instead", get_class($this), 'ThemeData')); + } + public function getFoldersOptions() { return [ @@ -88,8 +98,7 @@ class ThemeImport extends Model $this->theme = $theme; $this->fill($data); - try - { + try { $file = $this->uploaded_file()->withDeferred($sessionKey)->first(); if (!$file) { throw new ApplicationException('There is no file attached to import!'); @@ -125,7 +134,6 @@ class ThemeImport extends Model $file->delete(); } catch (Exception $ex) { - if (!empty($tempPath) && File::isDirectory($tempPath)) { File::deleteDirectory($tempPath); } diff --git a/modules/cms/models/ThemeLog.php b/modules/cms/models/ThemeLog.php index 343f2fe05..6105e3150 100644 --- a/modules/cms/models/ThemeLog.php +++ b/modules/cms/models/ThemeLog.php @@ -94,7 +94,8 @@ class ThemeLog extends Model try { $record->save(); } - catch (Exception $ex) {} + catch (Exception $ex) { + } return $record; } diff --git a/modules/cms/models/themelog/columns.yaml b/modules/cms/models/themelog/columns.yaml index 966b5a0f8..26a4e2419 100644 --- a/modules/cms/models/themelog/columns.yaml +++ b/modules/cms/models/themelog/columns.yaml @@ -21,6 +21,8 @@ columns: any_template: label: cms::lang.theme_log.template + searchable: false + sortable: false template: label: cms::lang.theme_log.new_template diff --git a/modules/cms/twig/DebugExtension.php b/modules/cms/twig/DebugExtension.php index ee151b13c..242eb23a5 100644 --- a/modules/cms/twig/DebugExtension.php +++ b/modules/cms/twig/DebugExtension.php @@ -93,7 +93,6 @@ class DebugExtension extends TwigExtension $count = func_num_args(); if ($count == 2) { - $this->variablePrefix = true; $vars = []; foreach ($context as $key => $value) { @@ -103,13 +102,10 @@ class DebugExtension extends TwigExtension } $result .= $this->dump($vars, static::PAGE_CAPTION); - } else { - $this->variablePrefix = false; for ($i = 2; $i < $count; $i++) { - $var = func_get_arg($i); if ($var instanceof ComponentBase) { @@ -127,9 +123,7 @@ class DebugExtension extends TwigExtension $result .= $this->dump($var, $caption); } - } - return $result; } @@ -164,9 +158,11 @@ class DebugExtension extends TwigExtension $output[] = $this->makeTableHeader($caption); } + $output[] = ''; foreach ($variables as $key => $item) { $output[] = $this->makeTableRow($key, $item); } + $output[] = ''; $output[] = ''; $html = implode(PHP_EOL, $output); @@ -186,16 +182,18 @@ class DebugExtension extends TwigExtension } $output = []; + $output[] = ''; $output[] = ''; - $output[] = ''; + $output[] = ''; $output[] = $caption; if (isset($subcaption)) { $output[] = '
'.$subcaption.'
'; } - $output[] = ''; + $output[] = ''; $output[] = ''; + $output[] = ''; return implode(PHP_EOL, $output); } diff --git a/modules/cms/twig/Extension.php b/modules/cms/twig/Extension.php index ed07dfb95..8dfc67de5 100644 --- a/modules/cms/twig/Extension.php +++ b/modules/cms/twig/Extension.php @@ -193,8 +193,9 @@ class Extension extends TwigExtension return $default; } - if ($event = Event::fire('cms.block.render', [$name, $result], true)) + if ($event = Event::fire('cms.block.render', [$name, $result], true)) { $result = $event; + } $result = str_replace('', trim($default), $result); return $result; diff --git a/modules/cms/twig/FlashNode.php b/modules/cms/twig/FlashNode.php index cf0f4ac7c..462a5382f 100644 --- a/modules/cms/twig/FlashNode.php +++ b/modules/cms/twig/FlashNode.php @@ -31,7 +31,7 @@ class FlashNode extends TwigNode ; if ($attrib == 'all') { - $compiler + $compiler ->addDebugInfo($this) ->write('foreach (Flash::getMessages() as $type => $messages) {'.PHP_EOL) ->indent() diff --git a/modules/cms/twig/FrameworkNode.php b/modules/cms/twig/FrameworkNode.php index e1af81a92..9aa2aa04b 100644 --- a/modules/cms/twig/FrameworkNode.php +++ b/modules/cms/twig/FrameworkNode.php @@ -25,7 +25,7 @@ class FrameworkNode extends TwigNode public function compile(TwigCompiler $compiler) { $attrib = $this->getAttribute('name'); - $includeExtras = strtolower(trim($attrib)) == 'extras'; + $includeExtras = strtolower(trim($attrib)) === 'extras'; $compiler ->addDebugInfo($this) @@ -35,26 +35,20 @@ class FrameworkNode extends TwigNode $compiler ->write("if (\$_minify) {" . PHP_EOL) ->indent() - ->write("echo ''.PHP_EOL;" . PHP_EOL) + ->write("echo ''.PHP_EOL;" . PHP_EOL) ->outdent() ->write("}" . PHP_EOL) ->write("else {" . PHP_EOL) ->indent() - ->write("echo ''.PHP_EOL;" . PHP_EOL) - ->write("echo ''.PHP_EOL;" . PHP_EOL) + ->write("echo ''.PHP_EOL;" . PHP_EOL) + ->write("echo ''.PHP_EOL;" . PHP_EOL) ->outdent() ->write("}" . PHP_EOL) - ->write("echo ''.PHP_EOL;" . PHP_EOL) + ->write("echo ''.PHP_EOL;" . PHP_EOL) ; } else { - $compiler->write("echo ''.PHP_EOL;" . PHP_EOL) - ; + $compiler->write("echo ''.PHP_EOL;" . PHP_EOL); } $compiler->write('unset($_minify);' . PHP_EOL); diff --git a/modules/cms/twig/Loader.php b/modules/cms/twig/Loader.php index 88670f0e8..d3fe3475b 100644 --- a/modules/cms/twig/Loader.php +++ b/modules/cms/twig/Loader.php @@ -1,7 +1,7 @@ getId('component-list') => $this->makePartial('items', [ - 'items' => $this->getData()] - )]; + return [ + '#' . $this->getId('component-list') => $this->makePartial('items', [ + 'items' => $this->getData() + ]) + ]; } protected function itemMatchesSearch(&$words, $item) diff --git a/modules/cms/widgets/assetlist/assets/css/assetlist.css b/modules/cms/widgets/assetlist/assets/css/assetlist.css index 879091007..c748bb323 100644 --- a/modules/cms/widgets/assetlist/assets/css/assetlist.css +++ b/modules/cms/widgets/assetlist/assets/css/assetlist.css @@ -87,7 +87,6 @@ font-style: normal; text-decoration: inherit; -webkit-font-smoothing: antialiased; - *margin-right: .3em; content: "\f07b"; color: #a1aab1; font-size: 14px; @@ -124,7 +123,6 @@ font-style: normal; text-decoration: inherit; -webkit-font-smoothing: antialiased; - *margin-right: .3em; content: "\f053"; } .control-assetlist p.parent a.link:hover { diff --git a/modules/cms/widgets/templatelist/partials/_items.htm b/modules/cms/widgets/templatelist/partials/_items.htm index c8b5711e3..60f5d90a6 100644 --- a/modules/cms/widgets/templatelist/partials/_items.htm +++ b/modules/cms/widgets/templatelist/partials/_items.htm @@ -36,7 +36,7 @@
- fileName) ?> + itemType . '/' . $item->fileName) ?> $config) { + if ($config['driver'] === 'local' && ends_with($config['root'], '/storage/app') && empty($config['url'])) { + Config::set("filesystems.disks.$key.url", '/storage/app'); + } + } + Paginator::defaultSimpleView('system::pagination.simple-default'); /* @@ -146,7 +153,7 @@ class ServiceProvider extends ModuleServiceProvider /* * CLI */ - if (App::runningInConsole() && count(array_intersect($commands, Request::server('argv'))) > 0) { + if (App::runningInConsole() && count(array_intersect($commands, Request::server('argv', []))) > 0) { PluginManager::$noInit = true; } } @@ -536,7 +543,7 @@ class ServiceProvider extends ModuleServiceProvider */ protected function registerValidator() { - $this->app->resolving('validator', function($validator) { + $this->app->resolving('validator', function ($validator) { /* * Allowed file extensions, as opposed to mime types. * - extensions: png,jpg,txt diff --git a/modules/system/assets/css/styles.css b/modules/system/assets/css/styles.css index 9abb752c2..f65f3bc8f 100644 --- a/modules/system/assets/css/styles.css +++ b/modules/system/assets/css/styles.css @@ -70,7 +70,7 @@ fieldset {border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em} legend {border:0;padding:0} textarea {overflow:auto} optgroup {font-weight:bold} -table {border-collapse:collapse;border-spacing:0;table-layout:auto;word-wrap:break-word;word-break:break-all} +table {border-collapse:collapse;border-spacing:0;table-layout:auto} td, th {padding:0} *, @@ -1937,7 +1937,7 @@ address {margin-bottom:20px;font-style:normal;line-height:1.42857143} button.close {padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none} @font-face {font-family:'FontAwesome';src:url('../ui/font/fontawesome-webfont.eot?v=1.0.1');src:url('../ui/font/fontawesome-webfont.eot?#iefix&v=1.0.1') format('embedded-opentype'),url('../ui/font/fontawesome-webfont.woff?v=1.0.1') format('woff'),url('../ui/font/fontawesome-webfont.ttf?v=1.0.1') format('truetype'),url('../ui/font/fontawesome-webfont.svg#fontawesomeregular?v=1.0.1') format('svg');font-weight:normal;font-style:normal} [class^="icon-"], -[class*=" icon-"] {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0% 0%;background-repeat:repeat;margin-top:0} +[class*=" icon-"] {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;display:inline;width:auto;height:auto;line-height:normal;vertical-align:baseline;background-image:none;background-position:0% 0%;background-repeat:repeat;margin-top:0} [class^="icon-"]:before, [class*=" icon-"]:before {text-decoration:inherit;display:inline-block;speak:none} [class^="icon-"].pull-left, @@ -1945,7 +1945,7 @@ button.close {padding:0;cursor:pointer;background:transparent;border:0;-webkit-a [class^="icon-"].pull-right, [class*=" icon-"].pull-right {margin-left:.3em} [class^="oc-icon-"]:before, -[class*=" oc-icon-"]:before {display:inline-block;margin-right:8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;vertical-align:baseline} +[class*=" oc-icon-"]:before {display:inline-block;margin-right:8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;vertical-align:baseline} [class^="oc-icon-"].empty:before, [class*=" oc-icon-"].empty:before {margin-right:0} .icon-lg {font-size:1.33333333em;line-height:0.75em;vertical-align:-15%} diff --git a/modules/system/assets/js/framework-min.js b/modules/system/assets/js/framework-min.js index 98eff1505..acc69b4c4 100644 --- a/modules/system/assets/js/framework-min.js +++ b/modules/system/assets/js/framework-min.js @@ -14,17 +14,19 @@ useFiles=false} if($.type(loading)=='string'){loading=$(loading)} var requestHeaders={'X-OCTOBER-REQUEST-HANDLER':handler,'X-OCTOBER-REQUEST-PARTIALS':this.extractPartials(options.update)} if(useFlash){requestHeaders['X-OCTOBER-REQUEST-FLASH']=1} +var csrfToken=getXSRFToken() +if(csrfToken){requestHeaders['X-XSRF-TOKEN']=csrfToken} var requestData,inputName,data={} $.each($el.parents('[data-request-data]').toArray().reverse(),function extendRequest(){$.extend(data,paramToObj('data-request-data',$(this).data('request-data')))}) if($el.is(':input')&&!$form.length){inputName=$el.attr('name') if(inputName!==undefined&&options.data[inputName]===undefined){options.data[inputName]=$el.val()}} if(options.data!==undefined&&!$.isEmptyObject(options.data)){$.extend(data,options.data)} -if(useFiles){requestData=new FormData($form.length?$form.get(0):null) +if(useFiles){requestData=new FormData($form.length?$form.get(0):undefined) if($el.is(':file')&&inputName){$.each($el.prop('files'),function(){requestData.append(inputName,this)}) delete data[inputName]} $.each(data,function(key){requestData.append(key,this)})} else{requestData=[$form.serialize(),$.param(data)].filter(Boolean).join('&')} -var requestOptions={url:url,crossDomain:false,context:context,headers:requestHeaders,success:function(data,textStatus,jqXHR){if(this.options.beforeUpdate.apply(this,[data,textStatus,jqXHR])===false)return +var requestOptions={url:url,crossDomain:false,global:options.ajaxGlobal,context:context,headers:requestHeaders,success:function(data,textStatus,jqXHR){if(this.options.beforeUpdate.apply(this,[data,textStatus,jqXHR])===false)return if(options.evalBeforeUpdate&&eval('(function($el, context, data, textStatus, jqXHR) {'+options.evalBeforeUpdate+'}.call($el.get(0), $el, context, data, textStatus, jqXHR))')===false)return var _event=jQuery.Event('ajaxBeforeUpdate') $triggerEl.trigger(_event,[context,data,textStatus,jqXHR]) @@ -66,7 +68,7 @@ var fieldElement=$form.find('[name="'+fieldName+'"], [name="'+fieldName+'[]"], [ if(fieldElement.length>0){var _event=jQuery.Event('ajaxInvalidField') $(window).trigger(_event,[fieldElement.get(0),fieldName,fieldMessages,isFirstInvalidField]) if(isFirstInvalidField){if(!_event.isDefaultPrevented())fieldElement.focus() -isFirstInvalidField=false}}})},handleFlashMessage:function(message,type){},handleRedirectResponse:function(url){window.location.href=url},handleUpdateResponse:function(data,textStatus,jqXHR){var updatePromise=$.Deferred().done(function(){for(var partial in data){var selector=(options.update[partial])?options.update[partial]:partial +isFirstInvalidField=false}}})},handleFlashMessage:function(message,type){},handleRedirectResponse:function(url){window.location.assign(url)},handleUpdateResponse:function(data,textStatus,jqXHR){var updatePromise=$.Deferred().done(function(){for(var partial in data){var selector=(options.update[partial])?options.update[partial]:partial if($.type(selector)=='string'&&selector.charAt(0)=='@'){$(selector.substring(1)).append(data[partial]).trigger('ajaxUpdate',[context,data,textStatus,jqXHR])} else if($.type(selector)=='string'&&selector.charAt(0)=='^'){$(selector.substring(1)).prepend(data[partial]).trigger('ajaxUpdate',[context,data,textStatus,jqXHR])} else{$(selector).trigger('ajaxBeforeReplace') @@ -92,7 +94,7 @@ $el.trigger('ajaxPromise',[context]) return $.ajax(requestOptions).fail(function(jqXHR,textStatus,errorThrown){if(!isRedirect){$el.trigger('ajaxFail',[context,textStatus,jqXHR])} if(loading)loading.hide()}).done(function(data,textStatus,jqXHR){if(!isRedirect){$el.trigger('ajaxDone',[context,data,textStatus,jqXHR])} if(loading)loading.hide()}).always(function(dataOrXhr,textStatus,xhrOrError){$el.trigger('ajaxAlways',[context,dataOrXhr,textStatus,xhrOrError])})} -Request.DEFAULTS={update:{},type:'POST',beforeUpdate:function(data,textStatus,jqXHR){},evalBeforeUpdate:null,evalSuccess:null,evalError:null,evalComplete:null} +Request.DEFAULTS={update:{},type:'POST',beforeUpdate:function(data,textStatus,jqXHR){},evalBeforeUpdate:null,evalSuccess:null,evalError:null,evalComplete:null,ajaxGlobal:false} Request.prototype.extractPartials=function(update){var result=[] for(var partial in update) result.push(partial) @@ -100,7 +102,7 @@ return result.join('&')} var old=$.fn.request $.fn.request=function(handler,option){var args=arguments var $this=$(this).first() -var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))} +var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),ajaxGlobal:$this.data('request-ajax-global'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))} if(!handler)handler=$this.data('request') var options=$.extend(true,{},Request.DEFAULTS,data,typeof option=='object'&&option) return new Request($this,handler,options)} @@ -110,14 +112,20 @@ $.fn.request.noConflict=function(){$.fn.request=old return this} function paramToObj(name,value){if(value===undefined)value='' if(typeof value=='object')return value -try{return JSON.parse(JSON.stringify(eval("({"+value+"})")))} +try{return ocJSON("{"+value+"}")} catch(e){throw new Error('Error parsing the '+name+' attribute value. '+e)}} +function getXSRFToken(){var cookieValue=null +if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';') +for(var i=0;i="0"&&str[pos]<="9")){var body="";for(var i=pos;i="0"&&str[i]<="9")){body+=str[i];}else{return{originLength:body.length,body:body};}} +throw new Error("Broken JSON number body near "+body);} +if(str[pos]==="{"||str[pos]==="["){var stack=[str[pos]];var body=str[pos];for(var i=pos+1;i=0)?pos-5:0,50));} +function canBeKeyHead(ch){if(ch[0]==="\\")return false;if((ch[0]>='a'&&ch[0]<='z')||(ch[0]>='A'&&ch[0]<='Z')||ch[0]==='_')return true;if(ch[0]>='0'&&ch[0]<='9')return true;if(ch[0]==='$')return true;if(ch.charCodeAt(0)>255)return true;return false;} +function isBlankChar(ch){return ch===" "||ch==="\n"||ch==="\t";} +function parse(str){str=str.trim();if(!str.length)throw new Error("Broken JSON object.");var result="";while(str&&str[0]===","){str=str.substr(1);} +if(str[0]==="\""||str[0]==="'"){if(str[str.length-1]!==str[0]){throw new Error("Invalid string JSON object.");} +var body="\"";for(var i=1;i0){var _event=jQuery.Event('ajaxInvalidField') $(window).trigger(_event,[fieldElement.get(0),fieldName,fieldMessages,isFirstInvalidField]) if(isFirstInvalidField){if(!_event.isDefaultPrevented())fieldElement.focus() -isFirstInvalidField=false}}})},handleFlashMessage:function(message,type){},handleRedirectResponse:function(url){window.location.href=url},handleUpdateResponse:function(data,textStatus,jqXHR){var updatePromise=$.Deferred().done(function(){for(var partial in data){var selector=(options.update[partial])?options.update[partial]:partial +isFirstInvalidField=false}}})},handleFlashMessage:function(message,type){},handleRedirectResponse:function(url){window.location.assign(url)},handleUpdateResponse:function(data,textStatus,jqXHR){var updatePromise=$.Deferred().done(function(){for(var partial in data){var selector=(options.update[partial])?options.update[partial]:partial if($.type(selector)=='string'&&selector.charAt(0)=='@'){$(selector.substring(1)).append(data[partial]).trigger('ajaxUpdate',[context,data,textStatus,jqXHR])} else if($.type(selector)=='string'&&selector.charAt(0)=='^'){$(selector.substring(1)).prepend(data[partial]).trigger('ajaxUpdate',[context,data,textStatus,jqXHR])} else{$(selector).trigger('ajaxBeforeReplace') @@ -92,7 +94,7 @@ $el.trigger('ajaxPromise',[context]) return $.ajax(requestOptions).fail(function(jqXHR,textStatus,errorThrown){if(!isRedirect){$el.trigger('ajaxFail',[context,textStatus,jqXHR])} if(loading)loading.hide()}).done(function(data,textStatus,jqXHR){if(!isRedirect){$el.trigger('ajaxDone',[context,data,textStatus,jqXHR])} if(loading)loading.hide()}).always(function(dataOrXhr,textStatus,xhrOrError){$el.trigger('ajaxAlways',[context,dataOrXhr,textStatus,xhrOrError])})} -Request.DEFAULTS={update:{},type:'POST',beforeUpdate:function(data,textStatus,jqXHR){},evalBeforeUpdate:null,evalSuccess:null,evalError:null,evalComplete:null} +Request.DEFAULTS={update:{},type:'POST',beforeUpdate:function(data,textStatus,jqXHR){},evalBeforeUpdate:null,evalSuccess:null,evalError:null,evalComplete:null,ajaxGlobal:false} Request.prototype.extractPartials=function(update){var result=[] for(var partial in update) result.push(partial) @@ -100,7 +102,7 @@ return result.join('&')} var old=$.fn.request $.fn.request=function(handler,option){var args=arguments var $this=$(this).first() -var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))} +var data={evalBeforeUpdate:$this.data('request-before-update'),evalSuccess:$this.data('request-success'),evalError:$this.data('request-error'),evalComplete:$this.data('request-complete'),ajaxGlobal:$this.data('request-ajax-global'),confirm:$this.data('request-confirm'),redirect:$this.data('request-redirect'),loading:$this.data('request-loading'),flash:$this.data('request-flash'),files:$this.data('request-files'),form:$this.data('request-form'),url:$this.data('request-url'),update:paramToObj('data-request-update',$this.data('request-update')),data:paramToObj('data-request-data',$this.data('request-data'))} if(!handler)handler=$this.data('request') var options=$.extend(true,{},Request.DEFAULTS,data,typeof option=='object'&&option) return new Request($this,handler,options)} @@ -110,14 +112,20 @@ $.fn.request.noConflict=function(){$.fn.request=old return this} function paramToObj(name,value){if(value===undefined)value='' if(typeof value=='object')return value -try{return JSON.parse(JSON.stringify(eval("({"+value+"})")))} +try{return ocJSON("{"+value+"}")} catch(e){throw new Error('Error parsing the '+name+' attribute value. '+e)}} +function getXSRFToken(){var cookieValue=null +if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';') +for(var i=0;i="0"&&str[pos]<="9")){var body="";for(var i=pos;i="0"&&str[i]<="9")){body+=str[i];}else{return{originLength:body.length,body:body};}} +throw new Error("Broken JSON number body near "+body);} +if(str[pos]==="{"||str[pos]==="["){var stack=[str[pos]];var body=str[pos];for(var i=pos+1;i=0)?pos-5:0,50));} +function canBeKeyHead(ch){if(ch[0]==="\\")return false;if((ch[0]>='a'&&ch[0]<='z')||(ch[0]>='A'&&ch[0]<='Z')||ch[0]==='_')return true;if(ch[0]>='0'&&ch[0]<='9')return true;if(ch[0]==='$')return true;if(ch.charCodeAt(0)>255)return true;return false;} +function isBlankChar(ch){return ch===" "||ch==="\n"||ch==="\t";} +function parse(str){str=str.trim();if(!str.length)throw new Error("Broken JSON object.");var result="";while(str&&str[0]===","){str=str.substr(1);} +if(str[0]==="\""||str[0]==="'"){if(str[str.length-1]!==str[0]){throw new Error("Invalid string JSON object.");} +var body="\"";for(var i=1;i= "0" && str[pos] <= "9")) { + var body = ""; + for (var i = pos; i < str.length; i++) { + if (str[i] === "-" || str[i] === "+" || str[i] === "." || (str[i] >= "0" && str[i] <= "9")) { + body += str[i]; + } else { + return { + originLength: body.length, + body: body + }; + } + } + throw new Error("Broken JSON number body near " + body); + } + + // parse object + if (str[pos] === "{" || str[pos] === "[") { + var stack = [str[pos]]; + var body = str[pos]; + for (var i = pos + 1; i < str.length; i++) { + body += str[i]; + if (str[i] === "\\") { + if (i + 1 < str.length) body += str[i + 1]; + i++; + } else if (str[i] === "\"") { + if (stack[stack.length - 1] === "\"") { + stack.pop(); + } else if (stack[stack.length - 1] !== "'") { + stack.push(str[i]); + } + } else if (str[i] === "'") { + if (stack[stack.length - 1] === "'") { + stack.pop(); + } else if (stack[stack.length - 1] !== "\"") { + stack.push(str[i]); + } + } else if (stack[stack.length - 1] !== "\"" && stack[stack.length - 1] !== "'") { + if (str[i] === "{") { + stack.push("{"); + } else if (str[i] === "}") { + if (stack[stack.length - 1] === "{") { + stack.pop(); + } else { + throw new Error("Broken JSON " + (str[pos] === "{" ? "object" : "array") + " body near " + body); + } + } else if (str[i] === "[") { + stack.push("["); + } else if (str[i] === "]") { + if (stack[stack.length - 1] === "[") { + stack.pop(); + } else { + throw new Error("Broken JSON " + (str[pos] === "{" ? "object" : "array") + " body near " + body); + } + } + } + if (!stack.length) { + return { + originLength: i - pos, + body: body + }; + } + } + throw new Error("Broken JSON " + (str[pos] === "{" ? "object" : "array") + " body near " + body); + } + throw new Error("Broken JSON body near " + str.substr((pos - 5 >= 0) ? pos - 5 : 0, 50)); + } + + function canBeKeyHead(ch) { + if (ch[0] === "\\") return false; + if ((ch[0] >= 'a' && ch[0] <= 'z') || (ch[0] >= 'A' && ch[0] <= 'Z') || ch[0] === '_') return true; + if (ch[0] >= '0' && ch[0] <= '9') return true; + if (ch[0] === '$') return true; + if (ch.charCodeAt(0) > 255) return true; + return false; + } + + function isBlankChar(ch) { + return ch === " " || ch === "\n" || ch === "\t"; + } + + function parse(str) { + str = str.trim(); + if (!str.length) throw new Error("Broken JSON object."); + var result = ""; + + /* + * the mistake ',' + */ + while (str && str[0] === ",") { + str = str.substr(1); + } + + /* + * string + */ + if (str[0] === "\"" || str[0] === "'") { + if (str[str.length - 1] !== str[0]) { + throw new Error("Invalid string JSON object."); + } + + var body = "\""; + for (var i = 1; i < str.length; i++) { + if (str[i] === "\\") { + if (str[i + 1] === "'") { + body += str[i + 1] + } else { + body += str[i]; + body += str[i + 1]; + } + i++; + } else if (str[i] === str[0]) { + body += "\""; + return body + } else if (str[i] === "\"") { + body += "\\\"" + } else body += str[i]; + } + throw new Error("Invalid string JSON object."); + } + + /* + * boolean + */ + if (str === "true" || str === "false") { + return str; + } + + /* + * null + */ + if (str === "null") { + return "null"; + } + + /* + * number + */ + var num = parseFloat(str); + if (!isNaN(num)) { + return num.toString(); + } + + /* + * object + */ + if (str[0] === "{") { + var type = "needKey"; + var result = "{"; + + for (var i = 1; i < str.length; i++) { + if (isBlankChar(str[i])) { + continue; + } else if (type === "needKey" && (str[i] === "\"" || str[i] === "'")) { + var key = parseKey(str, i + 1, str[i]); + result += "\"" + key + "\""; + i += key.length; + i += 1; + type = "afterKey"; + } else if (type === "needKey" && canBeKeyHead(str[i])) { + var key = parseKey(str, i); + result += "\""; + result += key; + result += "\""; + i += key.length - 1; + type = "afterKey"; + } else if (type === "afterKey" && str[i] === ":") { + result += ":"; + type = ":"; + } else if (type === ":") { + var body = getBody(str, i); + + i = i + body.originLength - 1; + result += parse(body.body); + + type = "afterBody"; + } else if (type === "afterBody" || type === "needKey") { + var last = i; + while (str[last] === "," || isBlankChar(str[last])) { + last++; + } + if (str[last] === "}" && last === str.length - 1) { + while (result[result.length - 1] === ",") { + result = result.substr(0, result.length - 1); + } + result += "}"; + return result; + } else if (last !== i && result !== "{") { + result += ","; + type = "needKey"; + i = last - 1; + } + } + } + throw new Error("Broken JSON object near " + result); + } + + /* + * array + */ + if (str[0] === "[") { + var result = "["; + var type = "needBody"; + for (var i = 1; i < str.length; i++) { + if (" " === str[i] || "\n" === str[i] || "\t" === str[i]) { + continue; + } else if (type === "needBody") { + if (str[i] === ",") { + result += "null,"; + continue; + } + if (str[i] === "]" && i === str.length - 1) { + if (result[result.length - 1] === ",") result = result.substr(0, result.length - 1); + result += "]"; + return result; + } + + var body = getBody(str, i); + + i = i + body.originLength - 1; + result += parse(body.body); + + type = "afterBody"; + } else if (type === "afterBody") { + if (str[i] === ",") { + result += ","; + type = "needBody"; + + // deal with mistake "," + while (str[i + 1] === "," || isBlankChar(str[i + 1])) { + if (str[i + 1] === ",") result += "null,"; + i++; + } + } else if (str[i] === "]" && i === str.length - 1) { + result += "]"; + return result; + } + } + } + throw new Error("Broken JSON array near " + result); + } + } + + // Global function + window.ocJSON = function(json) { + var jsonString = parse(json); + return JSON.parse(jsonString); + }; + +}(window); diff --git a/modules/system/assets/js/lang/lang.fi.js b/modules/system/assets/js/lang/lang.fi.js index 15d7547d6..9691c8c74 100644 --- a/modules/system/assets/js/lang/lang.fi.js +++ b/modules/system/assets/js/lang/lang.fi.js @@ -5,7 +5,7 @@ if ($.oc === undefined) $.oc = {} if ($.oc.langMessages === undefined) $.oc.langMessages = {} $.oc.langMessages['fi'] = $.extend( $.oc.langMessages['fi'] || {}, - {"markdowneditor":{"formatting":"Muotoilu","quote":"Lainaus","code":"Koodi","header1":"Otsikko 1","header2":"Otsikko 2","header3":"Otsikko 3","header4":"Otsikko 4","header5":"Otsikko 5","header6":"Otsikko 6","bold":"Lihavoi","italic":"Kursivoi","unorderedlist":"J\u00e4rjest\u00e4m\u00e4t\u00f6n lista","orderedlist":"J\u00e4rjestetty lista","video":"Video","image":"Kuva","link":"Linkki","horizontalrule":"Aseta vaakasuuntainen viiva","fullscreen":"Koko n\u00e4ytt\u00f6","preview":"Esikatsele"},"mediamanager":{"insert_link":"Aseta medialinkki","insert_image":"Aseta Media kuva","insert_video":"Aseta Media video","insert_audio":"Aseta Media audio","invalid_file_empty_insert":"Valitse linkitett\u00e4v\u00e4 tiedosto.","invalid_file_single_insert":"Valitse yksi tiedosto.","invalid_image_empty_insert":"Valitse liitett\u00e4v\u00e4(t) kuva(t).","invalid_video_empty_insert":"Valitse liitett\u00e4v\u00e4 videotiedosto.","invalid_audio_empty_insert":"Valitse liitett\u00e4v\u00e4 audiotiedosto."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Peruuta","widget_remove_confirm":"Poista t\u00e4m\u00e4 vekotin?"},"datepicker":{"previousMonth":"Edellinen kuukausi","nextMonth":"Seuraava kuukausi","months":["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kes\u00e4kuu","hein\u00e4kuu","elokuu","toukokuu","lokakuu","marraskuu","joulukuu"],"weekdays":["Sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],"weekdaysShort":["su","ma","ti","ke","to","pe","la"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"kaikki"},"scopes":{"apply_button_text":"Apply","clear_button_text":"Clear"},"dates":{"all":"kaikki","filter_button_text":"Suodata","reset_button_text":"Palauta","date_placeholder":"P\u00e4iv\u00e4","after_placeholder":"J\u00e4lkeen","before_placeholder":"Ennen"},"numbers":{"all":"kaikki","filter_button_text":"Suodata","reset_button_text":"Palauta","min_placeholder":"V\u00e4hint\u00e4\u00e4n","max_placeholder":"Enint\u00e4\u00e4n"}},"eventlog":{"show_stacktrace":"N\u00e4yt\u00e4 stacktrace","hide_stacktrace":"Piilota stacktrace","tabs":{"formatted":"Muotoiltu","raw":"Raaka"},"editor":{"title":"L\u00e4hdekoodieditori","description":"K\u00e4ytt\u00f6j\u00e4rjestelm\u00e4si pit\u00e4isi olla m\u00e4\u00e4ritetty kuuntelemaan jotain n\u00e4ist\u00e4 URL osoitteista.","openWith":"Avaa sovelluksella","remember_choice":"Muista valittu vaihtoehto t\u00e4lle istunnolle","open":"Avaa","cancel":"Peruuta"}}} + {"markdowneditor":{"formatting":"Muotoilu","quote":"Lainaus","code":"Koodi","header1":"Otsikko 1","header2":"Otsikko 2","header3":"Otsikko 3","header4":"Otsikko 4","header5":"Otsikko 5","header6":"Otsikko 6","bold":"Lihavointi","italic":"Kursivointi","unorderedlist":"J\u00e4rjest\u00e4m\u00e4t\u00f6n lista","orderedlist":"J\u00e4rjestetty lista","video":"Video","image":"Kuva","link":"Linkki","horizontalrule":"Lis\u00e4\u00e4 horisontaalinen jakaja","fullscreen":"Kokon\u00e4ytt\u00f6","preview":"Esikatsele"},"mediamanager":{"insert_link":"Lis\u00e4\u00e4 linkki Mediaan","insert_image":"Lis\u00e4\u00e4 kuva","insert_video":"Lis\u00e4\u00e4 video","insert_audio":"Lis\u00e4\u00e4 \u00e4\u00e4nitiedosto","invalid_file_empty_insert":"Valitse liitett\u00e4v\u00e4 tiedosto.","invalid_file_single_insert":"Valitse vain yksi tiedosto.","invalid_image_empty_insert":"Valitse linkitett\u00e4v\u00e4(t) kuva(t).","invalid_video_empty_insert":"Valitse linkitett\u00e4v\u00e4 videotiedosto.","invalid_audio_empty_insert":"Valitse linkitett\u00e4v\u00e4 \u00e4\u00e4nitiedosto."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Peruuta","widget_remove_confirm":"Poista t\u00e4m\u00e4 vimpain?"},"datepicker":{"previousMonth":"Edellinen kuukausi","nextMonth":"Seuraava kuukausi","months":["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kes\u00e4kuu","hein\u00e4kuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],"weekdays":["sunnutai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],"weekdaysShort":["su","ma","ti","ke","to","pe","la"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"kaikki"},"scopes":{"apply_button_text":"Ota k\u00e4ytt\u00f6\u00f6n","clear_button_text":"Tyhjenn\u00e4"},"dates":{"all":"kaikki","filter_button_text":"Suodata","reset_button_text":"Palauta","date_placeholder":"P\u00e4iv\u00e4","after_placeholder":"J\u00e4lkeen","before_placeholder":"Ennen"},"numbers":{"all":"kaikki","filter_button_text":"Suodata","reset_button_text":"Palauta","min_placeholder":"V\u00e4h.","max_placeholder":"Enint."}},"eventlog":{"show_stacktrace":"N\u00e4yt\u00e4 stacktrace","hide_stacktrace":"Piilota stacktrace","tabs":{"formatted":"Muotoiltu","raw":"Raaka"},"editor":{"title":"L\u00e4hdekoodieditori","description":"K\u00e4ytt\u00f6j\u00e4rjestelm\u00e4si pit\u00e4isi olla m\u00e4\u00e4ritetty kuuntelemaan jotain n\u00e4ist\u00e4 URL osoitteista.","openWith":"Avaa sovelluksessa","remember_choice":"Muista valittu vaihtoehto istunnon ajan","open":"Avaa","cancel":"Peruuta"}}} ); //! moment.js locale configuration v2.22.2 diff --git a/modules/system/assets/js/lang/lang.fr.js b/modules/system/assets/js/lang/lang.fr.js index b0e48ddf2..912c23504 100644 --- a/modules/system/assets/js/lang/lang.fr.js +++ b/modules/system/assets/js/lang/lang.fr.js @@ -5,61 +5,62 @@ if ($.oc === undefined) $.oc = {} if ($.oc.langMessages === undefined) $.oc.langMessages = {} $.oc.langMessages['fr'] = $.extend( $.oc.langMessages['fr'] || {}, - {"markdowneditor":{"formatting":"Formatage","quote":"Citation","code":"Code","header1":"Ent\u00eate 1","header2":"Ent\u00eate 2","header3":"Ent\u00eate 3","header4":"Ent\u00eate 4","header5":"Ent\u00eate 5","header6":"Ent\u00eate 6","bold":"Gras","italic":"Italique","unorderedlist":"Liste non ordonn\u00e9e","orderedlist":"Liste ordonn\u00e9e","video":"Vid\u00e9o","image":"Image","link":"Lien","horizontalrule":"Ins\u00e9rer la r\u00e8gle horizontalement","fullscreen":"Plein \u00e9cran","preview":"Aper\u00e7u"},"mediamanager":{"insert_link":"Ins\u00e9rer un lien vers un fichier du gestionnaire de m\u00e9dia","insert_image":"Ins\u00e9rer une image du gestionnaire de m\u00e9dia","insert_video":"Ins\u00e9rer une vid\u00e9o du gestionnaire de m\u00e9dia","insert_audio":"Ins\u00e9rer un document audio du gestionnaire de m\u00e9dia","invalid_file_empty_insert":"Veuillez s\u00e9lectionner un fichier \u00e0 lier.","invalid_file_single_insert":"Veuillez s\u00e9lectionner un seul fichier.","invalid_image_empty_insert":"Veuillez s\u00e9lectionner au moins une image \u00e0 ins\u00e9rer.","invalid_video_empty_insert":"Veuillez s\u00e9lectionner une vid\u00e9o \u00e0 ins\u00e9rer.","invalid_audio_empty_insert":"Veuillez s\u00e9lectionner un document audio \u00e0 ins\u00e9rer."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Annuler","widget_remove_confirm":"Retirer ce widget ?"},"datepicker":{"previousMonth":"Mois pr\u00e9c\u00e9dent","nextMonth":"Mois suivant","months":["Janvier","F\u00e9vrier","Mars","Avril","Mai","Juin","Juillet","Ao\u00fbt","Septembre","Octobre","Novembre","D\u00e9cembre"],"weekdays":["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],"weekdaysShort":["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"tous"},"scopes":{"apply_button_text":"Apply","clear_button_text":"Clear"},"dates":{"all":"toute la p\u00e9riode","filter_button_text":"Filtrer","reset_button_text":"Effacer","date_placeholder":"Date","after_placeholder":"Apr\u00e8s le","before_placeholder":"Avant le"},"numbers":{"all":"all","filter_button_text":"Filter","reset_button_text":"Reset","min_placeholder":"Min","max_placeholder":"Max"}},"eventlog":{"show_stacktrace":"Afficher la pile d\u2019ex\u00e9cution","hide_stacktrace":"Masquer la pile d\u2019ex\u00e9cution","tabs":{"formatted":"Message format\u00e9","raw":"Message brut"},"editor":{"title":"S\u00e9lectionnez l\u2019\u00e9diteur de code source \u00e0 utiliser","description":"L\u2019environnement de votre syst\u00e8me d\u2019exploitation doit \u00eatre configur\u00e9 pour ouvrir l\u2019un des sch\u00e9mas d\u2019URL ci-dessous.","openWith":"Ouvrir avec","remember_choice":"Se souvenir de la s\u00e9lection pour la dur\u00e9e de la session dans ce navigateur","open":"Ouvrir","cancel":"Annuler"}}} + { "markdowneditor": { "formatting": "Formatage", "quote": "Citation", "code": "Code", "header1": "Ent\u00eate 1", "header2": "Ent\u00eate 2", "header3": "Ent\u00eate 3", "header4": "Ent\u00eate 4", "header5": "Ent\u00eate 5", "header6": "Ent\u00eate 6", "bold": "Gras", "italic": "Italique", "unorderedlist": "Liste non ordonn\u00e9e", "orderedlist": "Liste ordonn\u00e9e", "video": "Vid\u00e9o", "image": "Image", "link": "Lien", "horizontalrule": "Ins\u00e9rer la r\u00e8gle horizontalement", "fullscreen": "Plein \u00e9cran", "preview": "Aper\u00e7u" }, "mediamanager": { "insert_link": "Ins\u00e9rer un lien vers un fichier du gestionnaire de m\u00e9dia", "insert_image": "Ins\u00e9rer une image du gestionnaire de m\u00e9dia", "insert_video": "Ins\u00e9rer une vid\u00e9o du gestionnaire de m\u00e9dia", "insert_audio": "Ins\u00e9rer un document audio du gestionnaire de m\u00e9dia", "invalid_file_empty_insert": "Veuillez s\u00e9lectionner un fichier \u00e0 lier.", "invalid_file_single_insert": "Veuillez s\u00e9lectionner un seul fichier.", "invalid_image_empty_insert": "Veuillez s\u00e9lectionner au moins une image \u00e0 ins\u00e9rer.", "invalid_video_empty_insert": "Veuillez s\u00e9lectionner une vid\u00e9o \u00e0 ins\u00e9rer.", "invalid_audio_empty_insert": "Veuillez s\u00e9lectionner un document audio \u00e0 ins\u00e9rer." }, "alert": { "confirm_button_text": "OK", "cancel_button_text": "Annuler", "widget_remove_confirm": "Retirer ce widget ?" }, "datepicker": { "previousMonth": "Mois pr\u00e9c\u00e9dent", "nextMonth": "Mois suivant", "months": ["Janvier", "F\u00e9vrier", "Mars", "Avril", "Mai", "Juin", "Juillet", "Ao\u00fbt", "Septembre", "Octobre", "Novembre", "D\u00e9cembre"], "weekdays": ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"], "weekdaysShort": ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"] }, "colorpicker": { "choose": "Ok" }, "filter": { "group": { "all": "tous" }, "scopes": { "apply_button_text": "Appliquer", "clear_button_text": "Annuler" }, "dates": { "all": "toute la p\u00e9riode", "filter_button_text": "Filtrer", "reset_button_text": "Effacer", "date_placeholder": "Date", "after_placeholder": "Apr\u00e8s le", "before_placeholder": "Avant le" }, "numbers": { "all": "tous", "filter_button_text": "Filtres", "reset_button_text": "R\u00e9initialiser", "min_placeholder": "Min", "max_placeholder": "Max" } }, "eventlog": { "show_stacktrace": "Afficher la pile d\u2019ex\u00e9cution", "hide_stacktrace": "Masquer la pile d\u2019ex\u00e9cution", "tabs": { "formatted": "Message format\u00e9", "raw": "Message brut" }, "editor": { "title": "S\u00e9lectionnez l\u2019\u00e9diteur de code source \u00e0 utiliser", "description": "L\u2019environnement de votre syst\u00e8me d\u2019exploitation doit \u00eatre configur\u00e9 pour ouvrir l\u2019un des sch\u00e9mas d\u2019URL ci-dessous.", "openWith": "Ouvrir avec", "remember_choice": "Se souvenir de la s\u00e9lection pour la dur\u00e9e de la session dans ce navigateur", "open": "Ouvrir", "cancel": "Annuler" } } } ); //! moment.js locale configuration v2.22.2 -;(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' - && typeof require === 'function' ? factory(require('../moment')) : - typeof define === 'function' && define.amd ? define(['../moment'], factory) : - factory(global.moment) -}(this, (function (moment) { 'use strict'; +; (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { + 'use strict'; var fr = moment.defineLocale('fr', { - months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), - monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), - monthsParseExact : true, - weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), - weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), - weekdaysMin : 'di_lu_ma_me_je_ve_sa'.split('_'), - weekdaysParseExact : true, - longDateFormat : { - LT : 'HH:mm', - LTS : 'HH:mm:ss', - L : 'DD/MM/YYYY', - LL : 'D MMMM YYYY', - LLL : 'D MMMM YYYY HH:mm', - LLLL : 'dddd D MMMM YYYY HH:mm' + months: 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), + monthsShort: 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), + monthsParseExact: true, + weekdays: 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), + weekdaysShort: 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), + weekdaysMin: 'di_lu_ma_me_je_ve_sa'.split('_'), + weekdaysParseExact: true, + longDateFormat: { + LT: 'HH:mm', + LTS: 'HH:mm:ss', + L: 'DD/MM/YYYY', + LL: 'D MMMM YYYY', + LLL: 'D MMMM YYYY HH:mm', + LLLL: 'dddd D MMMM YYYY HH:mm' }, - calendar : { - sameDay : '[Aujourd’hui à] LT', - nextDay : '[Demain à] LT', - nextWeek : 'dddd [à] LT', - lastDay : '[Hier à] LT', - lastWeek : 'dddd [dernier à] LT', - sameElse : 'L' + calendar: { + sameDay: '[Aujourd’hui à] LT', + nextDay: '[Demain à] LT', + nextWeek: 'dddd [à] LT', + lastDay: '[Hier à] LT', + lastWeek: 'dddd [dernier à] LT', + sameElse: 'L' }, - relativeTime : { - future : 'dans %s', - past : 'il y a %s', - s : 'quelques secondes', - ss : '%d secondes', - m : 'une minute', - mm : '%d minutes', - h : 'une heure', - hh : '%d heures', - d : 'un jour', - dd : '%d jours', - M : 'un mois', - MM : '%d mois', - y : 'un an', - yy : '%d ans' + relativeTime: { + future: 'dans %s', + past: 'il y a %s', + s: 'quelques secondes', + ss: '%d secondes', + m: 'une minute', + mm: '%d minutes', + h: 'une heure', + hh: '%d heures', + d: 'un jour', + dd: '%d jours', + M: 'un mois', + MM: '%d mois', + y: 'un an', + yy: '%d ans' }, dayOfMonthOrdinalParse: /\d{1,2}(er|)/, - ordinal : function (number, period) { + ordinal: function (number, period) { switch (period) { // TODO: Return 'e' when day of month > 1. Move this case inside // block for masculine words below. @@ -81,9 +82,9 @@ $.oc.langMessages['fr'] = $.extend( return number + (number === 1 ? 're' : 'e'); } }, - week : { - dow : 1, // Monday is the first day of the week. - doy : 4 // The week that contains Jan 4th is the first week of the year. + week: { + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. } }); diff --git a/modules/system/assets/js/lang/lang.nl.js b/modules/system/assets/js/lang/lang.nl.js index 9c8e283fa..b490c469a 100644 --- a/modules/system/assets/js/lang/lang.nl.js +++ b/modules/system/assets/js/lang/lang.nl.js @@ -5,7 +5,7 @@ if ($.oc === undefined) $.oc = {} if ($.oc.langMessages === undefined) $.oc.langMessages = {} $.oc.langMessages['nl'] = $.extend( $.oc.langMessages['nl'] || {}, - {"markdowneditor":{"formatting":"Opmaak","quote":"Quote","code":"Code","header1":"Koptekst 1","header2":"Koptekst 2","header3":"Koptekst 3","header4":"Koptekst 4","header5":"Koptekst 5","header6":"Koptekst 6","bold":"Vet","italic":"Cursief","unorderedlist":"Ongeordende lijst","orderedlist":"Gerangschikte lijst","video":"Video","image":"Afbeelding","link":"Hyperlink","horizontalrule":"Invoegen horizontale lijn","fullscreen":"Volledig scherm","preview":"Voorbeeldweergave"},"mediamanager":{"insert_link":"Invoegen Media Link","insert_image":"Invoegen Media Afbeelding","insert_video":"Invoegen Media Video","insert_audio":"Invoegen Media Audio","invalid_file_empty_insert":"Selecteer bestand om een link naar te maken.","invalid_file_single_insert":"Selecteer \u00e9\u00e9n bestand.","invalid_image_empty_insert":"Selecteer afbeelding(en) om in te voegen.","invalid_video_empty_insert":"Selecteer een video bestand om in te voegen.","invalid_audio_empty_insert":"Selecteer een audio bestand om in te voegen."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Annuleren","widget_remove_confirm":"Deze widget verwijderen?"},"datepicker":{"previousMonth":"Vorige maand","nextMonth":"Volgende maan","months":["Januari","Februari","Maart","April","Mei","Juni","Juli","Augustus","September","Oktober","November","December"],"weekdays":["Zondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrijdag","Zaterdag"],"weekdaysShort":["Zo","Ma","Di","Wo","Do","Vr","Za"]},"colorpicker":{"choose":"OK"},"filter":{"group":{"all":"alle"},"scopes":{"apply_button_text":"Apply","clear_button_text":"Clear"},"dates":{"all":"alle","filter_button_text":"Filteren","reset_button_text":"Resetten","date_placeholder":"Datum","after_placeholder":"Na","before_placeholder":"Voor"},"numbers":{"all":"alle","filter_button_text":"Filteren","reset_button_text":"Resetten","min_placeholder":"Minimum","max_placeholder":"Maximum"}},"eventlog":{"show_stacktrace":"Toon stacktrace","hide_stacktrace":"Verberg stacktrace","tabs":{"formatted":"Geformatteerd","raw":"Bronversie"},"editor":{"title":"Broncode editor","description":"Je besturingssysteem moet in staat zijn om met deze URL-schema's om te kunnen gaan.","openWith":"Openen met","remember_choice":"Onthoudt de geselecteerde optie voor deze browser-sessie","open":"Openen","cancel":"Annuleren"}}} + {"markdowneditor":{"formatting":"Opmaak","quote":"Quote","code":"Code","header1":"Koptekst 1","header2":"Koptekst 2","header3":"Koptekst 3","header4":"Koptekst 4","header5":"Koptekst 5","header6":"Koptekst 6","bold":"Vet","italic":"Cursief","unorderedlist":"Ongeordende lijst","orderedlist":"Gerangschikte lijst","video":"Video","image":"Afbeelding","link":"Hyperlink","horizontalrule":"Invoegen horizontale lijn","fullscreen":"Volledig scherm","preview":"Voorbeeldweergave"},"mediamanager":{"insert_link":"Invoegen Media Link","insert_image":"Invoegen Media Afbeelding","insert_video":"Invoegen Media Video","insert_audio":"Invoegen Media Audio","invalid_file_empty_insert":"Selecteer bestand om een link naar te maken.","invalid_file_single_insert":"Selecteer \u00e9\u00e9n bestand.","invalid_image_empty_insert":"Selecteer afbeelding(en) om in te voegen.","invalid_video_empty_insert":"Selecteer een video bestand om in te voegen.","invalid_audio_empty_insert":"Selecteer een audio bestand om in te voegen."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Annuleren","widget_remove_confirm":"Deze widget verwijderen?"},"datepicker":{"previousMonth":"Vorige maand","nextMonth":"Volgende maan","months":["Januari","Februari","Maart","April","Mei","Juni","Juli","Augustus","September","Oktober","November","December"],"weekdays":["Zondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrijdag","Zaterdag"],"weekdaysShort":["Zo","Ma","Di","Wo","Do","Vr","Za"]},"colorpicker":{"choose":"OK"},"filter":{"group":{"all":"alle"},"scopes":{"apply_button_text":"Toepassen","clear_button_text":"Resetten"},"dates":{"all":"alle","filter_button_text":"Filteren","reset_button_text":"Resetten","date_placeholder":"Datum","after_placeholder":"Na","before_placeholder":"Voor"},"numbers":{"all":"alle","filter_button_text":"Filteren","reset_button_text":"Resetten","min_placeholder":"Minimum","max_placeholder":"Maximum"}},"eventlog":{"show_stacktrace":"Toon stacktrace","hide_stacktrace":"Verberg stacktrace","tabs":{"formatted":"Geformatteerd","raw":"Bronversie"},"editor":{"title":"Broncode editor","description":"Je besturingssysteem moet in staat zijn om met deze URL-schema's om te kunnen gaan.","openWith":"Openen met","remember_choice":"Onthoudt de geselecteerde optie voor deze browser-sessie","open":"Openen","cancel":"Annuleren"}}} ); //! moment.js locale configuration v2.22.2 diff --git a/modules/system/assets/js/lang/lang.pt-br.js b/modules/system/assets/js/lang/lang.pt-br.js index a19007dfe..cd3468dc8 100644 --- a/modules/system/assets/js/lang/lang.pt-br.js +++ b/modules/system/assets/js/lang/lang.pt-br.js @@ -5,7 +5,7 @@ if ($.oc === undefined) $.oc = {} if ($.oc.langMessages === undefined) $.oc.langMessages = {} $.oc.langMessages['pt-br'] = $.extend( $.oc.langMessages['pt-br'] || {}, - {"markdowneditor":{"formatting":"Formatando","quote":"Cita\u00e7\u00e3o","code":"C\u00f3digo","header1":"Cabe\u00e7alho 1","header2":"Cabe\u00e7alho 2","header3":"Cabe\u00e7alho 3","header4":"Cabe\u00e7alho 4","header5":"Cabe\u00e7alho 5","header6":"Cabe\u00e7alho 6","bold":"Negrito","italic":"It\u00e1lico","unorderedlist":"Lista n\u00e3o ordenada","orderedlist":"Lista ordenada","video":"V\u00eddeo","image":"Imagem","link":"Link","horizontalrule":"Inserir linha horizontal","fullscreen":"Tela cheia","preview":"Visualizar"},"mediamanager":{"insert_link":"Inserir link","insert_image":"Inserir imagem","insert_video":"Inserir v\u00eddeo","insert_audio":"Inserir \u00e1udio","invalid_file_empty_insert":"Por favor, selecione o arquivo para criar o link.","invalid_file_single_insert":"Por favor, selecione apenas um arquivo.","invalid_image_empty_insert":"Por favor, selecione as imagens que deseja inserir.","invalid_video_empty_insert":"Por favor, selecione os v\u00eddeos que deseja inserir.","invalid_audio_empty_insert":"Por favor, selecione os \u00e1udios que deseja inserir."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Cancelar","widget_remove_confirm":"Remove this widget?"},"datepicker":{"previousMonth":"M\u00eas anterior","nextMonth":"Pr\u00f3ximo m\u00eas","months":["Janeiro","Fevereiro","Mar\u00e7o","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],"weekdays":["Domingo","Segunda-feira","Ter\u00e7a-feira","Quarta-feira","Quinta-feira","Sexta-feira","S\u00e1bado"],"weekdaysShort":["Dom","Seg","Ter","Qua","Qui","Sex","Sab"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"todos"},"scopes":{"apply_button_text":"Apply","clear_button_text":"Clear"},"dates":{"all":"todas","filter_button_text":"Filtro","reset_button_text":"Reiniciar","date_placeholder":"Data","after_placeholder":"Ap\u00f3s","before_placeholder":"Antes"},"numbers":{"all":"all","filter_button_text":"Filter","reset_button_text":"Reset","min_placeholder":"Min","max_placeholder":"Max"}},"eventlog":{"show_stacktrace":"Exibir o rastreamento","hide_stacktrace":"Ocultar o rastreamento","tabs":{"formatted":"Formatado","raw":"Bruto"},"editor":{"title":"Editor de c\u00f3digo fonte","description":"Seu sistema operacional deve ser configurado para ouvir um desses esquemas de URL.","openWith":"Abrir com","remember_choice":"Lembrar a op\u00e7\u00e3o selecionada nesta sess\u00e3o","open":"Abrir","cancel":"Cancelar"}}} + {"markdowneditor":{"formatting":"Formatando","quote":"Cita\u00e7\u00e3o","code":"C\u00f3digo","header1":"Cabe\u00e7alho 1","header2":"Cabe\u00e7alho 2","header3":"Cabe\u00e7alho 3","header4":"Cabe\u00e7alho 4","header5":"Cabe\u00e7alho 5","header6":"Cabe\u00e7alho 6","bold":"Negrito","italic":"It\u00e1lico","unorderedlist":"Lista n\u00e3o ordenada","orderedlist":"Lista ordenada","video":"V\u00eddeo","image":"Imagem","link":"Link","horizontalrule":"Inserir linha horizontal","fullscreen":"Tela cheia","preview":"Visualizar"},"mediamanager":{"insert_link":"Inserir link","insert_image":"Inserir imagem","insert_video":"Inserir v\u00eddeo","insert_audio":"Inserir \u00e1udio","invalid_file_empty_insert":"Por favor, selecione o arquivo para criar o link.","invalid_file_single_insert":"Por favor, selecione apenas um arquivo.","invalid_image_empty_insert":"Por favor, selecione as imagens que deseja inserir.","invalid_video_empty_insert":"Por favor, selecione os v\u00eddeos que deseja inserir.","invalid_audio_empty_insert":"Por favor, selecione os \u00e1udios que deseja inserir."},"alert":{"confirm_button_text":"OK","cancel_button_text":"Cancelar","widget_remove_confirm":"Remover este widget?"},"datepicker":{"previousMonth":"M\u00eas anterior","nextMonth":"Pr\u00f3ximo m\u00eas","months":["Janeiro","Fevereiro","Mar\u00e7o","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],"weekdays":["Domingo","Segunda-feira","Ter\u00e7a-feira","Quarta-feira","Quinta-feira","Sexta-feira","S\u00e1bado"],"weekdaysShort":["Dom","Seg","Ter","Qua","Qui","Sex","Sab"]},"colorpicker":{"choose":"Ok"},"filter":{"group":{"all":"todos"},"scopes":{"apply_button_text":"Aplicar","clear_button_text":"Limpar"},"dates":{"all":"todas","filter_button_text":"Filtro","reset_button_text":"Reiniciar","date_placeholder":"Data","after_placeholder":"Ap\u00f3s","before_placeholder":"Antes"},"numbers":{"all":"todas","filter_button_text":"Filtar","reset_button_text":"Reiniciar","min_placeholder":"Min","max_placeholder":"Max"}},"eventlog":{"show_stacktrace":"Exibir o rastreamento","hide_stacktrace":"Ocultar o rastreamento","tabs":{"formatted":"Formatado","raw":"Bruto"},"editor":{"title":"Editor de c\u00f3digo fonte","description":"Seu sistema operacional deve ser configurado para ouvir um desses esquemas de URL.","openWith":"Abrir com","remember_choice":"Lembrar a op\u00e7\u00e3o selecionada nesta sess\u00e3o","open":"Abrir","cancel":"Cancelar"}}} ); //! moment.js locale configuration v2.22.2 diff --git a/modules/system/assets/js/lang/lang.th.js b/modules/system/assets/js/lang/lang.th.js new file mode 100644 index 000000000..3d3d6a257 --- /dev/null +++ b/modules/system/assets/js/lang/lang.th.js @@ -0,0 +1,77 @@ +/* + * This file has been compiled from: /modules/system/lang/th/client.php + */ +if ($.oc === undefined) $.oc = {} +if ($.oc.langMessages === undefined) $.oc.langMessages = {} +$.oc.langMessages['th'] = $.extend( + $.oc.langMessages['th'] || {}, + {"markdowneditor":{"formatting":"\u0e01\u0e32\u0e23\u0e08\u0e31\u0e14\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a","quote":"\u0e2d\u0e49\u0e32\u0e07\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21","code":"\u0e42\u0e04\u0e49\u0e14","header1":"\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 1","header2":"\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 2","header3":"\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 3","header4":"\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 4","header5":"\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 5","header6":"\u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07 6","bold":"\u0e15\u0e31\u0e27\u0e2b\u0e19\u0e32","italic":"\u0e15\u0e31\u0e27\u0e40\u0e2d\u0e35\u0e22\u0e07","unorderedlist":"\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e41\u0e1a\u0e1a\u0e44\u0e21\u0e48\u0e21\u0e35\u0e25\u0e33\u0e14\u0e31\u0e1a","orderedlist":"\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e41\u0e1a\u0e1a\u0e21\u0e35\u0e25\u0e33\u0e14\u0e31\u0e1a","video":"\u0e27\u0e34\u0e14\u0e35\u0e42\u0e2d","image":"\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e","link":"\u0e25\u0e34\u0e07\u0e01\u0e4c","horizontalrule":"\u0e43\u0e2a\u0e48\u0e17\u0e35\u0e48\u0e04\u0e31\u0e48\u0e19\u0e1a\u0e23\u0e23\u0e17\u0e31\u0e14","fullscreen":"\u0e40\u0e15\u0e47\u0e21\u0e2b\u0e19\u0e49\u0e32\u0e08\u0e2d","preview":"\u0e14\u0e39\u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07"},"mediamanager":{"insert_link":"\u0e43\u0e2a\u0e48\u0e25\u0e34\u0e07\u0e01\u0e4c","insert_image":"\u0e43\u0e2a\u0e48\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e","insert_video":"\u0e43\u0e2a\u0e48\u0e27\u0e34\u0e14\u0e35\u0e42\u0e2d","insert_audio":"\u0e43\u0e2a\u0e48\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e2a\u0e35\u0e22\u0e07","invalid_file_empty_insert":"\u0e01\u0e23\u0e38\u0e13\u0e32\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e44\u0e1f\u0e25\u0e4c\u0e17\u0e35\u0e48\u0e08\u0e30\u0e43\u0e2a\u0e48\u0e25\u0e34\u0e07\u0e01\u0e4c","invalid_file_single_insert":"\u0e01\u0e23\u0e38\u0e13\u0e32\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e14\u0e35\u0e22\u0e27","invalid_image_empty_insert":"\u0e01\u0e23\u0e38\u0e13\u0e32\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e23\u0e39\u0e1b\u0e20\u0e32\u0e1e\u0e17\u0e35\u0e48\u0e08\u0e30\u0e43\u0e2a\u0e48","invalid_video_empty_insert":"\u0e01\u0e23\u0e38\u0e13\u0e32\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e27\u0e34\u0e14\u0e35\u0e42\u0e2d\u0e17\u0e35\u0e48\u0e08\u0e30\u0e43\u0e2a\u0e48","invalid_audio_empty_insert":"\u0e01\u0e23\u0e38\u0e13\u0e32\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e44\u0e1f\u0e25\u0e4c\u0e40\u0e2a\u0e35\u0e22\u0e07\u0e17\u0e35\u0e48\u0e08\u0e30\u0e43\u0e2a\u0e48"},"alert":{"confirm_button_text":"\u0e15\u0e01\u0e25\u0e07","cancel_button_text":"\u0e22\u0e01\u0e40\u0e25\u0e34\u0e01","widget_remove_confirm":"\u0e25\u0e1a\u0e27\u0e34\u0e14\u0e40\u0e08\u0e47\u0e17\u0e19\u0e35\u0e49?"},"datepicker":{"previousMonth":"\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e01\u0e48\u0e2d\u0e19\u0e2b\u0e19\u0e49\u0e32","nextMonth":"\u0e40\u0e14\u0e37\u0e2d\u0e19\u0e16\u0e31\u0e14\u0e44\u0e1b","months":["\u0e21\u0e01\u0e23\u0e32\u0e04\u0e21","\u0e01\u0e38\u0e21\u0e20\u0e32\u0e1e\u0e31\u0e19\u0e18\u0e4c","\u0e21\u0e35\u0e19\u0e32\u0e04\u0e21","\u0e40\u0e21\u0e29\u0e32\u0e22\u0e19","\u0e1e\u0e24\u0e29\u0e20\u0e32\u0e04\u0e21","\u0e21\u0e34\u0e16\u0e38\u0e19\u0e32\u0e22\u0e19","\u0e01\u0e23\u0e01\u0e0e\u0e32\u0e04\u0e21","\u0e2a\u0e34\u0e07\u0e2b\u0e32\u0e04\u0e21","\u0e01\u0e31\u0e19\u0e22\u0e32\u0e22\u0e19","\u0e15\u0e38\u0e25\u0e32\u0e04\u0e21","\u0e1e\u0e24\u0e28\u0e08\u0e34\u0e01\u0e32\u0e22\u0e19","\u0e18\u0e31\u0e19\u0e27\u0e32\u0e04\u0e21"],"weekdays":["\u0e2d\u0e32\u0e17\u0e34\u0e15\u0e22\u0e4c","\u0e08\u0e31\u0e19\u0e17\u0e23\u0e4c","\u0e2d\u0e31\u0e07\u0e04\u0e32\u0e23","\u0e1e\u0e38\u0e18","\u0e1e\u0e24\u0e2b\u0e31\u0e2a\u0e1a\u0e14\u0e35","\u0e28\u0e38\u0e01\u0e23\u0e4c","\u0e40\u0e2a\u0e32\u0e23\u0e4c"],"weekdaysShort":["\u0e2d\u0e32.","\u0e08.","\u0e2d.","\u0e1e.","\u0e1e\u0e24.","\u0e28.","\u0e2a."]},"colorpicker":{"choose":"\u0e15\u0e01\u0e25\u0e07"},"filter":{"group":{"all":"\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14"},"scopes":{"apply_button_text":"\u0e19\u0e33\u0e44\u0e1b\u0e43\u0e0a\u0e49","clear_button_text":"\u0e40\u0e04\u0e25\u0e35\u0e22\u0e23\u0e4c"},"dates":{"all":"\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14","filter_button_text":"\u0e01\u0e23\u0e2d\u0e07","reset_button_text":"\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32\u0e40\u0e23\u0e34\u0e48\u0e21\u0e15\u0e49\u0e19","date_placeholder":"\u0e27\u0e31\u0e19\u0e17\u0e35\u0e48","after_placeholder":"\u0e2b\u0e25\u0e31\u0e07\u0e08\u0e32\u0e01","before_placeholder":"\u0e01\u0e48\u0e2d\u0e19"},"numbers":{"all":"\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14","filter_button_text":"\u0e01\u0e23\u0e2d\u0e07","reset_button_text":"\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32\u0e40\u0e23\u0e34\u0e48\u0e21\u0e15\u0e49\u0e19","min_placeholder":"\u0e19\u0e49\u0e2d\u0e22\u0e17\u0e35\u0e48\u0e2a\u0e38\u0e14","max_placeholder":"\u0e21\u0e32\u0e01\u0e17\u0e35\u0e48\u0e2a\u0e38\u0e14"}},"eventlog":{"show_stacktrace":"\u0e41\u0e2a\u0e14\u0e07 stacktrace","hide_stacktrace":"\u0e0b\u0e48\u0e2d\u0e19 stacktrace","tabs":{"formatted":"\u0e08\u0e31\u0e14\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a\u0e41\u0e25\u0e49\u0e27","raw":"\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e14\u0e34\u0e1a"},"editor":{"title":"\u0e15\u0e31\u0e27\u0e41\u0e01\u0e49\u0e44\u0e02\u0e0b\u0e2d\u0e23\u0e4c\u0e2a\u0e42\u0e04\u0e49\u0e14","description":"Your operating system should be configured to listen to one of these URL schemes.","openWith":"\u0e40\u0e1b\u0e34\u0e14\u0e14\u0e49\u0e27\u0e22","remember_choice":"\u0e08\u0e33\u0e15\u0e31\u0e27\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e17\u0e35\u0e48\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e44\u0e27\u0e49","open":"\u0e40\u0e1b\u0e34\u0e14","cancel":"\u0e22\u0e01\u0e40\u0e25\u0e34\u0e01"}}} +); + +//! moment.js locale configuration v2.22.2 + +;(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' + && typeof require === 'function' ? factory(require('../moment')) : + typeof define === 'function' && define.amd ? define(['../moment'], factory) : + factory(global.moment) +}(this, (function (moment) { 'use strict'; + + + var th = moment.defineLocale('th', { + months : 'มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม'.split('_'), + monthsShort : 'ม.ค._ก.พ._มี.ค._เม.ย._พ.ค._มิ.ย._ก.ค._ส.ค._ก.ย._ต.ค._พ.ย._ธ.ค.'.split('_'), + monthsParseExact: true, + weekdays : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์'.split('_'), + weekdaysShort : 'อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์'.split('_'), // yes, three characters difference + weekdaysMin : 'อา._จ._อ._พ._พฤ._ศ._ส.'.split('_'), + weekdaysParseExact : true, + longDateFormat : { + LT : 'H:mm', + LTS : 'H:mm:ss', + L : 'DD/MM/YYYY', + LL : 'D MMMM YYYY', + LLL : 'D MMMM YYYY เวลา H:mm', + LLLL : 'วันddddที่ D MMMM YYYY เวลา H:mm' + }, + meridiemParse: /ก่อนเที่ยง|หลังเที่ยง/, + isPM: function (input) { + return input === 'หลังเที่ยง'; + }, + meridiem : function (hour, minute, isLower) { + if (hour < 12) { + return 'ก่อนเที่ยง'; + } else { + return 'หลังเที่ยง'; + } + }, + calendar : { + sameDay : '[วันนี้ เวลา] LT', + nextDay : '[พรุ่งนี้ เวลา] LT', + nextWeek : 'dddd[หน้า เวลา] LT', + lastDay : '[เมื่อวานนี้ เวลา] LT', + lastWeek : '[วัน]dddd[ที่แล้ว เวลา] LT', + sameElse : 'L' + }, + relativeTime : { + future : 'อีก %s', + past : '%sที่แล้ว', + s : 'ไม่กี่วินาที', + ss : '%d วินาที', + m : '1 นาที', + mm : '%d นาที', + h : '1 ชั่วโมง', + hh : '%d ชั่วโมง', + d : '1 วัน', + dd : '%d วัน', + M : '1 เดือน', + MM : '%d เดือน', + y : '1 ปี', + yy : '%d ปี' + } + }); + + return th; + +}))); + diff --git a/modules/system/assets/ui/docs/checkbox.md b/modules/system/assets/ui/docs/checkbox.md index a286b62e6..d4276d9cd 100644 --- a/modules/system/assets/ui/docs/checkbox.md +++ b/modules/system/assets/ui/docs/checkbox.md @@ -9,6 +9,44 @@ Allows a user to select from a small set of binary options.
+### Checkbox lists + +Allows a user to select from a list of binary options. + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+

What cars would you like in your garage?

+
+ + +

Do not send new comment notifications.

+
+
+ + +

Send new comment notifications only to post author.

+
+
+ + +

Notify all users who have permissions to receive blog notifications.

+
+
+
+
+ ### Radio
diff --git a/modules/system/assets/ui/docs/select.md b/modules/system/assets/ui/docs/select.md index e8f18c6bd..871698389 100644 --- a/modules/system/assets/ui/docs/select.md +++ b/modules/system/assets/ui/docs/select.md @@ -68,10 +68,71 @@ Use the `data-handler` attribute to source the select options from an AJAX handl data-minimum-input-length="2" data-ajax--delay="300" data-request-data="foo: 'bar'" - > +> ``` -The AJAX handler should return results as an array. +The AJAX handler should return results in the [Select2 data format](https://select2.org/data-sources/formats). + +```php +public function onGetOptions() +{ + return [ + 'results' => [ + [ + 'id' => 1, + 'text' => 'Foo' + ], + [ + 'id' => 2, + 'text' => 'Bar' + ] + ... + ] + ]; +} +``` + +Or a more full-featured example: + +```php +public function onGetOptions() +{ + return [ + 'results' => [ + [ + 'id' => 1, + 'text' => 'Foo', + 'disabled' => true + ], + [ + 'id' => 2, + 'text' => 'Bar', + 'selected' => true + ], + [ + 'text' => 'Group', + 'children' => [ + [ + 'id' => 3, + 'text' => 'Child 1' + ], + [ + 'id' => 4, + 'text' => 'Child 2' + ] + ... + ] + ] + ... + ], + 'pagination' => [ + 'more' => true + ] + ]; +} +``` + +The results array can be assigned to either the `result` or `results` key. As an alternative to the Select2 format, results can also be provided as an associative array (also assigned to either key). Due to the fact that JavaScript does not guarantee the order of object properties, we suggest the method above for defining results. ```php public function onGetOptions() diff --git a/modules/system/assets/ui/js/autocomplete.js b/modules/system/assets/ui/js/autocomplete.js index 59d2b9689..1203ff46c 100644 --- a/modules/system/assets/ui/js/autocomplete.js +++ b/modules/system/assets/ui/js/autocomplete.js @@ -1,6 +1,6 @@ /* * The autcomplete plugin, a forked version of Bootstrap's original typeahead plugin. - * + * * Data attributes: * - data-control="autocomplete" - enables the autocomplete plugin * @@ -233,19 +233,19 @@ move: function (e) { if (!this.shown) return - switch(e.keyCode) { - case 9: // tab - case 13: // enter - case 27: // escape + switch(e.key) { + case 'Tab': + case 'Enter': + case 'Escape': e.preventDefault() break - case 38: // up arrow + case 'ArrowUp': e.preventDefault() this.prev() break - case 40: // down arrow + case 'ArrowDown': e.preventDefault() this.next() break @@ -255,7 +255,7 @@ }, keydown: function (e) { - this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]) + this.suppressKeyPressRepeat = ~$.inArray(e.key, ['ArrowDown','ArrowUp','Tab','Enter','Escape']) this.move(e) }, @@ -378,7 +378,7 @@ if (typeof value == 'object') return value try { - return JSON.parse(JSON.stringify(eval("({" + value + "})"))) + return ocJSON("{" + value + "}") } catch (e) { throw new Error('Error parsing the '+name+' attribute value. '+e) diff --git a/modules/system/assets/ui/js/chart.line.js b/modules/system/assets/ui/js/chart.line.js index f89b412fa..208b0e51d 100644 --- a/modules/system/assets/ui/js/chart.line.js +++ b/modules/system/assets/ui/js/chart.line.js @@ -39,7 +39,7 @@ tickLength: 5 }, selection: { mode: "x" }, - grid: { + grid: { markingsColor: "rgba(0,0,0, 0.02)", backgroundColor: { colors: ["#fff", "#fff"] }, borderColor: "#7bafcc", @@ -80,7 +80,7 @@ var parsedOptions = {} try { - parsedOptions = JSON.parse(JSON.stringify(eval("({" + options.chartOptions + "})"))); + parsedOptions = ocJSON("{" + options.chartOptions + "}"); } catch (e) { throw new Error('Error parsing the data-chart-options attribute value. '+e); } @@ -102,15 +102,15 @@ if (this.options.zoomable) { this.$el.on("plotselected", function (event, ranges) { - var newCoords = { - xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to } + var newCoords = { + xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to } } - + $.plot(self.$el, self.fullDataSet, $.extend(true, {}, self.chartOptions, newCoords)) self.resetZoomLink.show() }); } - + /* * Markings Helper */ @@ -121,14 +121,14 @@ function weekendAreas(axes) { var markings = [], d = new Date(axes.xaxis.min); - + // Go to the first Saturday d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7)) d.setUTCSeconds(0) d.setUTCMinutes(0) d.setUTCHours(0) var i = d.getTime() - + do { // When we don't set yaxis, the rectangle automatically // extends to infinity upwards and downwards @@ -178,7 +178,7 @@ } /* - * Adds a data set to the chart. + * Adds a data set to the chart. * See https://github.com/flot/flot/blob/master/API.md#data-format for the list * of supported data set options. */ diff --git a/modules/system/assets/ui/js/checkbox.js b/modules/system/assets/ui/js/checkbox.js index f47bd7130..fc61d6f91 100644 --- a/modules/system/assets/ui/js/checkbox.js +++ b/modules/system/assets/ui/js/checkbox.js @@ -5,14 +5,9 @@ (function($) { - $(document).on('keydown', 'div.custom-checkbox', function(e) { - if (e.keyCode == 32) - e.preventDefault() - }) - - $(document).on('input', 'div.custom-checkbox', function(e) { - if (e.keyCode == 32) { - var $cb = $('input', this) + $(document).on('keypress', 'div.custom-checkbox', function(e) { + if (e.key === '(Space character)' || e.key === 'Spacebar' || e.key === ' ') { + var $cb = $('input[type=checkbox]', this) if ($cb.data('oc-space-timestamp') == e.timeStamp) return @@ -88,4 +83,4 @@ return false }) -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/modules/system/assets/ui/js/filter.js b/modules/system/assets/ui/js/filter.js index b6251fbef..543575e9a 100644 --- a/modules/system/assets/ui/js/filter.js +++ b/modules/system/assets/ui/js/filter.js @@ -19,7 +19,6 @@ +function ($) { "use strict"; var FilterWidget = function (element, options) { - this.$el = $(element); this.options = options || {} @@ -28,6 +27,12 @@ this.activeScopeName = null this.isActiveScopeDirty = false + /* + * Throttle dependency updating + */ + this.dependantUpdateInterval = 300 + this.dependantUpdateTimers = {} + this.init() } @@ -89,6 +94,9 @@ FilterWidget.prototype.init = function() { var self = this + this.bindDependants() + + // Setup event handler on type: checkbox scopes this.$el.on('change', '.filter-scope input[type="checkbox"]', function(){ var $scope = $(this).closest('.filter-scope') @@ -100,12 +108,14 @@ } }) + // Apply classes to type: checkbox scopes that are active from the server $('.filter-scope input[type="checkbox"]', this.$el).each(function() { $(this) .closest('.filter-scope') .toggleClass('active', $(this).is(':checked')) }) + // Setup click handler on type: group scopes this.$el.on('click', 'a.filter-scope', function(){ var $scope = $(this), scopeName = $scope.data('scope-name') @@ -120,6 +130,7 @@ $scope.addClass('filter-scope-open') }) + // Setup event handlers on type: group scopes' controls this.$el.on('show.oc.popover', 'a.filter-scope', function(event){ self.focusSearch() @@ -144,9 +155,9 @@ e.preventDefault() self.filterScope(true) }) - }) + // Setup event handler to apply selected options when closing the type: group scope popup this.$el.on('hide.oc.popover', 'a.filter-scope', function(){ var $scope = $(this) self.pushOptions(self.activeScopeName) @@ -158,6 +169,81 @@ }) } + /* + * Bind dependant scopes + */ + FilterWidget.prototype.bindDependants = function() { + if (!$('[data-scope-depends]', this.$el).length) { + return; + } + + var self = this, + scopeMap = {}, + scopeElements = this.$el.find('.filter-scope') + + /* + * Map master and slave scope + */ + scopeElements.filter('[data-scope-depends]').each(function() { + var name = $(this).data('scope-name'), + depends = $(this).data('scope-depends') + + $.each(depends, function(index, depend){ + if (!scopeMap[depend]) { + scopeMap[depend] = { scopes: [] } + } + + scopeMap[depend].scopes.push(name) + }) + }) + + /* + * When a master is updated, refresh its slaves + */ + $.each(scopeMap, function(scopeName, toRefresh){ + scopeElements.filter('[data-scope-name="'+scopeName+'"]') + .on('change.oc.filterScope', $.proxy(self.onRefreshDependants, self, scopeName, toRefresh)) + }) + } + + /* + * Refresh a dependancy scope + * Uses a throttle to prevent duplicate calls and click spamming + */ + FilterWidget.prototype.onRefreshDependants = function(scopeName, toRefresh) { + var self = this, + scopeElements = this.$el.find('.filter-scope') + + if (this.dependantUpdateTimers[scopeName] !== undefined) { + window.clearTimeout(this.dependantUpdateTimers[scopeName]) + } + + this.dependantUpdateTimers[scopeName] = window.setTimeout(function() { + $.each(toRefresh.scopes, function (index, dependantScope) { + self.scopeValues[dependantScope] = null + var $scope = self.$el.find('[data-scope-name="'+dependantScope+'"]') + + /* + * Request options from server + */ + self.$el.request(self.options.optionsHandler, { + data: { scopeName: dependantScope }, + success: function(data) { + self.fillOptions(dependantScope, data.options) + self.updateScopeSetting($scope, data.options.active.length) + $scope.loadIndicator('hide') + } + }) + }) + }, this.dependantUpdateInterval) + + $.each(toRefresh.scopes, function(index, scope) { + scopeElements.filter('[data-scope-name="'+scope+'"]') + .addClass('loading-indicator-container') + .loadIndicator() + }) + } + FilterWidget.prototype.focusSearch = function() { if (Modernizr.touchevents) return @@ -369,7 +455,7 @@ var items = $('#controlFilterPopover .filter-active-items > ul'), buttonContainer = $('#controlFilterPopover .filter-buttons') - if(data) { + if (data) { data.active.length > 0 ? buttonContainer.show() : buttonContainer.hide() } else { items.children().length > 0 ? buttonContainer.show() : buttonContainer.hide() @@ -383,16 +469,21 @@ if (!this.isActiveScopeDirty || !this.options.updateHandler) return - var data = { + var self = this, + data = { scopeName: scopeName, options: this.scopeValues[scopeName] } $.oc.stripeLoadIndicator.show() + this.$el.request(this.options.updateHandler, { data: data - }).always(function(){ + }).always(function () { $.oc.stripeLoadIndicator.hide() + }).done(function () { + // Trigger dependsOn updates on successful requests + self.$el.find('[data-scope-name="'+scopeName+'"]').trigger('change.oc.filterScope') }) } diff --git a/modules/system/assets/ui/js/input.hotkey.js b/modules/system/assets/ui/js/input.hotkey.js index 5f58cc148..53f47e0bc 100644 --- a/modules/system/assets/ui/js/input.hotkey.js +++ b/modules/system/assets/ui/js/input.hotkey.js @@ -14,8 +14,9 @@ BaseProto = Base.prototype var HotKey = function (element, options) { - if (!options.hotkey) + if (!options.hotkey) { throw new Error('No hotkey has been defined.'); + } this.$el = $(element) this.$target = $(options.hotkeyTarget) @@ -34,8 +35,9 @@ HotKey.prototype.constructor = HotKey HotKey.prototype.dispose = function() { - if (this.$el === null) + if (this.$el === null) { return + } this.unregisterHandlers() @@ -94,8 +96,9 @@ condition.specific = this.keyMap[keys[keys.length-1]] - if (typeof (condition.specific) == 'undefined') + if (typeof (condition.specific) == 'undefined') { condition.specific = keys[keys.length-1].toUpperCase().charCodeAt() + } return condition } @@ -149,11 +152,11 @@ for (var i = 0, len = this.keyConditions.length; i < len; i++) { var condition = this.keyConditions[i] - if (ev.which == condition.specific - && ev.originalEvent.shiftKey == condition.shift - && ev.originalEvent.ctrlKey == condition.ctrl - && ev.originalEvent.metaKey == condition.cmd - && ev.originalEvent.altKey == condition.alt) { + if (ev.which === condition.specific + && ev.originalEvent.shiftKey === condition.shift + && ev.originalEvent.ctrlKey === condition.ctrl + && ev.originalEvent.metaKey === condition.cmd + && ev.originalEvent.altKey === condition.alt) { return true } } @@ -163,11 +166,13 @@ HotKey.prototype.onKeyDown = function(ev) { if (this.testConditions(ev)) { - if (this.options.hotkeyVisible && !this.$el.is(':visible')) + if (this.options.hotkeyVisible && !this.$el.is(':visible')) { return + } - if (this.options.callback) + if (this.options.callback) { return this.options.callback(this.$el, ev.currentTarget, ev) + } } } diff --git a/modules/system/assets/ui/js/inspector.datainteraction.js b/modules/system/assets/ui/js/inspector.datainteraction.js index 90de00a5a..f9bfbba39 100644 --- a/modules/system/assets/ui/js/inspector.datainteraction.js +++ b/modules/system/assets/ui/js/inspector.datainteraction.js @@ -52,7 +52,7 @@ var valuesStr = $.trim(valuesField.value) try { - return valuesStr.length === 0 ? {} : $.parseJSON(valuesStr) + return valuesStr.length === 0 ? {} : JSON.parse(valuesStr) } catch (err) { throw new Error('Error parsing Inspector field values. ' + err) @@ -134,7 +134,7 @@ } try { - return $.parseJSON(configuration) + return JSON.parse(configuration) } catch(err) { throw new Error('Error parsing Inspector configuration. ' + err) diff --git a/modules/system/assets/ui/js/inspector.editor.dictionary.js b/modules/system/assets/ui/js/inspector.editor.dictionary.js index 755c80e3d..2ff645b08 100644 --- a/modules/system/assets/ui/js/inspector.editor.dictionary.js +++ b/modules/system/assets/ui/js/inspector.editor.dictionary.js @@ -438,10 +438,10 @@ } DictionaryEditor.prototype.onKeyDown = function(ev) { - if (ev.keyCode == 40) { + if (ev.key === 'ArrowDown') { return this.navigateDown(ev) } - else if (ev.keyCode == 38) { + else if (ev.key === 'ArrowUp') { return this.navigateUp(ev) } } diff --git a/modules/system/assets/ui/js/inspector.editor.objectlist.js b/modules/system/assets/ui/js/inspector.editor.objectlist.js index e116fdd3b..a8ed51d01 100644 --- a/modules/system/assets/ui/js/inspector.editor.objectlist.js +++ b/modules/system/assets/ui/js/inspector.editor.objectlist.js @@ -297,7 +297,7 @@ } var properties = this.propertyDefinition.itemProperties, - values = $.parseJSON(dataStr), + values = JSON.parse(dataStr), options = { enableExternalParameterEditor: false, onChange: this.proxy(this.onInspectorDataChange), @@ -447,7 +447,7 @@ for (var i = 0, len = dataRows.length; i < len; i++) { var dataRow = dataRows[i], - rowData = $.parseJSON(dataRow.getAttribute('data-inspector-values')) + rowData = JSON.parse(dataRow.getAttribute('data-inspector-values')) if (!this.isKeyValueMode()) { result.push(rowData) @@ -500,7 +500,7 @@ for (var i = 0, len = dataRows.length; i < len; i++) { var dataRow = dataRows[i], - rowData = $.parseJSON(dataRow.getAttribute('data-inspector-values')) + rowData = JSON.parse(dataRow.getAttribute('data-inspector-values')) if (selectedRow == dataRow) { continue diff --git a/modules/system/assets/ui/js/inspector.editor.stringlistautocomplete.js b/modules/system/assets/ui/js/inspector.editor.stringlistautocomplete.js index a48326b61..bf960b136 100644 --- a/modules/system/assets/ui/js/inspector.editor.stringlistautocomplete.js +++ b/modules/system/assets/ui/js/inspector.editor.stringlistautocomplete.js @@ -537,10 +537,10 @@ } StringListAutocomplete.prototype.onKeyDown = function(ev) { - if (ev.keyCode == 40) { + if (ev.key === 'ArrowDown') { return this.navigateDown(ev) } - else if (ev.keyCode == 38) { + else if (ev.key === 'ArrowUp') { return this.navigateUp(ev) } } diff --git a/modules/system/assets/ui/js/inspector.wrapper.base.js b/modules/system/assets/ui/js/inspector.wrapper.base.js index 4b69a358f..ed80fa92e 100644 --- a/modules/system/assets/ui/js/inspector.wrapper.base.js +++ b/modules/system/assets/ui/js/inspector.wrapper.base.js @@ -168,7 +168,7 @@ var valuesStr = $.trim($valuesField.val()) try { - return valuesStr.length === 0 ? {} : $.parseJSON(valuesStr) + return valuesStr.length === 0 ? {} : JSON.parse(valuesStr) } catch (err) { throw new Error('Error parsing Inspector field values. ' + err) @@ -338,7 +338,7 @@ } try { - return $.parseJSON(configuration) + return JSON.parse(configuration) } catch(err) { throw new Error('Error parsing Inspector configuration. ' + err) diff --git a/modules/system/assets/ui/js/inspector.wrapper.popup.js b/modules/system/assets/ui/js/inspector.wrapper.popup.js index 09d85799f..4a4c32bab 100644 --- a/modules/system/assets/ui/js/inspector.wrapper.popup.js +++ b/modules/system/assets/ui/js/inspector.wrapper.popup.js @@ -188,7 +188,7 @@ } InspectorPopup.prototype.onPopoverKeyDown = function(ev) { - if(ev.keyCode == 13) { + if(ev.key === 'Enter') { $(ev.currentTarget).trigger('close.oc.popover') } } diff --git a/modules/system/assets/ui/js/list.rowlink.js b/modules/system/assets/ui/js/list.rowlink.js index de8add4c3..4e395912a 100644 --- a/modules/system/assets/ui/js/list.rowlink.js +++ b/modules/system/assets/ui/js/list.rowlink.js @@ -40,7 +40,7 @@ popup = link.is('[data-control=popup]'), request = link.is('[data-request]') - $(this).find('td').not('.' + options.excludeClass).click(function(e) { + function handleClick(e) { if ($(document.body).hasClass('drag')) { return } @@ -60,12 +60,25 @@ else { window.location = href } + } + + $(this).find('td').not('.' + options.excludeClass).click(function(e) { + handleClick(e) + }) + + $(this).on('keypress', function(e) { + if (e.key === '(Space character)' || e.key === 'Spacebar' || e.key === ' ') { + handleClick(e) + return false + } }) $(this).addClass(options.linkedClass) link.hide().after(link.html()) }) + // Add Keyboard Navigation to list rows + $('tr.rowlink').attr('tabindex', 0) } RowLink.DEFAULTS = { diff --git a/modules/system/assets/ui/js/popover.js b/modules/system/assets/ui/js/popover.js index e001e140e..1c92e3114 100644 --- a/modules/system/assets/ui/js/popover.js +++ b/modules/system/assets/ui/js/popover.js @@ -155,7 +155,7 @@ return false } - if (e.keyCode == 27) { + if (e.key === 'Escape') { self.hide() return false } diff --git a/modules/system/assets/ui/js/popup.js b/modules/system/assets/ui/js/popup.js index 2ba8f348e..bf3b05e40 100644 --- a/modules/system/assets/ui/js/popup.js +++ b/modules/system/assets/ui/js/popup.js @@ -395,7 +395,7 @@ if (typeof value == 'object') return value try { - return JSON.parse(JSON.stringify(eval("({" + value + "})"))) + return ocJSON("{" + value + "}") } catch (e) { throw new Error('Error parsing the '+name+' attribute value. '+e) diff --git a/modules/system/assets/ui/js/select.js b/modules/system/assets/ui/js/select.js index 6b4d4b2ea..a7c6c5190 100644 --- a/modules/system/assets/ui/js/select.js +++ b/modules/system/assets/ui/js/select.js @@ -86,7 +86,28 @@ return $request }, + processResults: function (data, params) { + var results = data.result || data.results, + options = [] + delete(data.result) + if (results[0] && typeof(results[0]) === 'object') { // Pass through Select2 format + options = results + } + else { // Key-value map + for (var i in results) { + if (results.hasOwnProperty(i)) { + options.push({ + id: i, + text: results[i], + }) + } + } + } + + data.results = options + return data + }, dataType: 'json' } } diff --git a/modules/system/assets/ui/js/tab.js b/modules/system/assets/ui/js/tab.js index 97efc18c1..89989d4e7 100644 --- a/modules/system/assets/ui/js/tab.js +++ b/modules/system/assets/ui/js/tab.js @@ -40,7 +40,7 @@ }) this.$el.on('mousedown', "li[data-tab-id]", function (ev) { - if (ev.which === 2) { + if (ev.key === '2') { $(ev.target).trigger('close.oc.tab'); } }) @@ -102,12 +102,14 @@ if (!$anchor.attr('title')) $anchor.attr('title', $anchor.text()) - var html = $anchor.html() - - $anchor.html('') - $anchor - .append($('') - .append($('').html(html))) + // Setup the required tabs markup if it does not exist already. + if ($anchor.find('> span.title > span').length < 1) { + var html = $anchor.html() + $anchor + .html('') + .append($('') + .append($('').html(html))) + } var pane = $('> .tab-pane', this.$pagesContainer).eq(tabIndex).attr('id', targetId) diff --git a/modules/system/assets/ui/less/checkbox.less b/modules/system/assets/ui/less/checkbox.less index fd99b6a64..01c9582df 100644 --- a/modules/system/assets/ui/less/checkbox.less +++ b/modules/system/assets/ui/less/checkbox.less @@ -188,6 +188,17 @@ } } } + + &.radio-field { + > label { + display: block; + } + + .custom-radio { + display: inline-block; + margin-bottom: 0; + } + } } // diff --git a/modules/system/assets/ui/less/filter.less b/modules/system/assets/ui/less/filter.less index 071ed0658..3d591af83 100644 --- a/modules/system/assets/ui/less/filter.less +++ b/modules/system/assets/ui/less/filter.less @@ -55,6 +55,27 @@ .transition(color 0.6s); } + &.loading-indicator-container.in-progress { + pointer-events: none; + cursor: default; + + .loading-indicator { + background: transparent; + + > span { + left: unset; + right: 0; + top: 10px; + background-color: @color-filter-bg; + border-radius: 50%; + margin-top: 0; + width: 20px; + height: 20px; + background-size: 15px 15px; + } + } + } + &:after { font-size: 14px; .icon(@angle-down); diff --git a/modules/system/assets/ui/less/form.less b/modules/system/assets/ui/less/form.less index 38503f80e..075b2fc0a 100644 --- a/modules/system/assets/ui/less/form.less +++ b/modules/system/assets/ui/less/form.less @@ -124,7 +124,8 @@ .box-sizing(border-box); &.is-required { - > label:after { + > label:not(.custom-switch):after, + > .field-switch > label:after { background-color: @color-form-required-asterisk; width: 5px; height: 5px; @@ -154,6 +155,18 @@ clear: right; } + &.clear-full { + clear: both; + } + + &.clear-left { + clear: left; + } + + &.clear-right { + clear: right; + } + &.layout-relative { padding-bottom: 0; } @@ -315,23 +328,47 @@ &.size-giant { min-height: @size-giant; } } -.field-checkboxlist:not(.is-scrollable) { - padding: 20px 20px 2px 20px; - .border-radius(@border-radius-base); - background: @color-form-checkboxlist-background; - border: 1px solid @color-form-checkboxlist-border; +.field-checkboxlist { + &:not(.is-scrollable) { + .border-radius(@border-radius-base); + background: @color-form-checkboxlist-background; + border: 1px solid @color-form-checkboxlist-border; - //.checkbox:last-of-type { - // margin-bottom: 0; - //} -} + .field-checkboxlist-inner { + padding: 20px 20px 2px 20px; + } + } -.field-checkboxlist.is-scrollable { - small { - color: @text-muted; + .checkboxlist-controls { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-align: baseline; + -ms-flex-align: baseline; + align-items: baseline; + > div { + padding-bottom: 7px; + + > a { + font-size: 13px; + margin-right: 20px; + text-decoration: none; + > i { + color: @text-muted; + margin: 0 4px; + } + + &:hover > i { + color: @color-label; + } + } + } } } + .field-checkboxlist-scrollable { background: @color-form-checkboxlist-background; border: 1px solid @color-form-checkboxlist-border; diff --git a/modules/system/assets/ui/less/icon.mixins.less b/modules/system/assets/ui/less/icon.mixins.less index 50529d55c..a504ca6ef 100644 --- a/modules/system/assets/ui/less/icon.mixins.less +++ b/modules/system/assets/ui/less/icon.mixins.less @@ -12,7 +12,6 @@ font-style: normal; text-decoration: inherit; -webkit-font-smoothing: antialiased; - *margin-right: .3em; // fixes ie7 issues } .icon-stack(@width: 2em, @height: 2em, @top-font-size: 1em, @base-font-size: 2em) { diff --git a/modules/system/assets/ui/less/list.less b/modules/system/assets/ui/less/list.less index 605ec1416..30472da58 100644 --- a/modules/system/assets/ui/less/list.less +++ b/modules/system/assets/ui/less/list.less @@ -597,4 +597,9 @@ table.table.data { > .list-scrollable { overflow: hidden; } + + &.scroll-after th a, + &.scroll-before th a { + cursor: grab; + } } diff --git a/modules/system/assets/ui/less/select.less b/modules/system/assets/ui/less/select.less index fe595434a..3d7d86369 100644 --- a/modules/system/assets/ui/less/select.less +++ b/modules/system/assets/ui/less/select.less @@ -588,4 +588,4 @@ .form-inline .select2-container--default { display: inline-block; -} \ No newline at end of file +} diff --git a/modules/system/assets/ui/less/site.normalize.less b/modules/system/assets/ui/less/site.normalize.less index 3606c3e0c..d40b1a822 100644 --- a/modules/system/assets/ui/less/site.normalize.less +++ b/modules/system/assets/ui/less/site.normalize.less @@ -416,11 +416,9 @@ table { border-collapse: collapse; border-spacing: 0; table-layout: auto; - word-wrap: break-word; - word-break: break-all; } td, th { padding: 0; -} \ No newline at end of file +} diff --git a/modules/system/assets/ui/less/tab.less b/modules/system/assets/ui/less/tab.less index 90fae3ee6..8ceeaee54 100644 --- a/modules/system/assets/ui/less/tab.less +++ b/modules/system/assets/ui/less/tab.less @@ -112,7 +112,7 @@ padding-top: 7px; > span:not([class*="oc-icon"]) { margin-right: 8px; - } + } } } diff --git a/modules/system/assets/ui/less/taglist.less b/modules/system/assets/ui/less/taglist.less new file mode 100644 index 000000000..da7621292 --- /dev/null +++ b/modules/system/assets/ui/less/taglist.less @@ -0,0 +1,28 @@ +// +// Dependencies +// -------------------------------------------------- + +@import "global.variables.less"; +@import "select.variables.less"; + + +// Taglist preview mode +//------------------------------------ + +.taglist--preview { + overflow: hidden; + list-style-type: none; + padding-top: 0; padding-left: 0; + margin: 0; + + .taglist__item { + color: @color-select-text; + background: @color-select-choice-bg; + border: 1px solid @color-select-choice-border; + border-radius: 4px; + cursor: default; + float: left; + margin: @padding-base-vertical 0 0 @padding-base-horizontal/2; + padding: 0 (@padding-base-vertical - 2); + } +} diff --git a/modules/system/assets/ui/storm-min.js b/modules/system/assets/ui/storm-min.js index e06d6ccf8..23f2c4ebe 100644 --- a/modules/system/assets/ui/storm-min.js +++ b/modules/system/assets/ui/storm-min.js @@ -1,16 +1,18 @@ -(function(global,factory){if(typeof exports==="object"&&exports){factory(exports);}else if(typeof define==="function"&&define.amd){define(['exports'],factory);}else{factory(global.Mustache={});}}(this,function(mustache){var Object_toString=Object.prototype.toString;var isArray=Array.isArray||function(object){return Object_toString.call(object)==='[object Array]';};function isFunction(object){return typeof object==='function';} -function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");} -var RegExp_test=RegExp.prototype.test;function testRegExp(re,string){return RegExp_test.call(re,string);} +(function defineMustache(global,factory){if(typeof exports==='object'&&exports&&typeof exports.nodeName!=='string'){factory(exports);}else if(typeof define==='function'&&define.amd){define(['exports'],factory);}else{global.Mustache={};factory(global.Mustache);}}(this,function mustacheFactory(mustache){var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==='[object Array]';};function isFunction(object){return typeof object==='function';} +function typeStr(obj){return isArray(obj)?'array':typeof obj;} +function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,'\\$&');} +function hasProperty(obj,propName){return obj!=null&&typeof obj==='object'&&(propName in obj);} +var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string);} var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string);} -var entityMap={"&":"&","<":"<",">":">",'"':'"',"'":''',"/":'/'};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s];});} +var entityMap={'&':'&','<':'<','>':'>','"':'"',"'":''','/':'/','`':'`','=':'='};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s];});} var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template) return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length) delete tokens[spaces.pop()];}else{spaces=[];} hasTag=false;nonSpace=false;} -var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==='string') -tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2) -throw new Error('Invalid tags: '+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+'\\s*');closingTagRe=new RegExp('\\s*'+escapeRegExp(tags[1]));closingCurlyRe=new RegExp('\\s*'+escapeRegExp('}'+tags[1]));} +var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==='string') +tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2) +throw new Error('Invalid tags: '+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+'\\s*');closingTagRe=new RegExp('\\s*'+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp('\\s*'+escapeRegExp('}'+tagsToCompile[1]));} compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token);}} return nestedTokens;} function Scanner(string){this.string=string;this.tail=string;this.pos=0;} -Scanner.prototype.eos=function(){return this.tail==="";};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0) -return'';var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string;};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index);} +Scanner.prototype.eos=function eos(){return this.tail==='';};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0) +return'';var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string;};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail='';break;case 0:match='';break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index);} this.pos+=match.length;return match;};function Context(view,parentContext){this.view=view;this.cache={'.':this.view};this.parent=parentContext;} -Context.prototype.push=function(view){return new Context(view,this);};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name];}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf('.')>0){value=context.view;names=name.split('.');index=0;while(value!=null&&index0){value=context.view;names=name.split('.');index=0;while(value!=null&&index')value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==='&')value=this._unescapedValue(token,context);else if(symbol==='name')value=this._escapedValue(token,context);else if(symbol==='text')value=this._rawValue(token);if(value!==undefined) +Writer.prototype.clearCache=function clearCache(){this.cache={};};Writer.prototype.parse=function parse(template,tags){var cache=this.cache;var tokens=cache[template];if(tokens==null) +tokens=cache[template]=parseTemplate(template,tags);return tokens;};Writer.prototype.render=function render(template,view,partials){var tokens=this.parse(template);var context=(view instanceof Context)?view:new Context(view);return this.renderTokens(tokens,context,partials,template);};Writer.prototype.renderTokens=function renderTokens(tokens,context,partials,originalTemplate){var buffer='';var token,symbol,value;for(var i=0,numTokens=tokens.length;i')value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==='&')value=this.unescapedValue(token,context);else if(symbol==='name')value=this.escapedValue(token,context);else if(symbol==='text')value=this.rawValue(token);if(value!==undefined) buffer+=value;} -return buffer;};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer='';var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials);} +return buffer;};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer='';var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials);} if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;jf;f++)if(m=e[f],g=G.style[m],s(m,"-")&&(m=d(m)),G.style[m]!==n){if(a||r(o,"undefined"))return c(),"pfx"==t?m:!0;try{G.style[m]=o}catch(y){}if(G.style[m]!=g)return c(),"pfx"==t?m:!0}return c(),!1}function y(e,t,n,o,a){var i=e.charAt(0).toUpperCase()+e.slice(1),s=(e+" "+U.join(i+" ")+i).split(" ");return r(t,"string")||r(t,"undefined")?v(s,t,o,a):(s=(e+" "+O.join(i+" ")+i).split(" "),p(s,t,n))}function b(e,t,r){return y(e,n,n,t,r)}function T(e,t){var n=e.deleteDatabase(t);n.onsuccess=function(){u("indexeddb.deletedatabase",!0)},n.onerror=function(){u("indexeddb.deletedatabase",!1)}}var x=[],w=[],S={_version:"3.6.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){w.push({name:e,fn:t,options:n})},addAsyncTest:function(e){w.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=S,Modernizr=new Modernizr,Modernizr.addTest("applicationcache","applicationCache"in e),Modernizr.addTest("geolocation","geolocation"in navigator),Modernizr.addTest("history",function(){var t=navigator.userAgent;return-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone")||"file:"===location.protocol?e.history&&"pushState"in e.history:!1}),Modernizr.addTest("postmessage","postMessage"in e),Modernizr.addTest("svg",!!t.createElementNS&&!!t.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect);var C=!1;try{C="WebSocket"in e&&2===e.WebSocket.CLOSING}catch(E){}Modernizr.addTest("websockets",C),Modernizr.addTest("localstorage",function(){var e="modernizr";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("sessionstorage",function(){var e="modernizr";try{return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("websqldatabase","openDatabase"in e),Modernizr.addTest("webworkers","Worker"in e);var _=S._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):["",""];S._prefixes=_;var k=t.documentElement,P="svg"===k.nodeName.toLowerCase();P||!function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=b.elements;return"string"==typeof e?e.split(" "):e}function o(e,t){var n=b.elements;"string"!=typeof n&&(n=n.join(" ")),"string"!=typeof e&&(e=e.join(" ")),b.elements=n+" "+e,l(t)}function a(e){var t=y[e[h]];return t||(t={},v++,e[h]=v,y[v]=t),t}function i(e,n,r){if(n||(n=t),u)return n.createElement(e);r||(r=a(n));var o;return o=r.cache[e]?r.cache[e].cloneNode():g.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!o.canHaveChildren||m.test(e)||o.tagUrn?o:r.frag.appendChild(o)}function s(e,n){if(e||(e=t),u)return e.createDocumentFragment();n=n||a(e);for(var o=n.frag.cloneNode(),i=0,s=r(),c=s.length;c>i;i++)o.createElement(s[i]);return o}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return b.shivMethods?i(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-:]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(b,t.frag)}function l(e){e||(e=t);var r=a(e);return!b.shivCSS||d||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),u||c(e,r),e}var d,u,f="3.7.3",p=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,g=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",v=0,y={};!function(){try{var e=t.createElement("a");e.innerHTML="",d="hidden"in e,u=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){d=!0,u=!0}}();var b={elements:p.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:f,shivCSS:p.shivCSS!==!1,supportsUnknownElements:u,shivMethods:p.shivMethods!==!1,type:"default",shivDocument:l,createElement:i,createDocumentFragment:s,addElements:o};e.html5=b,l(t),"object"==typeof module&&module.exports&&(module.exports=b)}("undefined"!=typeof e?e:this,t);var N="Moz O ms Webkit",O=S._config.usePrefixes?N.toLowerCase().split(" "):[];S._domPrefixes=O;var z=function(){function e(e,t){var o;return e?(t&&"string"!=typeof t||(t=i(t||"div")),e="on"+e,o=e in t,!o&&r&&(t.setAttribute||(t=i("div")),t.setAttribute(e,""),o="function"==typeof t[e],t[e]!==n&&(t[e]=n),t.removeAttribute(e)),o):!1}var r=!("onblur"in t.documentElement);return e}();S.hasEvent=z,Modernizr.addTest("hashchange",function(){return z("hashchange",e)===!1?!1:t.documentMode===n||t.documentMode>7}),Modernizr.addTest("audio",function(){var e=i("audio"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),t.mp3=e.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/,""),t.opus=e.canPlayType('audio/ogg; codecs="opus"')||e.canPlayType('audio/webm; codecs="opus"').replace(/^no$/,""),t.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),t.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("canvas",function(){var e=i("canvas");return!(!e.getContext||!e.getContext("2d"))}),Modernizr.addTest("canvastext",function(){return Modernizr.canvas===!1?!1:"function"==typeof i("canvas").getContext("2d").fillText}),Modernizr.addTest("video",function(){var e=i("video"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),t.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),t.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""),t.vp9=e.canPlayType('video/webm; codecs="vp9"').replace(/^no$/,""),t.hls=e.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("webgl",function(){var t=i("canvas"),n="probablySupportsContext"in t?"probablySupportsContext":"supportsContext";return n in t?t[n]("webgl")||t[n]("experimental-webgl"):"WebGLRenderingContext"in e}),Modernizr.addTest("cssgradients",function(){for(var e,t="background-image:",n="gradient(linear,left top,right bottom,from(#9f9),to(white));",r="",o=0,a=_.length-1;a>o;o++)e=0===o?"to ":"",r+=t+_[o]+"linear-gradient("+e+"left top, #9f9, white);";Modernizr._config.usePrefixes&&(r+=t+"-webkit-"+n);var s=i("a"),c=s.style;return c.cssText=r,(""+c.backgroundImage).indexOf("gradient")>-1}),Modernizr.addTest("multiplebgs",function(){var e=i("a").style;return e.cssText="background:url(https://),url(https://),red url(https://)",/(url\s*\(.*?){3}/.test(e.background)}),Modernizr.addTest("opacity",function(){var e=i("a").style;return e.cssText=_.join("opacity:.55;"),/^0.55$/.test(e.opacity)}),Modernizr.addTest("rgba",function(){var e=i("a").style;return e.cssText="background-color:rgba(150,255,150,.5)",(""+e.backgroundColor).indexOf("rgba")>-1}),Modernizr.addTest("inlinesvg",function(){var e=i("div");return e.innerHTML="","http://www.w3.org/2000/svg"==("undefined"!=typeof SVGRect&&e.firstChild&&e.firstChild.namespaceURI)});var R=i("input"),A="autocomplete autofocus list placeholder max min multiple pattern required step".split(" "),M={};Modernizr.input=function(t){for(var n=0,r=t.length;r>n;n++)M[t[n]]=!!(t[n]in R);return M.list&&(M.list=!(!i("datalist")||!e.HTMLDataListElement)),M}(A);var $="search tel url email datetime date month week time datetime-local number range color".split(" "),B={};Modernizr.inputtypes=function(e){for(var r,o,a,i=e.length,s="1)",c=0;i>c;c++)R.setAttribute("type",r=e[c]),a="text"!==R.type&&"style"in R,a&&(R.value=s,R.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(r)&&R.style.WebkitAppearance!==n?(k.appendChild(R),o=t.defaultView,a=o.getComputedStyle&&"textfield"!==o.getComputedStyle(R,null).WebkitAppearance&&0!==R.offsetHeight,k.removeChild(R)):/^(search|tel)$/.test(r)||(a=/^(url|email)$/.test(r)?R.checkValidity&&R.checkValidity()===!1:R.value!=s)),B[e[c]]=!!a;return B}($),Modernizr.addTest("hsla",function(){var e=i("a").style;return e.cssText="background-color:hsla(120,40%,100%,.5)",s(e.backgroundColor,"rgba")||s(e.backgroundColor,"hsla")});var j="CSS"in e&&"supports"in e.CSS,L="supportsCSS"in e;Modernizr.addTest("supports",j||L);var D={}.toString;Modernizr.addTest("svgclippaths",function(){return!!t.createElementNS&&/SVGClipPath/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","clipPath")))}),Modernizr.addTest("smil",function(){return!!t.createElementNS&&/SVGAnimate/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","animate")))});var F=function(){var t=e.matchMedia||e.msMatchMedia;return t?function(e){var n=t(e);return n&&n.matches||!1}:function(t){var n=!1;return l("@media "+t+" { #modernizr { position: absolute; } }",function(t){n="absolute"==(e.getComputedStyle?e.getComputedStyle(t,null):t.currentStyle).position}),n}}();S.mq=F;var I=S.testStyles=l,W=function(){var e=navigator.userAgent,t=e.match(/w(eb)?osbrowser/gi),n=e.match(/windows phone/gi)&&e.match(/iemobile\/([0-9])+/gi)&&parseFloat(RegExp.$1)>=9;return t||n}();W?Modernizr.addTest("fontface",!1):I('@font-face {font-family:"font";src:url("https://")}',function(e,n){var r=t.getElementById("smodernizr"),o=r.sheet||r.styleSheet,a=o?o.cssRules&&o.cssRules[0]?o.cssRules[0].cssText:o.cssText||"":"",i=/src/i.test(a)&&0===a.indexOf(n.split(" ")[0]);Modernizr.addTest("fontface",i)}),I('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}',function(e){Modernizr.addTest("generatedcontent",e.offsetHeight>=6)}),Modernizr.addTest("touchevents",function(){var n;if("ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch)n=!0;else{var r=["@media (",_.join("touch-enabled),("),"heartz",")","{#modernizr{top:9px;position:absolute}}"].join("");I(r,function(e){n=9===e.offsetTop})}return n});var U=S._config.usePrefixes?N.split(" "):[];S._cssomPrefixes=U;var V=function(t){var r,o=_.length,a=e.CSSRule;if("undefined"==typeof a)return n;if(!t)return!1;if(t=t.replace(/^@/,""),r=t.replace(/-/g,"_").toUpperCase()+"_RULE",r in a)return"@"+t;for(var i=0;o>i;i++){var s=_[i],c=s.toUpperCase()+"_"+r;if(c in a)return"@-"+s.toLowerCase()+"-"+t}return!1};S.atRule=V;var q;!function(){var e={}.hasOwnProperty;q=r(e,"undefined")||r(e.call,"undefined")?function(e,t){return t in e&&r(e.constructor.prototype[t],"undefined")}:function(t,n){return e.call(t,n)}}(),S._l={},S.on=function(e,t){this._l[e]||(this._l[e]=[]),this._l[e].push(t),Modernizr.hasOwnProperty(e)&&setTimeout(function(){Modernizr._trigger(e,Modernizr[e])},0)},S._trigger=function(e,t){if(this._l[e]){var n=this._l[e];setTimeout(function(){var e,r;for(e=0;e",r.insertBefore(n.lastChild,r.firstChild)}function f(){var e=h.elements;return"string"==typeof e?e.split(" "):e}function p(e){var t=c[e[r]];return t||(t={},d++,e[r]=d,c[d]=t),t}function l(e,t,n){return t||(t=i),s?t.createElement(e):(n||(n=p(t)),!(r=n.cache[e]?n.cache[e].cloneNode():a.test(e)?(n.cache[e]=n.createElem(e)).cloneNode():n.createElem(e)).canHaveChildren||o.test(e)||r.tagUrn?r:n.frag.appendChild(r));var r}function m(e){e||(e=i);var t=p(e);return!h.shivCSS||n||t.hasCSS||(t.hasCSS=!!u(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),s||function(t,n){n.cache||(n.cache={},n.createElem=t.createElement,n.createFrag=t.createDocumentFragment,n.frag=n.createFrag()),t.createElement=function(e){return h.shivMethods?l(e,t,n):n.createElem(e)},t.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+f().join().replace(/[\w\-:]+/g,function(e){return n.createElem(e),n.frag.createElement(e),'c("'+e+'")'})+");return n}")(h,n.frag)}(e,t),e}!function(){try{var e=i.createElement("a");e.innerHTML="",n="hidden"in e,s=1==e.childNodes.length||function(){i.createElement("a");var e=i.createDocumentFragment();return void 0===e.cloneNode||void 0===e.createDocumentFragment||void 0===e.createElement}()}catch(e){s=n=!0}}();var h={elements:t.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:"3.7.3",shivCSS:!1!==t.shivCSS,supportsUnknownElements:s,shivMethods:!1!==t.shivMethods,type:"default",shivDocument:m,createElement:l,createDocumentFragment:function(e,t){if(e||(e=i),s)return e.createDocumentFragment();for(var n=(t=t||p(e)).frag.cloneNode(),r=0,o=f(),a=o.length;r+~])("+f().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),a="$1"+y+"\\:$2";r--;)(t=n[r]=n[r].split("}"))[t.length-1]=t[t.length-1].replace(o,a),n[r]=t.join("}");return n.join("{")}(o.reverse().join("")),c=function(e){for(var t,n=e.getElementsByTagName("*"),r=n.length,o=RegExp("^(?:"+f().join("|")+")$","i"),a=[];r--;)t=n[r],o.test(t.nodeName)&&a.push(t.applyElement(T(t)));return a}(s),d=u(s,o)}),n.attachEvent("onafterprint",function(){!function(e){for(var t=e.length;t--;)e[t].removeNode()}(c),clearTimeout(e._removeSheetTimer),e._removeSheetTimer=setTimeout(l,500)}),s.printShived=!0,s}h.type+=" print",(h.shivPrint=x)(i),"object"==typeof module&&module.exports&&(module.exports=h)}(void 0!==f?f:this,u);var T=e._config.usePrefixes?t.split(" "):[];function x(e,t){return!!~(""+e).indexOf(t)}e._cssomPrefixes=T;var w={elem:v("modernizr")};c._q.push(function(){delete w.elem});var S={style:w.elem.style};function C(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function E(e,t,n){var r;if("getComputedStyle"in f){r=getComputedStyle.call(f,e,t);var o=f.console;if(null!==r)n&&(r=r.getPropertyValue(n));else if(o)o[o.error?"error":"log"].call(o,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}else r=!t&&e.currentStyle&&e.currentStyle[n];return r}function k(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function _(e,t,n,r){if(r=!m(r,"undefined")&&r,!m(n,"undefined")){var o=function(e,t){var n=e.length;if("CSS"in f&&"supports"in f.CSS){for(;n--;)if(f.CSS.supports(C(e[n]),t))return!0;return!1}if("CSSSupportsRule"in f){for(var r=[];n--;)r.push("("+C(e[n])+":"+t+")");return y("@supports ("+(r=r.join(" or "))+") { #modernizr { position: absolute; } }",function(e){return"absolute"===E(e,null,"position")})}return p}(e,n);if(!m(o,"undefined"))return o}for(var a,i,s,d,c,l=["modernizr","tspan","samp"];!S.style&&l.length;)a=!0,S.modElem=v(l.shift()),S.style=S.modElem.style;function u(){a&&(delete S.style,delete S.modElem)}for(s=e.length,i=0;i-1){return this;} return ret;}else{throw new Error('Invalid arguments for Select2: '+options);}};} if($.fn.select2.defaults==null){$.fn.select2.defaults=Defaults;} -return Select2;});return{define:S2.define,require:S2.require};}());var select2=S2.require('jquery.select2');jQuery.fn.select2.amd=S2;return select2;}));(function(factory){if(typeof define==='function'&&define.amd){define(['jquery'],factory);}else if(typeof exports==='object'){module.exports=factory;}else{factory(jQuery);}}(function($){var toFix=['wheel','mousewheel','DOMMouseScroll','MozMousePixelScroll'],toBind=('onwheel'in document||document.documentMode>=9)?['wheel']:['mousewheel','DomMouseScroll','MozMousePixelScroll'],slice=Array.prototype.slice,nullLowestDeltaTimeout,lowestDelta;if($.event.fixHooks){for(var i=toFix.length;i;){$.event.fixHooks[toFix[--i]]=$.event.mouseHooks;}} -var special=$.event.special.mousewheel={version:'3.1.9',setup:function(){if(this.addEventListener){for(var i=toBind.length;i;){this.addEventListener(toBind[--i],handler,false);}}else{this.onmousewheel=handler;} -$.data(this,'mousewheel-line-height',special.getLineHeight(this));$.data(this,'mousewheel-page-height',special.getPageHeight(this));},teardown:function(){if(this.removeEventListener){for(var i=toBind.length;i;){this.removeEventListener(toBind[--i],handler,false);}}else{this.onmousewheel=null;}},getLineHeight:function(elem){return parseInt($(elem)['offsetParent'in $.fn?'offsetParent':'parent']().css('fontSize'),10);},getPageHeight:function(elem){return $(elem).height();},settings:{adjustOldDeltas:true}};$.fn.extend({mousewheel:function(fn){return fn?this.bind('mousewheel',fn):this.trigger('mousewheel');},unmousewheel:function(fn){return this.unbind('mousewheel',fn);}});function handler(event){var orgEvent=event||window.event,args=slice.call(arguments,1),delta=0,deltaX=0,deltaY=0,absDelta=0;event=$.event.fix(orgEvent);event.type='mousewheel';if('detail'in orgEvent){deltaY=orgEvent.detail*-1;} -if('wheelDelta'in orgEvent){deltaY=orgEvent.wheelDelta;} -if('wheelDeltaY'in orgEvent){deltaY=orgEvent.wheelDeltaY;} -if('wheelDeltaX'in orgEvent){deltaX=orgEvent.wheelDeltaX*-1;} -if('axis'in orgEvent&&orgEvent.axis===orgEvent.HORIZONTAL_AXIS){deltaX=deltaY*-1;deltaY=0;} -delta=deltaY===0?deltaX:deltaY;if('deltaY'in orgEvent){deltaY=orgEvent.deltaY*-1;delta=deltaY;} -if('deltaX'in orgEvent){deltaX=orgEvent.deltaX;if(deltaY===0){delta=deltaX*-1;}} +return Select2;});return{define:S2.define,require:S2.require};}());var select2=S2.require('jquery.select2');jQuery.fn.select2.amd=S2;return select2;}));(function(factory){if(typeof define==="function"&&define.amd){define(["jquery"],factory);}else if(typeof exports==="object"){module.exports=factory;}else{factory(jQuery);}})(function($){var toFix=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],toBind=("onwheel"in window.document||window.document.documentMode>=9)?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],slice=Array.prototype.slice,nullLowestDeltaTimeout,lowestDelta;if($.event.fixHooks){for(var i=toFix.length;i;){$.event.fixHooks[toFix[--i]]=$.event.mouseHooks;}} +var special=$.event.special.mousewheel={version:"3.2.0",setup:function(){if(this.addEventListener){for(var i=toBind.length;i;){this.addEventListener(toBind[--i],handler,false);}}else{this.onmousewheel=handler;} +$.data(this,"mousewheel-line-height",special.getLineHeight(this));$.data(this,"mousewheel-page-height",special.getPageHeight(this));},teardown:function(){if(this.removeEventListener){for(var i=toBind.length;i;){this.removeEventListener(toBind[--i],handler,false);}}else{this.onmousewheel=null;} +$.removeData(this,"mousewheel-line-height");$.removeData(this,"mousewheel-page-height");},getLineHeight:function(elem){var $elem=$(elem),$parent=$elem["offsetParent"in $.fn?"offsetParent":"parent"]();if(!$parent.length){$parent=$("body");} +return parseInt($parent.css("fontSize"),10)||parseInt($elem.css("fontSize"),10)||16;},getPageHeight:function(elem){return $(elem).height();},settings:{adjustOldDeltas:true,normalizeOffset:true}};$.fn.extend({mousewheel:function(fn){return fn?this.on("mousewheel",fn):this.trigger("mousewheel");},unmousewheel:function(fn){return this.off("mousewheel",fn);}});function handler(event){var orgEvent=event||window.event,args=slice.call(arguments,1),delta=0,deltaX=0,deltaY=0,absDelta=0;event=$.event.fix(orgEvent);event.type="mousewheel";if("detail"in orgEvent){deltaY=orgEvent.detail*-1;} +if("wheelDelta"in orgEvent){deltaY=orgEvent.wheelDelta;} +if("wheelDeltaY"in orgEvent){deltaY=orgEvent.wheelDeltaY;} +if("wheelDeltaX"in orgEvent){deltaX=orgEvent.wheelDeltaX*-1;} +if("axis"in orgEvent&&orgEvent.axis===orgEvent.HORIZONTAL_AXIS){deltaX=deltaY*-1;deltaY=0;} +delta=deltaY===0?deltaX:deltaY;if("deltaY"in orgEvent){deltaY=orgEvent.deltaY*-1;delta=deltaY;} +if("deltaX"in orgEvent){deltaX=orgEvent.deltaX;if(deltaY===0){delta=deltaX*-1;}} if(deltaY===0&&deltaX===0){return;} -if(orgEvent.deltaMode===1){var lineHeight=$.data(this,'mousewheel-line-height');delta*=lineHeight;deltaY*=lineHeight;deltaX*=lineHeight;}else if(orgEvent.deltaMode===2){var pageHeight=$.data(this,'mousewheel-page-height');delta*=pageHeight;deltaY*=pageHeight;deltaX*=pageHeight;} +if(orgEvent.deltaMode===1){var lineHeight=$.data(this,"mousewheel-line-height");delta*=lineHeight;deltaY*=lineHeight;deltaX*=lineHeight;}else if(orgEvent.deltaMode===2){var pageHeight=$.data(this,"mousewheel-page-height");delta*=pageHeight;deltaY*=pageHeight;deltaX*=pageHeight;} absDelta=Math.max(Math.abs(deltaY),Math.abs(deltaX));if(!lowestDelta||absDelta=1?'floor':'ceil'](delta/lowestDelta);deltaX=Math[deltaX>=1?'floor':'ceil'](deltaX/lowestDelta);deltaY=Math[deltaY>=1?'floor':'ceil'](deltaY/lowestDelta);event.deltaX=deltaX;event.deltaY=deltaY;event.deltaFactor=lowestDelta;event.deltaMode=0;args.unshift(event,delta,deltaX,deltaY);if(nullLowestDeltaTimeout){clearTimeout(nullLowestDeltaTimeout);} -nullLowestDeltaTimeout=setTimeout(nullLowestDelta,200);return($.event.dispatch||$.event.handle).apply(this,args);} +delta=Math[delta>=1?"floor":"ceil"](delta/lowestDelta);deltaX=Math[deltaX>=1?"floor":"ceil"](deltaX/lowestDelta);deltaY=Math[deltaY>=1?"floor":"ceil"](deltaY/lowestDelta);if(special.settings.normalizeOffset&&this.getBoundingClientRect){var boundingRect=this.getBoundingClientRect();event.offsetX=event.clientX-boundingRect.left;event.offsetY=event.clientY-boundingRect.top;} +event.deltaX=deltaX;event.deltaY=deltaY;event.deltaFactor=lowestDelta;event.deltaMode=0;args.unshift(event,delta,deltaX,deltaY);if(nullLowestDeltaTimeout){window.clearTimeout(nullLowestDeltaTimeout);} +nullLowestDeltaTimeout=window.setTimeout(nullLowestDelta,200);return($.event.dispatch||$.event.handle).apply(this,args);} function nullLowestDelta(){lowestDelta=null;} -function shouldAdjustOldDeltas(orgEvent,absDelta){return special.settings.adjustOldDeltas&&orgEvent.type==='mousewheel'&&absDelta%120===0;}}));!function($,window,pluginName,undefined){var containerDefaults={drag:true,drop:true,exclude:"",nested:true,vertical:true},groupDefaults={afterMove:function($placeholder,container,$closestItemOrContainer){},containerPath:"",containerSelector:"ol, ul",distance:0,delay:0,handle:"",itemPath:"",itemSelector:"li",bodyClass:"dragging",draggedClass:"dragged",isValidTarget:function($item,container){return true},onCancel:function($item,container,_super,event){},onDrag:function($item,position,_super,event){$item.css(position)},onDragStart:function($item,container,_super,event){$item.css({height:$item.outerHeight(),width:$item.outerWidth()}) +function shouldAdjustOldDeltas(orgEvent,absDelta){return special.settings.adjustOldDeltas&&orgEvent.type==="mousewheel"&&absDelta%120===0;}});!function($,window,pluginName,undefined){var containerDefaults={drag:true,drop:true,exclude:"",nested:true,vertical:true},groupDefaults={afterMove:function($placeholder,container,$closestItemOrContainer){},containerPath:"",containerSelector:"ol, ul",distance:0,delay:0,handle:"",itemPath:"",itemSelector:"li",bodyClass:"dragging",draggedClass:"dragged",isValidTarget:function($item,container){return true},onCancel:function($item,container,_super,event){},onDrag:function($item,position,_super,event){$item.css(position)},onDragStart:function($item,container,_super,event){$item.css({height:$item.outerHeight(),width:$item.outerWidth()}) $item.addClass(container.group.options.draggedClass) $("body").addClass(container.group.options.bodyClass)},onDrop:function($item,container,_super,event){$item.removeClass(container.group.options.draggedClass).removeAttr("style") $("body").removeClass(container.group.options.bodyClass)},onMousedown:function($item,_super,event){if(!event.target.nodeName.match(/^(input|select|textarea)$/i)){event.preventDefault() @@ -2751,15 +2757,15 @@ this.$menu.on('click.autocomplete',$.proxy(this.click,this)).on('mouseenter.auto if(!isSupported){this.$element.setAttribute(eventName,'return;') isSupported=typeof this.$element[eventName]==='function'} return isSupported},move:function(e){if(!this.shown)return -switch(e.keyCode){case 9:case 13:case 27:e.preventDefault() +switch(e.key){case'Tab':case'Enter':case'Escape':e.preventDefault() break -case 38:e.preventDefault() +case'ArrowUp':e.preventDefault() this.prev() break -case 40:e.preventDefault() +case'ArrowDown':e.preventDefault() this.next() break} -e.stopPropagation()},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.keyCode,[40,38,9,13,27]) +e.stopPropagation()},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.key,['ArrowDown','ArrowUp','Tab','Enter','Escape']) this.move(e)},keypress:function(e){if(this.suppressKeyPressRepeat)return this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break case 9:case 13:if(!this.shown)return @@ -2794,15 +2800,13 @@ $.fn.autocomplete.noConflict=function(){$.fn.autocomplete=old return this} function paramToObj(name,value){if(value===undefined)value='' if(typeof value=='object')return value -try{return JSON.parse(JSON.stringify(eval("({"+value+"})")))} +try{return ocJSON("{"+value+"}")} catch(e){throw new Error('Error parsing the '+name+' attribute value. '+e)}} $(document).on('focus.autocomplete.data-api','[data-control="autocomplete"]',function(e){var $this=$(this) if($this.data('autocomplete'))return var opts=$this.data() if(opts.source){opts.source=paramToObj('data-source',opts.source)} -$this.autocomplete(opts)})}(window.jQuery);(function($){$(document).on('keydown','div.custom-checkbox',function(e){if(e.keyCode==32) -e.preventDefault()}) -$(document).on('input','div.custom-checkbox',function(e){if(e.keyCode==32){var $cb=$('input',this) +$this.autocomplete(opts)})}(window.jQuery);(function($){$(document).on('keypress','div.custom-checkbox',function(e){if(e.key==='(Space character)'||e.key==='Spacebar'||e.key===' '){var $cb=$('input[type=checkbox]',this) if($cb.data('oc-space-timestamp')==e.timeStamp) return $cb.get(0).checked=!$cb.get(0).checked @@ -3042,6 +3046,8 @@ this.scopeValues={} this.$activeScope=null this.activeScopeName=null this.isActiveScopeDirty=false +this.dependantUpdateInterval=300 +this.dependantUpdateTimers={} this.init()} FilterWidget.DEFAULTS={optionsHandler:null,updateHandler:null} FilterWidget.prototype.getPopoverTemplate=function(){return' \ @@ -3089,6 +3095,7 @@ FilterWidget.prototype.getPopoverTemplate=function(){return' \ '} FilterWidget.prototype.init=function(){var self=this +this.bindDependants() this.$el.on('change','.filter-scope input[type="checkbox"]',function(){var $scope=$(this).closest('.filter-scope') if($scope.hasClass('is-indeterminate')){self.switchToggle($(this))} else{self.checkboxToggle($(this))}}) @@ -3113,6 +3120,20 @@ self.pushOptions(self.activeScopeName) self.activeScopeName=null self.$activeScope=null setTimeout(function(){$scope.removeClass('filter-scope-open')},200)})} +FilterWidget.prototype.bindDependants=function(){if(!$('[data-scope-depends]',this.$el).length){return;} +var self=this,scopeMap={},scopeElements=this.$el.find('.filter-scope') +scopeElements.filter('[data-scope-depends]').each(function(){var name=$(this).data('scope-name'),depends=$(this).data('scope-depends') +$.each(depends,function(index,depend){if(!scopeMap[depend]){scopeMap[depend]={scopes:[]}} +scopeMap[depend].scopes.push(name)})}) +$.each(scopeMap,function(scopeName,toRefresh){scopeElements.filter('[data-scope-name="'+scopeName+'"]').on('change.oc.filterScope',$.proxy(self.onRefreshDependants,self,scopeName,toRefresh))})} +FilterWidget.prototype.onRefreshDependants=function(scopeName,toRefresh){var self=this,scopeElements=this.$el.find('.filter-scope') +if(this.dependantUpdateTimers[scopeName]!==undefined){window.clearTimeout(this.dependantUpdateTimers[scopeName])} +this.dependantUpdateTimers[scopeName]=window.setTimeout(function(){$.each(toRefresh.scopes,function(index,dependantScope){self.scopeValues[dependantScope]=null +var $scope=self.$el.find('[data-scope-name="'+dependantScope+'"]') +self.$el.request(self.options.optionsHandler,{data:{scopeName:dependantScope},success:function(data){self.fillOptions(dependantScope,data.options) +self.updateScopeSetting($scope,data.options.active.length) +$scope.loadIndicator('hide')}})})},this.dependantUpdateInterval) +$.each(toRefresh.scopes,function(index,scope){scopeElements.filter('[data-scope-name="'+scope+'"]').addClass('loading-indicator-container').loadIndicator()})} FilterWidget.prototype.focusSearch=function(){if(Modernizr.touchevents) return var $input=$('#controlFilterPopover input.filter-search-input'),length=$input.val().length @@ -3187,9 +3208,9 @@ FilterWidget.prototype.toggleFilterButtons=function(data) if(data){data.active.length>0?buttonContainer.show():buttonContainer.hide()}else{items.children().length>0?buttonContainer.show():buttonContainer.hide()}} FilterWidget.prototype.pushOptions=function(scopeName){if(!this.isActiveScopeDirty||!this.options.updateHandler) return -var data={scopeName:scopeName,options:this.scopeValues[scopeName]} +var self=this,data={scopeName:scopeName,options:this.scopeValues[scopeName]} $.oc.stripeLoadIndicator.show() -this.$el.request(this.options.updateHandler,{data:data}).always(function(){$.oc.stripeLoadIndicator.hide()})} +this.$el.request(this.options.updateHandler,{data:data}).always(function(){$.oc.stripeLoadIndicator.hide()}).done(function(){self.$el.find('[data-scope-name="'+scopeName+'"]').trigger('change.oc.filterScope')})} FilterWidget.prototype.checkboxToggle=function($el){var isChecked=$el.is(':checked'),$scope=$el.closest('.filter-scope'),scopeName=$scope.data('scope-name') this.scopeValues[scopeName]=isChecked if(this.options.updateHandler){var data={scopeName:scopeName,value:isChecked} @@ -3493,7 +3514,12 @@ if($element.hasClass('select-hide-selected')){extraOptions.dropdownCssClass+=' s var source=$element.data('handler');if(source){extraOptions.ajax={transport:function(params,success,failure){var $request=$element.request(source,{data:params.data}) $request.done(success) $request.fail(failure) -return $request},dataType:'json'}} +return $request},processResults:function(data,params){var results=data.result||data.results,options=[] +delete(data.result) +if(results[0]&&typeof(results[0])==='object'){options=results} +else{for(var i in results){if(results.hasOwnProperty(i)){options.push({id:i,text:results[i],})}}} +data.results=options +return data},dataType:'json'}} var separators=$element.data('token-separators') if(separators){extraOptions.tags=true extraOptions.tokenSeparators=separators.split('|') @@ -3647,7 +3673,7 @@ this.docClickHandler=$.proxy(this.onDocumentClick,this) $(document).bind('mousedown',this.docClickHandler);if(this.options.closeOnEsc){$(document).on('keyup.oc.popover',function(e){if($(e.target).hasClass('select2-offscreen')) return false if(!self.options.closeOnEsc){return false} -if(e.keyCode==27){self.hide() +if(e.key==='Escape'){self.hide() return false}})}} Popover.prototype.reposition=function(){var placement=this.calcPlacement(),position=this.calcPosition(placement) @@ -3855,7 +3881,7 @@ $.fn.popup.noConflict=function(){$.fn.popup=old return this} function paramToObj(name,value){if(value===undefined)value='' if(typeof value=='object')return value -try{return JSON.parse(JSON.stringify(eval("({"+value+"})")))} +try{return ocJSON("{"+value+"}")} catch(e){throw new Error('Error parsing the '+name+' attribute value. '+e)}} $(document).on('click.oc.popup','[data-control="popup"]',function(event){event.preventDefault() $(this).popup()});$(document).on('ajaxPromise','[data-popup-load-indicator]',function(event,context){if($(this).data('request')!=context.handler)return @@ -3898,7 +3924,7 @@ $.oc.chartUtils=new ChartUtils();}(window.jQuery);+function($){"use strict";var this.chartOptions={xaxis:{mode:"time",tickLength:5},selection:{mode:"x"},grid:{markingsColor:"rgba(0,0,0, 0.02)",backgroundColor:{colors:["#fff","#fff"]},borderColor:"#7bafcc",borderWidth:0,color:"#ddd",hoverable:true,clickable:true,labelMargin:10},series:{lines:{show:true,fill:true},points:{show:true}},tooltip:true,tooltipOpts:{defaultTheme:false,content:"%x: %y",dateFormat:"%y-%0m-%0d",shifts:{x:10,y:20}},legend:{show:true,noColumns:2}} this.defaultDataSetOptions={shadowSize:0} var parsedOptions={} -try{parsedOptions=JSON.parse(JSON.stringify(eval("({"+options.chartOptions+"})")));}catch(e){throw new Error('Error parsing the data-chart-options attribute value. '+e);} +try{parsedOptions=ocJSON("{"+options.chartOptions+"}");}catch(e){throw new Error('Error parsing the data-chart-options attribute value. '+e);} this.chartOptions=$.extend({},this.chartOptions,parsedOptions) this.options=options this.$el=$(element) @@ -4040,14 +4066,18 @@ var tr=this.$el.prop('tagName')=='TR'?this.$el:this.$el.find('tr:has(td)') tr.each(function(){var link=$(this).find(options.target).filter(function(){return!$(this).closest('td').hasClass(options.excludeClass)&&!$(this).hasClass(options.excludeClass)}).first() if(!link.length)return var href=link.attr('href'),onclick=(typeof link.get(0).onclick=="function")?link.get(0).onclick:null,popup=link.is('[data-control=popup]'),request=link.is('[data-request]') -$(this).find('td').not('.'+options.excludeClass).click(function(e){if($(document.body).hasClass('drag')){return} +function handleClick(e){if($(document.body).hasClass('drag')){return} if(onclick){onclick.apply(link.get(0))} else if(request){link.request()} else if(popup){link.popup()} else if(e.ctrlKey||e.metaKey){window.open(href)} -else{window.location=href}}) +else{window.location=href}} +$(this).find('td').not('.'+options.excludeClass).click(function(e){handleClick(e)}) +$(this).on('keypress',function(e){if(e.key==='(Space character)'||e.key==='Spacebar'||e.key===' '){handleClick(e) +return false}}) $(this).addClass(options.linkedClass) -link.hide().after(link.html())})} +link.hide().after(link.html())}) +$('tr.rowlink').attr('tabindex',0)} RowLink.DEFAULTS={target:'a',excludeClass:'nolink',linkedClass:'rowlink'} var old=$.fn.rowLink $.fn.rowLink=function(option){var args=Array.prototype.slice.call(arguments,1) @@ -4123,8 +4153,8 @@ $.fn.changeMonitor.Constructor=ChangeMonitor $.fn.changeMonitor.noConflict=function(){$.fn.changeMonitor=old return this} $(document).render(function(){$('[data-change-monitor]').changeMonitor()})}(window.jQuery);+function($){"use strict";var Base=$.oc.foundation.base,BaseProto=Base.prototype -var HotKey=function(element,options){if(!options.hotkey) -throw new Error('No hotkey has been defined.');this.$el=$(element) +var HotKey=function(element,options){if(!options.hotkey){throw new Error('No hotkey has been defined.');} +this.$el=$(element) this.$target=$(options.hotkeyTarget) this.options=options||{} this.keyConditions=[] @@ -4134,8 +4164,7 @@ Base.call(this) this.init()} HotKey.prototype=Object.create(BaseProto) HotKey.prototype.constructor=HotKey -HotKey.prototype.dispose=function(){if(this.$el===null) -return +HotKey.prototype.dispose=function(){if(this.$el===null){return} this.unregisterHandlers() this.$el.removeData('oc.hotkey') this.$target=null @@ -4162,18 +4191,15 @@ break case'alt':case'option':condition.alt=true break}} condition.specific=this.keyMap[keys[keys.length-1]] -if(typeof(condition.specific)=='undefined') -condition.specific=keys[keys.length-1].toUpperCase().charCodeAt() +if(typeof(condition.specific)=='undefined'){condition.specific=keys[keys.length-1].toUpperCase().charCodeAt()} return condition} HotKey.prototype.initKeyMap=function(){this.keyMap={'esc':27,'tab':9,'space':32,'return':13,'enter':13,'backspace':8,'scroll':145,'capslock':20,'numlock':144,'pause':19,'break':19,'insert':45,'home':36,'delete':46,'suppr':46,'end':35,'pageup':33,'pagedown':34,'left':37,'up':38,'right':39,'down':40,'f1':112,'f2':113,'f3':114,'f4':115,'f5':116,'f6':117,'f7':118,'f8':119,'f9':120,'f10':121,'f11':122,'f12':123}} HotKey.prototype.trim=function(str){return str.replace(/^\s+/,"").replace(/\s+$/,"")} HotKey.prototype.testConditions=function(ev){for(var i=0,len=this.keyConditions.length;i li',this.$tabsContainer).each(function(index){self.initTab(this)}) this.$el.on('close.oc.tab',function(ev,data){ev.preventDefault() var force=(data!==undefined&&data.force!==undefined)?data.force:false;self.closeTab($(ev.target).closest('ul.nav-tabs > li, div.tab-content > div'),force)}) -this.$el.on('mousedown',"li[data-tab-id]",function(ev){if(ev.which===2){$(ev.target).trigger('close.oc.tab');}}) +this.$el.on('mousedown',"li[data-tab-id]",function(ev){if(ev.key==='2'){$(ev.target).trigger('close.oc.tab');}}) this.$el.on('toggleCollapse.oc.tab',function(ev,data){ev.preventDefault() $(ev.target).closest('div.tab-content > div').toggleClass('collapsed')}) this.$el.on('modified.oc.tab',function(ev){ev.preventDefault() @@ -4561,9 +4587,8 @@ $tabs=$('>li',this.$tabsContainer),tabIndex=$tabs.index(li),time=new Date().getT $anchor.data('target','#'+targetId).attr('data-target','#'+targetId).attr('data-toggle','tab') if(!$anchor.attr('title')) $anchor.attr('title',$anchor.text()) -var html=$anchor.html() -$anchor.html('') -$anchor.append($('').append($('').html(html))) +if($anchor.find('> span.title > span').length<1){var html=$anchor.html() +$anchor.html('').append($('').append($('').html(html)))} var pane=$('> .tab-pane',this.$pagesContainer).eq(tabIndex).attr('id',targetId) if(!$('span.tab-close',li).length){$(li).append($('×').click(function(){$(this).trigger('close.oc.tab') return false}))} @@ -5092,7 +5117,7 @@ BaseWrapper.prototype.cleanupAfterSwitch=function(){this.switched=true this.dispose()} BaseWrapper.prototype.loadValues=function(configuration){var $valuesField=this.getElementValuesInput() if($valuesField.length>0){var valuesStr=$.trim($valuesField.val()) -try{return valuesStr.length===0?{}:$.parseJSON(valuesStr)} +try{return valuesStr.length===0?{}:JSON.parse(valuesStr)} catch(err){throw new Error('Error parsing Inspector field values. '+err)}} var values={},attributes=this.$element.get(0).attributes for(var i=0,len=attributes.length;iul li a:hover:before {position:absolute;font-size:14px;left:9px;top:7px;color:rgba(0,0,0,0.2)} .touch .dropdown-menu .dropdown-container >ul li.first-item a:hover:after {content:'';display:none} body.dropdown-open .dropdown-overlay {position:fixed;left:0;top:0;right:0;bottom:0;z-index:599} -@media (max-width:480px) {body.dropdown-open {overflow:hidden }body.dropdown-open .dropdown-overlay {background:rgba(0,0,0,0.4) }body.dropdown-open .dropdown-menu {overflow:auto;overflow-y:scroll;position:fixed !important;margin:0 !important;top:0 !important;right:0 !important;bottom:0 !important;left:0 !important;z-index:600 }body.dropdown-open .dropdown-menu .dropdown-container {padding:10px;height:100% }body.dropdown-open .dropdown-menu .dropdown-container ul {min-height:100%;margin-top:0 }body.dropdown-open .dropdown-menu .dropdown-container ul:before,body.dropdown-open .dropdown-menu .dropdown-container ul:after {display:none }body.dropdown-open .dropdown-menu .dropdown-container ul li.dropdown-title {display:block;padding:8px 15px;border-bottom:1px solid #c9c9c9;color:#39454a;position:relative;cursor:pointer;font-weight:600 }body.dropdown-open .dropdown-menu .dropdown-container ul li.dropdown-title:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f00d";position:absolute;top:7px;right:13px;opacity:0.3;filter:alpha(opacity=30) }body.dropdown-open .dropdown-menu .dropdown-container ul li.first-item a:hover:after,body.dropdown-open .dropdown-menu .dropdown-container ul li.first-item :focus:after {content:'';display:none }} +@media (max-width:480px) {body.dropdown-open {overflow:hidden }body.dropdown-open .dropdown-overlay {background:rgba(0,0,0,0.4) }body.dropdown-open .dropdown-menu {overflow:auto;overflow-y:scroll;position:fixed !important;margin:0 !important;top:0 !important;right:0 !important;bottom:0 !important;left:0 !important;z-index:600 }body.dropdown-open .dropdown-menu .dropdown-container {padding:10px;height:100% }body.dropdown-open .dropdown-menu .dropdown-container ul {min-height:100%;margin-top:0 }body.dropdown-open .dropdown-menu .dropdown-container ul:before,body.dropdown-open .dropdown-menu .dropdown-container ul:after {display:none }body.dropdown-open .dropdown-menu .dropdown-container ul li.dropdown-title {display:block;padding:8px 15px;border-bottom:1px solid #c9c9c9;color:#39454a;position:relative;cursor:pointer;font-weight:600 }body.dropdown-open .dropdown-menu .dropdown-container ul li.dropdown-title:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f00d";position:absolute;top:7px;right:13px;opacity:0.3;filter:alpha(opacity=30) }body.dropdown-open .dropdown-menu .dropdown-container ul li.first-item a:hover:after,body.dropdown-open .dropdown-menu .dropdown-container ul li.first-item :focus:after {content:'';display:none }} div.control-popover {position:absolute;background-clip:content-box;left:0;top:0;z-index:600;visibility:hidden} div.control-popover.in, div.control-popover.fade {visibility:visible} @@ -2781,8 +2781,8 @@ body.compact-container .control-breadcrumb {margin-top:0;margin-left:0;margin-ri .control-tabs:last-child {margin-bottom:0} .control-tabs:after, .control-tabs:before {display:none;position:absolute;top:50%;margin-top:-7px;height:9px;font-size:10px;color:#bbb} -.control-tabs:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f104"} -.control-tabs:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105"} +.control-tabs:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f104"} +.control-tabs:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105"} .control-tabs.scroll-before:before {display:block} .control-tabs.scroll-after:after {display:block} .control-tabs.scroll-active-before:before {color:#d0d0d0} @@ -3002,9 +3002,9 @@ body.compact-container .control-breadcrumb {margin-top:0;margin-left:0;margin-ri .title-value p.negative:after, .title-value p.positive:after {font-size:17px;vertical-align:top;position:relative;top:-3px;left:5px} .title-value p.negative {color:#c30} -.title-value p.negative:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f103"} +.title-value p.negative:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f103"} .title-value p.positive {color:#95b753} -.title-value p.positive:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f102"} +.title-value p.positive:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f102"} .title-value p.description {color:#999;font-weight:300;line-height:100%;font-size:13px} .report-container .title-value {margin-top:-18px} .report-container .title-value p {font-weight:100;font-size:40px} @@ -3054,8 +3054,8 @@ a.control-status-list >ul li .status-text.danger:hover {color:#843534} .control-toolbar {font-size:0;padding:0 0 20px 0;position:relative;display:table;table-layout:fixed;width:100%} .control-toolbar:after, .control-toolbar:before {display:none;position:absolute;top:50%;margin-top:-7px;height:9px;font-size:10px;color:#bbb} -.control-toolbar:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f104"} -.control-toolbar:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105"} +.control-toolbar:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f104"} +.control-toolbar:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105"} .control-toolbar.scroll-before:before {display:block} .control-toolbar.scroll-after:after {display:block} .control-toolbar:before {left:-10px} @@ -3065,8 +3065,8 @@ a.control-status-list >ul li .status-text.danger:hover {color:#843534} .control-toolbar .toolbar-item.last {padding-right:0} .control-toolbar .toolbar-item:after, .control-toolbar .toolbar-item:before {display:none;position:absolute;top:50%;margin-top:-7px;height:9px;font-size:10px;color:#bbb} -.control-toolbar .toolbar-item:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f104"} -.control-toolbar .toolbar-item:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105"} +.control-toolbar .toolbar-item:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f104"} +.control-toolbar .toolbar-item:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105"} .control-toolbar .toolbar-item.scroll-before:before {display:block} .control-toolbar .toolbar-item.scroll-after:after {display:block} .control-toolbar .toolbar-item:before {left:-10px} @@ -3130,8 +3130,8 @@ html.mobile [data-control=toolbar].is-native-drag {overflow:auto;-webkit-overflo div.scoreboard {position:relative;padding:0} div.scoreboard:after, div.scoreboard:before {display:none;position:absolute;top:50%;margin-top:-7px;height:9px;font-size:10px;color:#bbb} -div.scoreboard:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f104"} -div.scoreboard:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105"} +div.scoreboard:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f104"} +div.scoreboard:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105"} div.scoreboard.scroll-before:before {display:block} div.scoreboard.scroll-after:after {display:block} div.scoreboard:before, @@ -3943,7 +3943,7 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .select2-container--default {display:block} .select2-container--default .select2-selection {background-color:#fff;border:1px solid #d1d6d9;border-radius:3px;color:#385487;font-size:14px;-webkit-box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);outline:0} .select2-container--default .select2-search--dropdown {position:relative} -.select2-container--default .select2-search--dropdown:after {position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f002";right:13px;top:9px;color:#95a5a6} +.select2-container--default .select2-search--dropdown:after {position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f002";right:13px;top:9px;color:#95a5a6} .select2-container--default .select2-search--dropdown .select2-search__field {background-color:#fff;border:1px solid #d1d6d9;border-radius:3px;color:#385487;font-size:14px;-webkit-box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5);box-shadow:inset 0 1px 0 rgba(209,214,217,0.25),0 1px 0 rgba(255,255,255,.5)} .select2-container--default .select2-search__field {outline:0} .select2-container--default .select2-search__field::-webkit-input-placeholder {color:#ccc} @@ -3964,7 +3964,7 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .select2-container--default .select2-results__group {color:#999;display:block;padding:8px 6px;line-height:1.42857143;white-space:nowrap;font-weight:500} .select2-container--default.select2-container--focus .select2-selection, .select2-container--default.select2-container--open .select2-selection {-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;border-color:#d1d6d9} -.select2-container--default.select2-container--open .select2-selection .select2-selection__arrow b:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f106"} +.select2-container--default.select2-container--open .select2-selection .select2-selection__arrow b:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f106"} .select2-container--default.select2-container--open.select2-container--below .select2-selection:not(.select-no-dropdown) {border-bottom-right-radius:0;border-bottom-left-radius:0;border-bottom-color:transparent} .select2-container--default.select2-container--open.select2-container--above .select2-selection:not(.select-no-dropdown) {border-top-right-radius:0;border-top-left-radius:0;border-top-color:transparent} .select2-container--default .select2-selection__clear {color:#666;cursor:pointer;float:right;font-weight:bold;margin-right:10px} @@ -3984,7 +3984,7 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .select2-container--default .select2-selection--single {height:38px;line-height:1.42857143;padding:8px 25px 8px 13px} .select2-container--default .select2-selection--single .select2-selection__arrow {position:absolute;bottom:0;right:13px;top:0;width:4px} .select2-container--default .select2-selection--single .select2-selection__arrow b {position:absolute;top:50%;height:9px;width:8px;right:3px;margin-top:-5px;line-height:9px} -.select2-container--default .select2-selection--single .select2-selection__arrow b:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f107";display:inline-block} +.select2-container--default .select2-selection--single .select2-selection__arrow b:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f107";display:inline-block} .select2-container--default .select2-selection--single .select2-selection__rendered {color:#385487;padding:0} .select2-container--default .select2-selection--single .select2-selection__placeholder {color:#ccc} .select2-container--default .select2-selection--multiple {min-height:38px} @@ -4114,9 +4114,9 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .custom-checkbox input[type=checkbox]:checked + label:before, .custom-radio input[type=checkbox]:checked + label:before {border-color:#1f99dc;background-color:#1f99dc;font-size:12px;line-height:17px;border-width:2px;-webkit-box-shadow:none;box-shadow:none} .custom-checkbox input[type=checkbox]:checked + label:before, -.custom-radio input[type=checkbox]:checked + label:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f00c"} +.custom-radio input[type=checkbox]:checked + label:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f00c"} .custom-checkbox input[type=checkbox]:indeterminate + label:before, -.custom-radio input[type=checkbox]:indeterminate + label:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f068"} +.custom-radio input[type=checkbox]:indeterminate + label:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f068"} .custom-checkbox input:disabled + label:before, .custom-radio input:disabled + label:before {border:1px solid #d1d6d9 !important} .custom-checkbox input:disabled:checked + label:before, @@ -4133,6 +4133,8 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .inline-options .field-checkboxlist:not(.is-scrollable) .custom-checkbox {display:inline-block;margin:0} .inline-options .field-checkboxlist:not(.is-scrollable) .custom-checkbox label {margin-bottom:0 !important;padding-top:10px} .inline-options .field-checkboxlist:not(.is-scrollable) .custom-checkbox label:before {top:10px} +.inline-options.radio-field >label {display:block} +.inline-options.radio-field .custom-radio {display:inline-block;margin-bottom:0} .switch-field .field-switch {padding-left:85px;float:left} .switch-field .field-switch >label {margin-top:3px} .custom-switch {display:block;width:65px;height:26px;position:relative;text-transform:uppercase;border:none;cursor:pointer;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px} @@ -4193,10 +4195,14 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .form-group:empty {display:none} .form-group, .form-group.layout-item {padding-bottom:20px;margin-bottom:0} -.form-group.is-required >label:after {background-color:#c20a0a;width:5px;height:5px;margin-left:3px;vertical-align:super;font-size:60%;content:"";display:inline-block;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px} +.form-group.is-required >label:not(.custom-switch):after, +.form-group.is-required >.field-switch >label:after {background-color:#c20a0a;width:5px;height:5px;margin-left:3px;vertical-align:super;font-size:60%;content:"";display:inline-block;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px} .form-group.span-full {width:100%;float:left} .form-group.span-left {float:left;width:48.5%;clear:left} .form-group.span-right {float:right;width:48.5%;clear:right} +.form-group.clear-full {clear:both} +.form-group.clear-left {clear:left} +.form-group.clear-right {clear:right} .form-group.layout-relative {padding-bottom:0} .form-group.checkbox-field {padding-bottom:5px} .form-group.number-field >.form-control {text-align:right} @@ -4226,7 +4232,7 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .field-section >p:first-child, .field-section >h4:first-child {margin:0} .field-section.is-collapsible {cursor:pointer} -.field-section.is-collapsible >h4:before {display:inline-block;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;vertical-align:baseline;content:"\f077";font-size:12px;margin:2px 8px 0;float:right;color:rgba(0,0,0,0.4);-webkit-transition:all 0.3s;transition:all 0.3s;-webkit-transform:scale(1,1);-moz-transform:scale(1,1);-ms-transform:scale(1,1);-o-transform:scale(1,1);transform:scale(1,1)} +.field-section.is-collapsible >h4:before {display:inline-block;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;vertical-align:baseline;content:"\f077";font-size:12px;margin:2px 8px 0;float:right;color:rgba(0,0,0,0.4);-webkit-transition:all 0.3s;transition:all 0.3s;-webkit-transform:scale(1,1);-moz-transform:scale(1,1);-ms-transform:scale(1,1);-o-transform:scale(1,1);transform:scale(1,1)} .field-section.is-collapsible:hover {border-bottom:1px solid #b5bdc2} .field-section.is-collapsible:hover >h4:before {color:inherit} .form-group.section-field.collapsed .field-section.is-collapsible >h4:before {-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)} @@ -4236,8 +4242,13 @@ html.cssanimations .cursor-loading-indicator.hide {display:none} .field-textarea.size-large {min-height:200px} .field-textarea.size-huge {min-height:250px} .field-textarea.size-giant {min-height:350px} -.field-checkboxlist:not(.is-scrollable) {padding:20px 20px 2px 20px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff;border:1px solid #e2e2e2} -.field-checkboxlist.is-scrollable small {color:#999} +.field-checkboxlist:not(.is-scrollable) {-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff;border:1px solid #e2e2e2} +.field-checkboxlist:not(.is-scrollable) .field-checkboxlist-inner {padding:20px 20px 2px 20px} +.field-checkboxlist .checkboxlist-controls {display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:baseline;-ms-flex-align:baseline;align-items:baseline} +.field-checkboxlist .checkboxlist-controls >div {padding-bottom:7px} +.field-checkboxlist .checkboxlist-controls >div >a {font-size:13px;margin-right:20px;text-decoration:none} +.field-checkboxlist .checkboxlist-controls >div >a >i {color:#999;margin:0 4px} +.field-checkboxlist .checkboxlist-controls >div >a:hover >i {color:#2a3e51} .field-checkboxlist-scrollable {background:#fff;border:1px solid #e2e2e2;padding-left:15px;height:300px} .field-checkboxlist-scrollable .checkbox {margin-top:15px;margin-bottom:5px} .field-checkboxlist-scrollable .checkbox ~ .checkbox {margin-top:0} @@ -4418,7 +4429,7 @@ table.table.data thead th >span:hover {color:#000} table.table.data thead td.sort-desc >span:after, table.table.data thead th.sort-desc >span:after, table.table.data thead td.sort-desc >a:after, -table.table.data thead th.sort-desc >a:after {font-size:14px;line-height:14px;display:inline-block;margin-left:6px;vertical-align:baseline;opacity:0.4;filter:alpha(opacity=40);font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f107"} +table.table.data thead th.sort-desc >a:after {font-size:14px;line-height:14px;display:inline-block;margin-left:6px;vertical-align:baseline;opacity:0.4;filter:alpha(opacity=40);font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f107"} table.table.data thead td.sort-desc >span:hover:after, table.table.data thead th.sort-desc >span:hover:after, table.table.data thead td.sort-desc >a:hover:after, @@ -4426,7 +4437,7 @@ table.table.data thead th.sort-desc >a:hover:after {opacity:0.8;filter:alpha(opa table.table.data thead td.sort-asc >span:after, table.table.data thead th.sort-asc >span:after, table.table.data thead td.sort-asc >a:after, -table.table.data thead th.sort-asc >a:after {font-size:14px;line-height:14px;display:inline-block;margin-left:6px;vertical-align:baseline;opacity:0.4;filter:alpha(opacity=40);font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f106"} +table.table.data thead th.sort-asc >a:after {font-size:14px;line-height:14px;display:inline-block;margin-left:6px;vertical-align:baseline;opacity:0.4;filter:alpha(opacity=40);font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f106"} table.table.data thead td.sort-asc >span:hover:after, table.table.data thead th.sort-asc >span:hover:after, table.table.data thead td.sort-asc >a:hover:after, @@ -4588,7 +4599,7 @@ table.table.data tr.list-tree-level-10 td.list-cell-index-1 {padding-left:115px} .control-list table.table.data {margin-bottom:0} .control-list table.table.data .list-setup {width:48px} .control-list table.table.data .list-setup a {display:block;color:#000} -.control-list table.table.data .list-setup a:before {font-size:14px;line-height:14px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f0ca";display:inline-block;margin-left:8px;vertical-align:baseline;opacity:0.6;filter:alpha(opacity=60)} +.control-list table.table.data .list-setup a:before {font-size:14px;line-height:14px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f0ca";display:inline-block;margin-left:8px;vertical-align:baseline;opacity:0.6;filter:alpha(opacity=60)} .control-list table.table.data .list-setup a:hover:before {opacity:1;filter:alpha(opacity=100);color:#4ea5e0 !important} .list-header {background-color:transparent;padding:0 20px 1px 20px} .list-header h3 {font-size:14px;color:#7e8c8d;text-transform:uppercase;font-weight:600;margin-top:0;margin-bottom:15px} @@ -4604,8 +4615,8 @@ table.table.data tr.list-tree-level-10 td.list-cell-index-1 {padding-left:115px} .list-scrollable-container {touch-action:auto;position:relative} .list-scrollable-container:after, .list-scrollable-container:before {display:none;position:absolute;top:50%;margin-top:-7px;height:9px;font-size:10px;color:#666} -.list-scrollable-container:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f104"} -.list-scrollable-container:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105"} +.list-scrollable-container:before {left:-6px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f104"} +.list-scrollable-container:after {right:-8px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105"} .list-scrollable-container.scroll-before:before {display:block} .list-scrollable-container.scroll-after:after {display:block} .list-scrollable-container:after, @@ -4613,6 +4624,8 @@ table.table.data tr.list-tree-level-10 td.list-cell-index-1 {padding-left:115px} .list-scrollable-container:before {left:0} .list-scrollable-container:after {right:0} .list-scrollable-container >.list-scrollable {overflow:hidden} +.list-scrollable-container.scroll-after th a, +.list-scrollable-container.scroll-before th a {cursor:grab} .inspector-fields {min-width:220px;border-collapse:collapse;width:100%;table-layout:fixed;border-bottom-right-radius:2px;border-bottom-left-radius:2px} .inspector-fields td, .inspector-fields th {padding:5px 12px;font-size:12px;width:50%;border-bottom:1px solid #c8cccd;text-align:left} @@ -4668,8 +4681,8 @@ table.table.data tr.list-tree-level-10 td.list-cell-index-1 {padding-left:115px} .inspector-fields th >div >div span.info:hover {opacity:1;filter:alpha(opacity=100)} .inspector-fields th >div a.expandControl {display:block;position:absolute;width:12px;height:12px;left:-15px;top:2px;text-indent:-100000em} .inspector-fields th >div a.expandControl span {position:absolute;display:inline-block;left:0;top:0;width:12px;height:12px} -.inspector-fields th >div a.expandControl span:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105";position:absolute;left:4px;top:-2px;width:12px;height:12px;font-size:13px;color:#333;text-indent:0} -.inspector-fields th >div a.expandControl.expanded span:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f107";left:2px} +.inspector-fields th >div a.expandControl span:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105";position:absolute;left:4px;top:-2px;width:12px;height:12px;font-size:13px;color:#333;text-indent:0} +.inspector-fields th >div a.expandControl.expanded span:after {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f107";left:2px} .inspector-fields input[type=text] {display:block;width:100%;border:none;outline:none} .inspector-fields div.custom-checkbox {margin-top:0;margin-bottom:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} .inspector-fields div.custom-checkbox label:before {top:-12px} @@ -4731,7 +4744,7 @@ ul.autocomplete.dropdown-menu.inspector-autocomplete li a {padding:5px 12px;whit .select2-dropdown.ocInspectorDropdown >.select2-results li >i, .select2-dropdown.ocInspectorDropdown >.select2-results li >img {margin-left:6px} .select2-dropdown.ocInspectorDropdown .select2-search {min-height:26px;position:relative;border-bottom:1px solid #b2b9be} -.select2-dropdown.ocInspectorDropdown .select2-search:after {position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f002";right:10px;top:10px;color:#95a5a6} +.select2-dropdown.ocInspectorDropdown .select2-search:after {position:absolute;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f002";right:10px;top:10px;color:#95a5a6} .select2-dropdown.ocInspectorDropdown .select2-search input.select2-search__field {min-height:26px;background:transparent !important;font-size:13px;padding-left:4px;padding-right:20px;border:none} .control-pagination {font-size:0;text-align:center} @media (min-width:768px) {.control-pagination {text-align:right }} @@ -4763,17 +4776,20 @@ ul.autocomplete.dropdown-menu.inspector-autocomplete li a {padding:5px 12px;whit .control-pagination .page-back {padding-right:6px} .control-pagination .page-last {padding-left:6px} .control-pagination .page-first {padding-right:6px} -.control-pagination .page-next:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f105"} -.control-pagination .page-back:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f104"} -.control-pagination .page-last:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f101"} -.control-pagination .page-first:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f100"} +.control-pagination .page-next:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f105"} +.control-pagination .page-back:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f104"} +.control-pagination .page-last:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f101"} +.control-pagination .page-first:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f100"} .control-filter {padding:0 10px;color:rgba(0,0,0,0.6);background-color:#ecf0f1;border-top:1px solid #d7dbdd;border-bottom:1px solid #d7dbdd;font-size:13px} .control-filter .custom-checkbox label {font-size:13px;color:rgba(0,0,0,0.6)} .control-filter a {text-decoration:none;color:rgba(0,0,0,0.6)} .control-filter >.filter-scope {display:inline-block;padding:10px} .control-filter >.filter-scope .filter-label {margin-right:5px} .control-filter >.filter-scope .filter-setting {display:inline-block;margin-right:5px;-webkit-transition:color 0.6s;transition:color 0.6s} -.control-filter >.filter-scope:after {font-size:14px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f107"} +.control-filter >.filter-scope.loading-indicator-container.in-progress {pointer-events:none;cursor:default} +.control-filter >.filter-scope.loading-indicator-container.in-progress .loading-indicator {background:transparent} +.control-filter >.filter-scope.loading-indicator-container.in-progress .loading-indicator >span {left:unset;right:0;top:10px;background-color:#ecf0f1;border-radius:50%;margin-top:0;width:20px;height:20px;background-size:15px 15px} +.control-filter >.filter-scope:after {font-size:14px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f107"} .control-filter >.filter-scope.active .filter-setting {padding-left:5px;padding-right:5px;color:#FFF;background-color:#6aab55;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-transition:color 1s,background-color 1s;transition:color 1s,background-color 1s} .control-filter >.filter-scope.checkbox {padding-left:35px} .control-filter >.filter-scope.checkbox, @@ -4796,7 +4812,7 @@ ul.autocomplete.dropdown-menu.inspector-autocomplete li a {padding:5px 12px;whit .control-filter >.filter-scope.active.active .filter-setting {background-color:#5f9a4c} .control-filter >.filter-has-popover {display:inline-block;padding:10px} .control-filter >.filter-has-popover .filter-setting {display:inline-block;-webkit-transition:color 0.6s;transition:color 0.6s} -.control-filter >.filter-has-popover:after {font-size:14px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f107"} +.control-filter >.filter-has-popover:after {font-size:14px;font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f107"} .control-filter >.filter-has-popover.active .filter-setting {padding-left:5px;padding-right:5px;color:#FFF;background-color:#6aab55;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-transition:color 1s,background-color 1s;transition:color 1s,background-color 1s} .control-filter >.filter-has-popover:hover {color:#000} .control-filter >.filter-has-popover:hover .filter-label {color:rgba(0,0,0,0.6)} @@ -4821,11 +4837,11 @@ ul.autocomplete.dropdown-menu.inspector-autocomplete li a {padding:5px 12px;whit .control-filter-popover .filter-items a:hover, .control-filter-popover .filter-active-items a:hover {background-color:#4da7e8;color:#FFF} .control-filter-popover .filter-items {max-height:135px;overflow:auto;background-color:#fafafa;border-bottom:1px solid #d7dbdd} -.control-filter-popover .filter-items a:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f067"} +.control-filter-popover .filter-items a:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f067"} .control-filter-popover .filter-items li.loading {padding:7px} .control-filter-popover .filter-items li.loading >span {display:block;height:20px;width:20px;background-image:url('images/loader-transparent.svg');background-size:20px 20px;background-position:50% 50%;-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite} .control-filter-popover .filter-items li.animate-enter {-webkit-animation:fadeInUp 0.5s;animation:fadeInUp 0.5s} -.control-filter-popover .filter-active-items a:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;*margin-right:.3em;content:"\f00d"} +.control-filter-popover .filter-active-items a:before {font-family:FontAwesome;font-weight:normal;font-style:normal;text-decoration:inherit;-webkit-font-smoothing:antialiased;content:"\f00d"} .control-filter-popover .filter-active-items li.animate-enter {-webkit-animation:fadeInDown 0.5s;animation:fadeInDown 0.5s} .control-filter-popover.control-filter-box-popover {min-width:190px} .control-filter-popover.control-filter-box-popover .filter-buttons {margin:0;padding:0} @@ -4877,6 +4893,8 @@ ul.autocomplete.dropdown-menu.inspector-autocomplete li a {padding:5px 12px;whit @-moz-keyframes popup-shake {10%,90% {-moz-transform:translate3d(-1px,0,0) }20%,80% {-moz-transform:translate3d(2px,0,0) }30%,50%,70% {-moz-transform:translate3d(-4px,0,0) }40%,60% {-moz-transform:translate3d(4px,0,0) }} @-webkit-keyframes popup-shake {10%,90% {-webkit-transform:translate3d(-1px,0,0) }20%,80% {-webkit-transform:translate3d(2px,0,0) }30%,50%,70% {-webkit-transform:translate3d(-4px,0,0) }40%,60% {-webkit-transform:translate3d(4px,0,0) }} @keyframes popup-shake {10%,90% {transform:translate3d(-1px,0,0) }20%,80% {transform:translate3d(2px,0,0) }30%,50%,70% {transform:translate3d(-4px,0,0) }40%,60% {transform:translate3d(4px,0,0) }} +.taglist--preview {overflow:hidden;list-style-type:none;padding-top:0;padding-left:0;margin:0} +.taglist--preview .taglist__item {color:#515c5d;background:#fff;border:1px solid #ccc;border-radius:4px;cursor:default;float:left;margin:8px 0 0 6.5px;padding:0 6px} .pika-single {display:block;position:relative;width:240px;padding:8px;color:#333;background:#fff} .pika-single.is-hidden {display:none} .pika-single.is-bound {position:absolute;box-shadow:0 5px 15px -5px rgba(0,0,0,0.5)} diff --git a/modules/system/assets/ui/storm.less b/modules/system/assets/ui/storm.less index 490f0e431..d2078bef0 100644 --- a/modules/system/assets/ui/storm.less +++ b/modules/system/assets/ui/storm.less @@ -1,5 +1,5 @@ /* - __ _____ __ __ __ __ _____ __ __ + __ _____ __ __ __ __ _____ __ __ / \/ | / \|__)|_ |__) (_ | / \|__)|\/| \__/\__ | \__/|__)|__| \ __) | \__/| \ | | @@ -31,4 +31,5 @@ @import "less/popup.less"; @import "less/checkbox.less"; @import "less/select.less"; +@import "less/taglist.less"; @import "less/datepicker.less"; diff --git a/modules/system/assets/ui/vendor/modernizr/modernizr.js b/modules/system/assets/ui/vendor/modernizr/modernizr.js index 547aaf397..60edf6d9d 100644 --- a/modules/system/assets/ui/vendor/modernizr/modernizr.js +++ b/modules/system/assets/ui/vendor/modernizr/modernizr.js @@ -1,3 +1,5 @@ -/*! modernizr 3.6.0 (Custom Build) | MIT * - * https://modernizr.com/download/?-applicationcache-audio-backgroundsize-borderimage-borderradius-boxshadow-canvas-canvastext-cssanimations-csscolumns-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-flexbox-flexboxlegacy-fontface-forcetouch-generatedcontent-geolocation-hashchange-history-hsla-indexeddb-inlinesvg-input-inputtypes-localstorage-multiplebgs-opacity-postmessage-rgba-sessionstorage-smil-svg-svgclippaths-textshadow-touchevents-video-webgl-websockets-websqldatabase-webworkers-domprefixes-hasevent-mq-prefixes-setclasses-shiv-testallprops-testprop-teststyles !*/ -!function(e,t,n){function r(e,t){return typeof e===t}function o(){var e,t,n,o,a,i,s;for(var c in w)if(w.hasOwnProperty(c)){if(e=[],t=w[c],t.name&&(e.push(t.name.toLowerCase()),t.options&&t.options.aliases&&t.options.aliases.length))for(n=0;nf;f++)if(m=e[f],g=G.style[m],s(m,"-")&&(m=d(m)),G.style[m]!==n){if(a||r(o,"undefined"))return c(),"pfx"==t?m:!0;try{G.style[m]=o}catch(y){}if(G.style[m]!=g)return c(),"pfx"==t?m:!0}return c(),!1}function y(e,t,n,o,a){var i=e.charAt(0).toUpperCase()+e.slice(1),s=(e+" "+U.join(i+" ")+i).split(" ");return r(t,"string")||r(t,"undefined")?v(s,t,o,a):(s=(e+" "+O.join(i+" ")+i).split(" "),p(s,t,n))}function b(e,t,r){return y(e,n,n,t,r)}function T(e,t){var n=e.deleteDatabase(t);n.onsuccess=function(){u("indexeddb.deletedatabase",!0)},n.onerror=function(){u("indexeddb.deletedatabase",!1)}}var x=[],w=[],S={_version:"3.6.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){w.push({name:e,fn:t,options:n})},addAsyncTest:function(e){w.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=S,Modernizr=new Modernizr,Modernizr.addTest("applicationcache","applicationCache"in e),Modernizr.addTest("geolocation","geolocation"in navigator),Modernizr.addTest("history",function(){var t=navigator.userAgent;return-1===t.indexOf("Android 2.")&&-1===t.indexOf("Android 4.0")||-1===t.indexOf("Mobile Safari")||-1!==t.indexOf("Chrome")||-1!==t.indexOf("Windows Phone")||"file:"===location.protocol?e.history&&"pushState"in e.history:!1}),Modernizr.addTest("postmessage","postMessage"in e),Modernizr.addTest("svg",!!t.createElementNS&&!!t.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect);var C=!1;try{C="WebSocket"in e&&2===e.WebSocket.CLOSING}catch(E){}Modernizr.addTest("websockets",C),Modernizr.addTest("localstorage",function(){var e="modernizr";try{return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("sessionstorage",function(){var e="modernizr";try{return sessionStorage.setItem(e,e),sessionStorage.removeItem(e),!0}catch(t){return!1}}),Modernizr.addTest("websqldatabase","openDatabase"in e),Modernizr.addTest("webworkers","Worker"in e);var _=S._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):["",""];S._prefixes=_;var k=t.documentElement,P="svg"===k.nodeName.toLowerCase();P||!function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=b.elements;return"string"==typeof e?e.split(" "):e}function o(e,t){var n=b.elements;"string"!=typeof n&&(n=n.join(" ")),"string"!=typeof e&&(e=e.join(" ")),b.elements=n+" "+e,l(t)}function a(e){var t=y[e[h]];return t||(t={},v++,e[h]=v,y[v]=t),t}function i(e,n,r){if(n||(n=t),u)return n.createElement(e);r||(r=a(n));var o;return o=r.cache[e]?r.cache[e].cloneNode():g.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!o.canHaveChildren||m.test(e)||o.tagUrn?o:r.frag.appendChild(o)}function s(e,n){if(e||(e=t),u)return e.createDocumentFragment();n=n||a(e);for(var o=n.frag.cloneNode(),i=0,s=r(),c=s.length;c>i;i++)o.createElement(s[i]);return o}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return b.shivMethods?i(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-:]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(b,t.frag)}function l(e){e||(e=t);var r=a(e);return!b.shivCSS||d||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),u||c(e,r),e}var d,u,f="3.7.3",p=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,g=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",v=0,y={};!function(){try{var e=t.createElement("a");e.innerHTML="",d="hidden"in e,u=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){d=!0,u=!0}}();var b={elements:p.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:f,shivCSS:p.shivCSS!==!1,supportsUnknownElements:u,shivMethods:p.shivMethods!==!1,type:"default",shivDocument:l,createElement:i,createDocumentFragment:s,addElements:o};e.html5=b,l(t),"object"==typeof module&&module.exports&&(module.exports=b)}("undefined"!=typeof e?e:this,t);var N="Moz O ms Webkit",O=S._config.usePrefixes?N.toLowerCase().split(" "):[];S._domPrefixes=O;var z=function(){function e(e,t){var o;return e?(t&&"string"!=typeof t||(t=i(t||"div")),e="on"+e,o=e in t,!o&&r&&(t.setAttribute||(t=i("div")),t.setAttribute(e,""),o="function"==typeof t[e],t[e]!==n&&(t[e]=n),t.removeAttribute(e)),o):!1}var r=!("onblur"in t.documentElement);return e}();S.hasEvent=z,Modernizr.addTest("hashchange",function(){return z("hashchange",e)===!1?!1:t.documentMode===n||t.documentMode>7}),Modernizr.addTest("audio",function(){var e=i("audio"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),t.mp3=e.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/,""),t.opus=e.canPlayType('audio/ogg; codecs="opus"')||e.canPlayType('audio/webm; codecs="opus"').replace(/^no$/,""),t.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),t.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("canvas",function(){var e=i("canvas");return!(!e.getContext||!e.getContext("2d"))}),Modernizr.addTest("canvastext",function(){return Modernizr.canvas===!1?!1:"function"==typeof i("canvas").getContext("2d").fillText}),Modernizr.addTest("video",function(){var e=i("video"),t=!1;try{t=!!e.canPlayType,t&&(t=new Boolean(t),t.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),t.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),t.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""),t.vp9=e.canPlayType('video/webm; codecs="vp9"').replace(/^no$/,""),t.hls=e.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/,""))}catch(n){}return t}),Modernizr.addTest("webgl",function(){var t=i("canvas"),n="probablySupportsContext"in t?"probablySupportsContext":"supportsContext";return n in t?t[n]("webgl")||t[n]("experimental-webgl"):"WebGLRenderingContext"in e}),Modernizr.addTest("cssgradients",function(){for(var e,t="background-image:",n="gradient(linear,left top,right bottom,from(#9f9),to(white));",r="",o=0,a=_.length-1;a>o;o++)e=0===o?"to ":"",r+=t+_[o]+"linear-gradient("+e+"left top, #9f9, white);";Modernizr._config.usePrefixes&&(r+=t+"-webkit-"+n);var s=i("a"),c=s.style;return c.cssText=r,(""+c.backgroundImage).indexOf("gradient")>-1}),Modernizr.addTest("multiplebgs",function(){var e=i("a").style;return e.cssText="background:url(https://),url(https://),red url(https://)",/(url\s*\(.*?){3}/.test(e.background)}),Modernizr.addTest("opacity",function(){var e=i("a").style;return e.cssText=_.join("opacity:.55;"),/^0.55$/.test(e.opacity)}),Modernizr.addTest("rgba",function(){var e=i("a").style;return e.cssText="background-color:rgba(150,255,150,.5)",(""+e.backgroundColor).indexOf("rgba")>-1}),Modernizr.addTest("inlinesvg",function(){var e=i("div");return e.innerHTML="","http://www.w3.org/2000/svg"==("undefined"!=typeof SVGRect&&e.firstChild&&e.firstChild.namespaceURI)});var R=i("input"),A="autocomplete autofocus list placeholder max min multiple pattern required step".split(" "),M={};Modernizr.input=function(t){for(var n=0,r=t.length;r>n;n++)M[t[n]]=!!(t[n]in R);return M.list&&(M.list=!(!i("datalist")||!e.HTMLDataListElement)),M}(A);var $="search tel url email datetime date month week time datetime-local number range color".split(" "),B={};Modernizr.inputtypes=function(e){for(var r,o,a,i=e.length,s="1)",c=0;i>c;c++)R.setAttribute("type",r=e[c]),a="text"!==R.type&&"style"in R,a&&(R.value=s,R.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(r)&&R.style.WebkitAppearance!==n?(k.appendChild(R),o=t.defaultView,a=o.getComputedStyle&&"textfield"!==o.getComputedStyle(R,null).WebkitAppearance&&0!==R.offsetHeight,k.removeChild(R)):/^(search|tel)$/.test(r)||(a=/^(url|email)$/.test(r)?R.checkValidity&&R.checkValidity()===!1:R.value!=s)),B[e[c]]=!!a;return B}($),Modernizr.addTest("hsla",function(){var e=i("a").style;return e.cssText="background-color:hsla(120,40%,100%,.5)",s(e.backgroundColor,"rgba")||s(e.backgroundColor,"hsla")});var j="CSS"in e&&"supports"in e.CSS,L="supportsCSS"in e;Modernizr.addTest("supports",j||L);var D={}.toString;Modernizr.addTest("svgclippaths",function(){return!!t.createElementNS&&/SVGClipPath/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","clipPath")))}),Modernizr.addTest("smil",function(){return!!t.createElementNS&&/SVGAnimate/.test(D.call(t.createElementNS("http://www.w3.org/2000/svg","animate")))});var F=function(){var t=e.matchMedia||e.msMatchMedia;return t?function(e){var n=t(e);return n&&n.matches||!1}:function(t){var n=!1;return l("@media "+t+" { #modernizr { position: absolute; } }",function(t){n="absolute"==(e.getComputedStyle?e.getComputedStyle(t,null):t.currentStyle).position}),n}}();S.mq=F;var I=S.testStyles=l,W=function(){var e=navigator.userAgent,t=e.match(/w(eb)?osbrowser/gi),n=e.match(/windows phone/gi)&&e.match(/iemobile\/([0-9])+/gi)&&parseFloat(RegExp.$1)>=9;return t||n}();W?Modernizr.addTest("fontface",!1):I('@font-face {font-family:"font";src:url("https://")}',function(e,n){var r=t.getElementById("smodernizr"),o=r.sheet||r.styleSheet,a=o?o.cssRules&&o.cssRules[0]?o.cssRules[0].cssText:o.cssText||"":"",i=/src/i.test(a)&&0===a.indexOf(n.split(" ")[0]);Modernizr.addTest("fontface",i)}),I('#modernizr{font:0/0 a}#modernizr:after{content:":)";visibility:hidden;font:7px/1 a}',function(e){Modernizr.addTest("generatedcontent",e.offsetHeight>=6)}),Modernizr.addTest("touchevents",function(){var n;if("ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch)n=!0;else{var r=["@media (",_.join("touch-enabled),("),"heartz",")","{#modernizr{top:9px;position:absolute}}"].join("");I(r,function(e){n=9===e.offsetTop})}return n});var U=S._config.usePrefixes?N.split(" "):[];S._cssomPrefixes=U;var V=function(t){var r,o=_.length,a=e.CSSRule;if("undefined"==typeof a)return n;if(!t)return!1;if(t=t.replace(/^@/,""),r=t.replace(/-/g,"_").toUpperCase()+"_RULE",r in a)return"@"+t;for(var i=0;o>i;i++){var s=_[i],c=s.toUpperCase()+"_"+r;if(c in a)return"@-"+s.toLowerCase()+"-"+t}return!1};S.atRule=V;var q;!function(){var e={}.hasOwnProperty;q=r(e,"undefined")||r(e.call,"undefined")?function(e,t){return t in e&&r(e.constructor.prototype[t],"undefined")}:function(t,n){return e.call(t,n)}}(),S._l={},S.on=function(e,t){this._l[e]||(this._l[e]=[]),this._l[e].push(t),Modernizr.hasOwnProperty(e)&&setTimeout(function(){Modernizr._trigger(e,Modernizr[e])},0)},S._trigger=function(e,t){if(this._l[e]){var n=this._l[e];setTimeout(function(){var e,r;for(e=0;e",r.insertBefore(n.lastChild,r.firstChild)}function f(){var e=h.elements;return"string"==typeof e?e.split(" "):e}function p(e){var t=c[e[r]];return t||(t={},d++,e[r]=d,c[d]=t),t}function l(e,t,n){return t||(t=i),s?t.createElement(e):(n||(n=p(t)),!(r=n.cache[e]?n.cache[e].cloneNode():a.test(e)?(n.cache[e]=n.createElem(e)).cloneNode():n.createElem(e)).canHaveChildren||o.test(e)||r.tagUrn?r:n.frag.appendChild(r));var r}function m(e){e||(e=i);var t=p(e);return!h.shivCSS||n||t.hasCSS||(t.hasCSS=!!u(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),s||function(t,n){n.cache||(n.cache={},n.createElem=t.createElement,n.createFrag=t.createDocumentFragment,n.frag=n.createFrag()),t.createElement=function(e){return h.shivMethods?l(e,t,n):n.createElem(e)},t.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+f().join().replace(/[\w\-:]+/g,function(e){return n.createElem(e),n.frag.createElement(e),'c("'+e+'")'})+");return n}")(h,n.frag)}(e,t),e}!function(){try{var e=i.createElement("a");e.innerHTML="",n="hidden"in e,s=1==e.childNodes.length||function(){i.createElement("a");var e=i.createDocumentFragment();return void 0===e.cloneNode||void 0===e.createDocumentFragment||void 0===e.createElement}()}catch(e){s=n=!0}}();var h={elements:t.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:"3.7.3",shivCSS:!1!==t.shivCSS,supportsUnknownElements:s,shivMethods:!1!==t.shivMethods,type:"default",shivDocument:m,createElement:l,createDocumentFragment:function(e,t){if(e||(e=i),s)return e.createDocumentFragment();for(var n=(t=t||p(e)).frag.cloneNode(),r=0,o=f(),a=o.length;r+~])("+f().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),a="$1"+y+"\\:$2";r--;)(t=n[r]=n[r].split("}"))[t.length-1]=t[t.length-1].replace(o,a),n[r]=t.join("}");return n.join("{")}(o.reverse().join("")),c=function(e){for(var t,n=e.getElementsByTagName("*"),r=n.length,o=RegExp("^(?:"+f().join("|")+")$","i"),a=[];r--;)t=n[r],o.test(t.nodeName)&&a.push(t.applyElement(T(t)));return a}(s),d=u(s,o)}),n.attachEvent("onafterprint",function(){!function(e){for(var t=e.length;t--;)e[t].removeNode()}(c),clearTimeout(e._removeSheetTimer),e._removeSheetTimer=setTimeout(l,500)}),s.printShived=!0,s}h.type+=" print",(h.shivPrint=x)(i),"object"==typeof module&&module.exports&&(module.exports=h)}(void 0!==f?f:this,u);var T=e._config.usePrefixes?t.split(" "):[];function x(e,t){return!!~(""+e).indexOf(t)}e._cssomPrefixes=T;var w={elem:v("modernizr")};c._q.push(function(){delete w.elem});var S={style:w.elem.style};function C(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function E(e,t,n){var r;if("getComputedStyle"in f){r=getComputedStyle.call(f,e,t);var o=f.console;if(null!==r)n&&(r=r.getPropertyValue(n));else if(o)o[o.error?"error":"log"].call(o,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}else r=!t&&e.currentStyle&&e.currentStyle[n];return r}function k(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function _(e,t,n,r){if(r=!m(r,"undefined")&&r,!m(n,"undefined")){var o=function(e,t){var n=e.length;if("CSS"in f&&"supports"in f.CSS){for(;n--;)if(f.CSS.supports(C(e[n]),t))return!0;return!1}if("CSSSupportsRule"in f){for(var r=[];n--;)r.push("("+C(e[n])+":"+t+")");return y("@supports ("+(r=r.join(" or "))+") { #modernizr { position: absolute; } }",function(e){return"absolute"===E(e,null,"position")})}return p}(e,n);if(!m(o,"undefined"))return o}for(var a,i,s,d,c,l=["modernizr","tspan","samp"];!S.style&&l.length;)a=!0,S.modElem=v(l.shift()),S.style=S.modElem.style;function u(){a&&(delete S.style,delete S.modElem)}for(s=e.length,i=0;i= 9 ) ? - ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'], + // Browser globals + factory( jQuery ); + } +} )( function( $ ) { + + var toFix = [ "wheel", "mousewheel", "DOMMouseScroll", "MozMousePixelScroll" ], + toBind = ( "onwheel" in window.document || window.document.documentMode >= 9 ) ? + [ "wheel" ] : [ "mousewheel", "DomMouseScroll", "MozMousePixelScroll" ], slice = Array.prototype.slice, nullLowestDeltaTimeout, lowestDelta; if ( $.event.fixHooks ) { for ( var i = toFix.length; i; ) { - $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks; + $.event.fixHooks[ toFix[ --i ] ] = $.event.mouseHooks; } } var special = $.event.special.mousewheel = { - version: '3.1.9', + version: "3.2.0", setup: function() { if ( this.addEventListener ) { for ( var i = toBind.length; i; ) { - this.addEventListener( toBind[--i], handler, false ); + this.addEventListener( toBind[ --i ], handler, false ); } } else { this.onmousewheel = handler; } + // Store the line height and page height for this particular element - $.data(this, 'mousewheel-line-height', special.getLineHeight(this)); - $.data(this, 'mousewheel-page-height', special.getPageHeight(this)); + $.data( this, "mousewheel-line-height", special.getLineHeight( this ) ); + $.data( this, "mousewheel-page-height", special.getPageHeight( this ) ); }, teardown: function() { if ( this.removeEventListener ) { for ( var i = toBind.length; i; ) { - this.removeEventListener( toBind[--i], handler, false ); + this.removeEventListener( toBind[ --i ], handler, false ); } } else { this.onmousewheel = null; } + + // Clean up the data we added to the element + $.removeData( this, "mousewheel-line-height" ); + $.removeData( this, "mousewheel-page-height" ); }, - getLineHeight: function(elem) { - return parseInt($(elem)['offsetParent' in $.fn ? 'offsetParent' : 'parent']().css('fontSize'), 10); + getLineHeight: function( elem ) { + var $elem = $( elem ), + $parent = $elem[ "offsetParent" in $.fn ? "offsetParent" : "parent" ](); + if ( !$parent.length ) { + $parent = $( "body" ); + } + return parseInt( $parent.css( "fontSize" ), 10 ) || + parseInt( $elem.css( "fontSize" ), 10 ) || 16; }, - getPageHeight: function(elem) { - return $(elem).height(); + getPageHeight: function( elem ) { + return $( elem ).height(); }, settings: { - adjustOldDeltas: true + adjustOldDeltas: true, // see shouldAdjustOldDeltas() below + normalizeOffset: true // calls getBoundingClientRect for each event } }; - $.fn.extend({ - mousewheel: function(fn) { - return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel'); + $.fn.extend( { + mousewheel: function( fn ) { + return fn ? this.on( "mousewheel", fn ) : this.trigger( "mousewheel" ); }, - unmousewheel: function(fn) { - return this.unbind('mousewheel', fn); + unmousewheel: function( fn ) { + return this.off( "mousewheel", fn ); } - }); + } ); - function handler(event) { + function handler( event ) { var orgEvent = event || window.event, - args = slice.call(arguments, 1), + args = slice.call( arguments, 1 ), delta = 0, deltaX = 0, deltaY = 0, absDelta = 0; - event = $.event.fix(orgEvent); - event.type = 'mousewheel'; + event = $.event.fix( orgEvent ); + event.type = "mousewheel"; // Old school scrollwheel delta - if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; } - if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; } - if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; } - if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; } + if ( "detail" in orgEvent ) { + deltaY = orgEvent.detail * -1; + } + if ( "wheelDelta" in orgEvent ) { + deltaY = orgEvent.wheelDelta; + } + if ( "wheelDeltaY" in orgEvent ) { + deltaY = orgEvent.wheelDeltaY; + } + if ( "wheelDeltaX" in orgEvent ) { + deltaX = orgEvent.wheelDeltaX * -1; + } // Firefox < 17 horizontal scrolling related to DOMMouseScroll event - if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) { + if ( "axis" in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) { deltaX = deltaY * -1; deltaY = 0; } @@ -107,17 +127,21 @@ delta = deltaY === 0 ? deltaX : deltaY; // New school wheel delta (wheel event) - if ( 'deltaY' in orgEvent ) { + if ( "deltaY" in orgEvent ) { deltaY = orgEvent.deltaY * -1; delta = deltaY; } - if ( 'deltaX' in orgEvent ) { + if ( "deltaX" in orgEvent ) { deltaX = orgEvent.deltaX; - if ( deltaY === 0 ) { delta = deltaX * -1; } + if ( deltaY === 0 ) { + delta = deltaX * -1; + } } // No change actually happened, no reason to go any further - if ( deltaY === 0 && deltaX === 0 ) { return; } + if ( deltaY === 0 && deltaX === 0 ) { + return; + } // Need to convert lines and pages to pixels if we aren't already in pixels // There are three delta modes: @@ -125,31 +149,32 @@ // * deltaMode 1 is by lines // * deltaMode 2 is by pages if ( orgEvent.deltaMode === 1 ) { - var lineHeight = $.data(this, 'mousewheel-line-height'); + var lineHeight = $.data( this, "mousewheel-line-height" ); delta *= lineHeight; deltaY *= lineHeight; deltaX *= lineHeight; } else if ( orgEvent.deltaMode === 2 ) { - var pageHeight = $.data(this, 'mousewheel-page-height'); + var pageHeight = $.data( this, "mousewheel-page-height" ); delta *= pageHeight; deltaY *= pageHeight; deltaX *= pageHeight; } // Store lowest absolute delta to normalize the delta values - absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) ); + absDelta = Math.max( Math.abs( deltaY ), Math.abs( deltaX ) ); if ( !lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; // Adjust older deltas if necessary - if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) { + if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) { lowestDelta /= 40; } } // Adjust older deltas if necessary - if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) { + if ( shouldAdjustOldDeltas( orgEvent, absDelta ) ) { + // Divide all the things by 40! delta /= 40; deltaX /= 40; @@ -157,37 +182,48 @@ } // Get a whole, normalized value for the deltas - delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta); - deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta); - deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta); + delta = Math[ delta >= 1 ? "floor" : "ceil" ]( delta / lowestDelta ); + deltaX = Math[ deltaX >= 1 ? "floor" : "ceil" ]( deltaX / lowestDelta ); + deltaY = Math[ deltaY >= 1 ? "floor" : "ceil" ]( deltaY / lowestDelta ); + + // Normalise offsetX and offsetY properties + if ( special.settings.normalizeOffset && this.getBoundingClientRect ) { + var boundingRect = this.getBoundingClientRect(); + event.offsetX = event.clientX - boundingRect.left; + event.offsetY = event.clientY - boundingRect.top; + } // Add information to the event object event.deltaX = deltaX; event.deltaY = deltaY; event.deltaFactor = lowestDelta; + // Go ahead and set deltaMode to 0 since we converted to pixels // Although this is a little odd since we overwrite the deltaX/Y // properties with normalized deltas. event.deltaMode = 0; // Add event and delta to the front of the arguments - args.unshift(event, delta, deltaX, deltaY); + args.unshift( event, delta, deltaX, deltaY ); // Clearout lowestDelta after sometime to better // handle multiple device types that give different // a different lowestDelta // Ex: trackpad = 3 and mouse wheel = 120 - if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); } - nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200); + if ( nullLowestDeltaTimeout ) { + window.clearTimeout( nullLowestDeltaTimeout ); + } + nullLowestDeltaTimeout = window.setTimeout( nullLowestDelta, 200 ); - return ($.event.dispatch || $.event.handle).apply(this, args); + return ( $.event.dispatch || $.event.handle ).apply( this, args ); } function nullLowestDelta() { lowestDelta = null; } - function shouldAdjustOldDeltas(orgEvent, absDelta) { + function shouldAdjustOldDeltas( orgEvent, absDelta ) { + // If this is an older event and the delta is divisable by 120, // then we are assuming that the browser is treating this as an // older mouse wheel event and that we should divide the deltas @@ -195,7 +231,8 @@ // Side note, this actually impacts the reported scroll distance // in older browsers and can cause scrolling to be slower than native. // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false. - return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0; + return special.settings.adjustOldDeltas && orgEvent.type === "mousewheel" && + absDelta % 120 === 0; } -})); \ No newline at end of file +} ); diff --git a/modules/system/assets/ui/vendor/mustache/mustache.js b/modules/system/assets/ui/vendor/mustache/mustache.js index 344d65b86..e83c5f9c6 100644 --- a/modules/system/assets/ui/vendor/mustache/mustache.js +++ b/modules/system/assets/ui/vendor/mustache/mustache.js @@ -1,56 +1,76 @@ /*! * mustache.js - Logic-less {{mustache}} templates with JavaScript * http://github.com/janl/mustache.js + * version 2.3.2 */ -/*global define: false*/ +/*global define: false Mustache: true*/ -(function (global, factory) { - if (typeof exports === "object" && exports) { +(function defineMustache (global, factory) { + if (typeof exports === 'object' && exports && typeof exports.nodeName !== 'string') { factory(exports); // CommonJS - } else if (typeof define === "function" && define.amd) { + } else if (typeof define === 'function' && define.amd) { define(['exports'], factory); // AMD } else { - factory(global.Mustache = {}); // - \ No newline at end of file + diff --git a/modules/system/views/mail/partial-panel.htm b/modules/system/views/mail/partial-panel.htm index fa6ef4a9f..5fdb48eb0 100644 --- a/modules/system/views/mail/partial-panel.htm +++ b/modules/system/views/mail/partial-panel.htm @@ -2,7 +2,7 @@ name = "Panel" == {{ body|trim }} == - +
diff --git a/modules/system/views/mail/partial-promotion.htm b/modules/system/views/mail/partial-promotion.htm index fe9e877fc..90854f3a6 100644 --- a/modules/system/views/mail/partial-promotion.htm +++ b/modules/system/views/mail/partial-promotion.htm @@ -2,7 +2,7 @@ name = "Promotion" == {{ body|trim }} == -
+
{{ body|md_safe }} diff --git a/package.json b/package.json new file mode 100644 index 000000000..8ba2b08a6 --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "octobercms", + "description": "Free, open-source, self-hosted CMS platform based on the Laravel PHP Framework.", + "directories": { + "test": "tests/js/cases", + "helpers": "tests/js/helpers" + }, + "scripts": { + "test": "mocha --require @babel/register tests/js/cases/**/*.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/octobercms/october.git" + }, + "contributors": [ + { + "name": "Alexey Bobkov", + "email": "aleksey.bobkov@gmail.com" + }, + { + "name": "Samuel Georges", + "email": "daftspunky@gmail.com" + }, + { + "name": "Luke Towers", + "email": "octobercms@luketowers.ca", + "url": "https://luketowers.ca" + } + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/octobercms/october/issues" + }, + "homepage": "https://octobercms.com/", + "devDependencies": { + "@babel/cli": "^7.5.5", + "@babel/core": "^7.5.5", + "@babel/node": "^7.5.5", + "@babel/preset-env": "^7.5.5", + "@babel/register": "^7.5.5", + "babel-plugin-module-resolver": "^3.2.0", + "chai": "^4.2.0", + "jquery": "^3.4.1", + "jsdom": "^15.1.1", + "mocha": "^6.2.0", + "sinon": "^7.4.1" + } +} diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 000000000..fb9d971f8 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,42 @@ + + + The coding standard for October CMS. + + + + + + + + + + */database/migrations/*\.php + */tests/* + + + + + */tests/* + + + bootstrap/ + config/ + modules/ + plugins/october/demo/ + tests/ + + + */vendor/* + + modules/system/views/exception.php + + tests/fixtures/plugins/testvendor/goto/Plugin.php + diff --git a/phpunit.xml b/phpunit.xml index d75f4e0df..08cd19d58 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -18,9 +18,26 @@ ./vendor/october/rain/tests + + + + ./modules/ + + + ./modules/backend/routes.php + ./modules/cms/routes.php + ./modules/system/routes.php + + ./modules/backend/database + ./modules/cms/database + ./modules/system/database + + + + - \ No newline at end of file + diff --git a/plugins/october/demo/components/todo/default.htm b/plugins/october/demo/components/todo/default.htm index c9f7b228e..e30428c8e 100644 --- a/plugins/october/demo/components/todo/default.htm +++ b/plugins/october/demo/components/todo/default.htm @@ -1,15 +1,15 @@ -
+{{ form_ajax(__SELF__ ~ '::onAddItem', { + update: "'" ~ __SELF__ ~ "::list': '#result'", + success: "$('#input-item').val('')", + 'data-request-flash': '' +}) }}

To Do List

- + @@ -18,4 +18,4 @@
- +{{ form_close() }} diff --git a/tests/PluginTestCase.php b/tests/PluginTestCase.php index 06abaffc0..728d5d430 100644 --- a/tests/PluginTestCase.php +++ b/tests/PluginTestCase.php @@ -1,11 +1,15 @@ setDefaultDriver('array'); $app->setLocale('en'); + $app->singleton('auth', function ($app) { + $app['auth.loaded'] = true; + + return AuthManager::instance(); + }); + /* * Store database in memory by default, if not specified otherwise */ diff --git a/tests/TestCase.php b/tests/TestCase.php index ee25a6f42..062f67062 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,8 +1,6 @@ make('Illuminate\Contracts\Console\Kernel')->bootstrap(); $app['cache']->setDefaultDriver('array'); diff --git a/tests/UiTestCase.php b/tests/UiTestCase.php index f29b4fb4f..66354672c 100644 --- a/tests/UiTestCase.php +++ b/tests/UiTestCase.php @@ -2,24 +2,31 @@ class UiTestCase extends PHPUnit_Extensions_SeleniumTestCase { - protected function setUp() { /* * Look for selenium configuration */ - if (file_exists($seleniumEnv = __DIR__.'/../selenium.php')) + if (file_exists($seleniumEnv = __DIR__.'/../selenium.php')) { require_once $seleniumEnv; + } /* * Configure selenium */ - if (!defined('TEST_SELENIUM_URL')) + if (!defined('TEST_SELENIUM_URL')) { return $this->markTestSkipped('Selenium skipped'); + } - if (defined('TEST_SELENIUM_HOST')) $this->setHost(TEST_SELENIUM_HOST); - if (defined('TEST_SELENIUM_PORT')) $this->setPort(TEST_SELENIUM_PORT); - if (defined('TEST_SELENIUM_BROWSER')) $this->setBrowser(TEST_SELENIUM_BROWSER); + if (defined('TEST_SELENIUM_HOST')) { + $this->setHost(TEST_SELENIUM_HOST); + } + if (defined('TEST_SELENIUM_PORT')) { + $this->setPort(TEST_SELENIUM_PORT); + } + if (defined('TEST_SELENIUM_BROWSER')) { + $this->setBrowser(TEST_SELENIUM_BROWSER); + } $this->setBrowserUrl(TEST_SELENIUM_URL); } @@ -60,32 +67,45 @@ class UiTestCase extends PHPUnit_Extensions_SeleniumTestCase protected function waitForElementPresent($target, $timeout = 60) { - for ($second = 0; ; $second++) { - if ($second >= $timeout) + $second = 0; + + while (true) { + if ($second >= $timeout) { $this->fail('timeout'); + } try { - if ($this->isElementPresent($target)) break; + if ($this->isElementPresent($target)) { + break; + } + } + catch (Exception $e) { } - catch (Exception $e) {} sleep(1); + ++$second; } } protected function waitForElementNotPresent($target, $timeout = 60) { - for ($second = 0; ; $second++) { - if ($second >= $timeout) + $second = 0; + + while (true) { + if ($second >= $timeout) { $this->fail('timeout'); + } try { - if (!$this->isElementPresent($target)) break; + if (!$this->isElementPresent($target)) { + break; + } + } + catch (Exception $e) { } - catch (Exception $e) {} sleep(1); + ++$second; } } - } diff --git a/tests/concerns/InteractsWithAuthentication.php b/tests/concerns/InteractsWithAuthentication.php new file mode 100644 index 000000000..a7950f230 --- /dev/null +++ b/tests/concerns/InteractsWithAuthentication.php @@ -0,0 +1,149 @@ +be($user, $driver); + + return $this; + } + + /** + * Set the currently logged in user for the application. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param string|null $driver + * @return void + */ + public function be(UserContract $user, $driver = null) + { + $this->app['auth']->setUser($user); + } + + /** + * Assert that the user is authenticated. + * + * @param string|null $guard + * @return $this + */ + public function assertAuthenticated($guard = null) + { + $this->assertTrue($this->isAuthenticated($guard), 'The user is not authenticated'); + + return $this; + } + + /** + * Assert that the user is not authenticated. + * + * @param string|null $guard + * @return $this + */ + public function assertGuest($guard = null) + { + $this->assertFalse($this->isAuthenticated($guard), 'The user is authenticated'); + + return $this; + } + + /** + * Return true if the user is authenticated, false otherwise. + * + * @param string|null $guard + * @return bool + */ + protected function isAuthenticated($guard = null) + { + return $this->app->make('auth')->guard($guard)->check(); + } + + /** + * Assert that the user is authenticated as the given user. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param string|null $guard + * @return $this + */ + public function assertAuthenticatedAs($user, $guard = null) + { + $expected = $this->app->make('auth')->guard($guard)->user(); + + $this->assertNotNull($expected, 'The current user is not authenticated.'); + + $this->assertInstanceOf( + get_class($expected), + $user, + 'The currently authenticated user is not who was expected' + ); + + $this->assertSame( + $expected->getAuthIdentifier(), + $user->getAuthIdentifier(), + 'The currently authenticated user is not who was expected' + ); + + return $this; + } + + /** + * Assert that the given credentials are valid. + * + * @param array $credentials + * @param string|null $guard + * @return $this + */ + public function assertCredentials(array $credentials, $guard = null) + { + $this->assertTrue( + $this->hasCredentials($credentials, $guard), + 'The given credentials are invalid.' + ); + + return $this; + } + + /** + * Assert that the given credentials are invalid. + * + * @param array $credentials + * @param string|null $guard + * @return $this + */ + public function assertInvalidCredentials(array $credentials, $guard = null) + { + $this->assertFalse( + $this->hasCredentials($credentials, $guard), + 'The given credentials are valid.' + ); + + return $this; + } + + /** + * Return true if the credentials are valid, false otherwise. + * + * @param array $credentials + * @param string|null $guard + * @return bool + */ + protected function hasCredentials(array $credentials, $guard = null) + { + $provider = $this->app->make('auth')->guard($guard)->getProvider(); + + $user = $provider->retrieveByCredentials($credentials); + + return $user && $provider->validateCredentials($user, $credentials); + } +} diff --git a/tests/fixtures/backend/assets/compilation.js b/tests/fixtures/backend/assets/compilation.js new file mode 100644 index 000000000..3d4f4fa57 --- /dev/null +++ b/tests/fixtures/backend/assets/compilation.js @@ -0,0 +1,5 @@ +/* Comments + +=require js/file1.js +=require js/file2.js +*/ diff --git a/tests/fixtures/backend/assets/js/file1.js b/tests/fixtures/backend/assets/js/file1.js new file mode 100644 index 000000000..8fffa07fb --- /dev/null +++ b/tests/fixtures/backend/assets/js/file1.js @@ -0,0 +1 @@ +console.log('Test File 1'); diff --git a/tests/fixtures/backend/assets/js/file2.js b/tests/fixtures/backend/assets/js/file2.js new file mode 100644 index 000000000..7d129a4b8 --- /dev/null +++ b/tests/fixtures/backend/assets/js/file2.js @@ -0,0 +1 @@ +console.log('Test File 2'); diff --git a/tests/fixtures/backend/assets/not-compilation.js b/tests/fixtures/backend/assets/not-compilation.js new file mode 100644 index 000000000..40238d60d --- /dev/null +++ b/tests/fixtures/backend/assets/not-compilation.js @@ -0,0 +1 @@ +console.log('Legitimate file'); diff --git a/tests/fixtures/backend/models/UserFixture.php b/tests/fixtures/backend/models/UserFixture.php new file mode 100644 index 000000000..a444cd597 --- /dev/null +++ b/tests/fixtures/backend/models/UserFixture.php @@ -0,0 +1,66 @@ +fill([ + 'first_name' => 'Test', + 'last_name' => 'User', + 'login' => 'testuser', + 'email' => 'testuser@test.com', + 'password' => '', + 'activation_code' => null, + 'persist_code' => null, + 'reset_password_code' => null, + 'permissions' => null, + 'is_activated' => true, + 'role_id' => null, + 'activated_at' => null, + 'last_login' => '2019-09-27 12:00:00', + 'created_at' => '2019-09-27 12:00:00', + 'updated_at' => '2019-09-27 12:00:00', + 'deleted_at' => null, + 'is_superuser' => false + ]); + } + + public function asSuperUser() + { + $this->setAttribute('is_superuser', true); + + return $this; + } + + public function asDeletedUser() + { + $this->setAttribute('deleted_at', date('Y-m-d H:i:s')); + + return $this; + } + + public function withPermission($permission, bool $granted = true) + { + $currentPermissions = $this->getAttribute('permissions'); + + if (is_string($permission)) { + $permission = [ + $permission => (int) $granted + ]; + } + + if (is_array($currentPermissions)) { + $this->setAttribute('permissions', array_replace($currentPermissions, $permission)); + } else { + $this->setAttribute('permissions', $permission); + } + + return $this; + } +} diff --git a/tests/fixtures/plugins/database/tester/Plugin.php b/tests/fixtures/plugins/database/tester/Plugin.php index ad118519f..bd3796d28 100644 --- a/tests/fixtures/plugins/database/tester/Plugin.php +++ b/tests/fixtures/plugins/database/tester/Plugin.php @@ -4,7 +4,6 @@ use System\Classes\PluginBase; class Plugin extends PluginBase { - public function pluginDetails() { return [ @@ -13,5 +12,4 @@ class Plugin extends PluginBase 'author' => 'Alexey Bobkov, Samuel Georges' ]; } - } diff --git a/tests/fixtures/plugins/database/tester/models/EventLog.php b/tests/fixtures/plugins/database/tester/models/EventLog.php index 6e093a249..33a361b41 100644 --- a/tests/fixtures/plugins/database/tester/models/EventLog.php +++ b/tests/fixtures/plugins/database/tester/models/EventLog.php @@ -27,5 +27,4 @@ class EventLog extends Model public $morphTo = [ 'related' => [] ]; - } diff --git a/tests/fixtures/plugins/database/tester/models/Phone.php b/tests/fixtures/plugins/database/tester/models/Phone.php index ffbd8e8cf..54035a5c5 100644 --- a/tests/fixtures/plugins/database/tester/models/Phone.php +++ b/tests/fixtures/plugins/database/tester/models/Phone.php @@ -26,5 +26,4 @@ class Phone extends Model public $belongsTo = [ 'author' => 'Database\Tester\Models\Author', ]; - } diff --git a/tests/fixtures/plugins/database/tester/models/Post.php b/tests/fixtures/plugins/database/tester/models/Post.php index e20410923..421768d9b 100644 --- a/tests/fixtures/plugins/database/tester/models/Post.php +++ b/tests/fixtures/plugins/database/tester/models/Post.php @@ -68,7 +68,6 @@ class SluggablePost extends Post 'slug' => 'title', 'long_slug' => ['title', 'description'] ]; - } class RevisionablePost extends Post @@ -117,7 +116,6 @@ class RevisionablePost extends Post { return 7; } - } class ValidationPost extends Post diff --git a/tests/fixtures/plugins/database/tester/models/Role.php b/tests/fixtures/plugins/database/tester/models/Role.php index 6a2fdd44b..8d561319d 100644 --- a/tests/fixtures/plugins/database/tester/models/Role.php +++ b/tests/fixtures/plugins/database/tester/models/Role.php @@ -7,7 +7,6 @@ use Model; */ class Role extends Model { - /** * @var string The database table used by the model. */ @@ -32,5 +31,4 @@ class Role extends Model 'table' => 'database_tester_authors_roles' ], ]; - } diff --git a/tests/fixtures/plugins/database/tester/models/User.php b/tests/fixtures/plugins/database/tester/models/User.php index 6c4e42b8c..867992c2c 100644 --- a/tests/fixtures/plugins/database/tester/models/User.php +++ b/tests/fixtures/plugins/database/tester/models/User.php @@ -24,7 +24,6 @@ class User extends Model public $attachMany = [ 'photos' => 'System\Models\File' ]; - } class SoftDeleteUser extends User diff --git a/tests/fixtures/plugins/database/tester/updates/create_authors_table.php b/tests/fixtures/plugins/database/tester/updates/create_authors_table.php index 2e542580a..6466e4e13 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_authors_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_authors_table.php @@ -5,11 +5,9 @@ use October\Rain\Database\Updates\Migration; class CreateAuthorsTable extends Migration { - public function up() { - Schema::create('database_tester_authors', function ($table) - { + Schema::create('database_tester_authors', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->integer('user_id')->unsigned()->index()->nullable(); @@ -24,5 +22,4 @@ class CreateAuthorsTable extends Migration { Schema::dropIfExists('database_tester_authors'); } - } diff --git a/tests/fixtures/plugins/database/tester/updates/create_categories_table.php b/tests/fixtures/plugins/database/tester/updates/create_categories_table.php index 49cf8a1c1..39e21890b 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_categories_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_categories_table.php @@ -8,8 +8,7 @@ class CreateCategoriesTable extends Migration public function up() { - Schema::create('database_tester_categories', function ($table) - { + Schema::create('database_tester_categories', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->integer('parent_id')->nullable(); @@ -22,8 +21,7 @@ class CreateCategoriesTable extends Migration $table->softDeletes(); }); - Schema::create('database_tester_categories_nested', function ($table) - { + Schema::create('database_tester_categories_nested', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->integer('parent_id')->nullable(); @@ -45,5 +43,4 @@ class CreateCategoriesTable extends Migration Schema::dropIfExists('database_tester_categories'); Schema::dropIfExists('database_tester_categories_nested'); } - } diff --git a/tests/fixtures/plugins/database/tester/updates/create_event_log_table.php b/tests/fixtures/plugins/database/tester/updates/create_event_log_table.php index 8539d85d8..90c0c7479 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_event_log_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_event_log_table.php @@ -8,8 +8,7 @@ class CreateEventLogTable extends Migration public function up() { - Schema::create('database_tester_event_log', function ($table) - { + Schema::create('database_tester_event_log', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('action', 30)->nullable(); @@ -24,5 +23,4 @@ class CreateEventLogTable extends Migration { Schema::dropIfExists('database_tester_event_log'); } - } diff --git a/tests/fixtures/plugins/database/tester/updates/create_meta_table.php b/tests/fixtures/plugins/database/tester/updates/create_meta_table.php index 3add13d2c..f0e456259 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_meta_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_meta_table.php @@ -8,8 +8,7 @@ class CreateMetaTable extends Migration public function up() { - Schema::create('database_tester_meta', function ($table) - { + Schema::create('database_tester_meta', function ($table) { $table->engine = 'InnoDB'; $table->increments('id')->unsigned(); $table->integer('taggable_id')->unsigned()->index()->nullable(); @@ -28,5 +27,4 @@ class CreateMetaTable extends Migration { Schema::dropIfExists('database_tester_meta'); } - } diff --git a/tests/fixtures/plugins/database/tester/updates/create_phones_table.php b/tests/fixtures/plugins/database/tester/updates/create_phones_table.php index 891344c27..61e02d3f8 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_phones_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_phones_table.php @@ -8,8 +8,7 @@ class CreatePhonesTable extends Migration public function up() { - Schema::create('database_tester_phones', function ($table) - { + Schema::create('database_tester_phones', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('number')->nullable(); @@ -22,5 +21,4 @@ class CreatePhonesTable extends Migration { Schema::dropIfExists('database_tester_phones'); } - } diff --git a/tests/fixtures/plugins/database/tester/updates/create_posts_table.php b/tests/fixtures/plugins/database/tester/updates/create_posts_table.php index 139ea288f..de4b28e4e 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_posts_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_posts_table.php @@ -8,8 +8,7 @@ class CreatePostsTable extends Migration public function up() { - Schema::create('database_tester_posts', function ($table) - { + Schema::create('database_tester_posts', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('title')->nullable(); @@ -29,5 +28,4 @@ class CreatePostsTable extends Migration { Schema::dropIfExists('database_tester_posts'); } - } diff --git a/tests/fixtures/plugins/database/tester/updates/create_roles_table.php b/tests/fixtures/plugins/database/tester/updates/create_roles_table.php index 6ed84b4c9..8a3ae0a69 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_roles_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_roles_table.php @@ -8,8 +8,7 @@ class CreateRolesTable extends Migration public function up() { - Schema::create('database_tester_roles', function ($table) - { + Schema::create('database_tester_roles', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('name')->nullable(); @@ -17,8 +16,7 @@ class CreateRolesTable extends Migration $table->timestamps(); }); - Schema::create('database_tester_authors_roles', function ($table) - { + Schema::create('database_tester_authors_roles', function ($table) { $table->engine = 'InnoDB'; $table->integer('author_id')->unsigned(); $table->integer('role_id')->unsigned(); @@ -33,5 +31,4 @@ class CreateRolesTable extends Migration Schema::dropIfExists('database_tester_roles'); Schema::dropIfExists('database_tester_authors_roles'); } - } diff --git a/tests/fixtures/plugins/database/tester/updates/create_users_table.php b/tests/fixtures/plugins/database/tester/updates/create_users_table.php index 13838f474..455100460 100644 --- a/tests/fixtures/plugins/database/tester/updates/create_users_table.php +++ b/tests/fixtures/plugins/database/tester/updates/create_users_table.php @@ -8,8 +8,7 @@ class CreateUsersTable extends Migration public function up() { - Schema::create('database_tester_users', function ($table) - { + Schema::create('database_tester_users', function ($table) { $table->engine = 'InnoDB'; $table->increments('id'); $table->string('name')->nullable(); @@ -23,5 +22,4 @@ class CreateUsersTable extends Migration { Schema::dropIfExists('database_tester_users'); } - } diff --git a/tests/fixtures/plugins/dependencytest/dependency/Plugin.php b/tests/fixtures/plugins/dependencytest/dependency/Plugin.php new file mode 100644 index 000000000..57bddff38 --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/dependency/Plugin.php @@ -0,0 +1,16 @@ + 'Dependency Test - Dependency', + 'description' => 'This is a test plugin that will act as a dependency for the other test plugins in this + namespace.', + 'author' => 'Ben Thomson' + ]; + } +} diff --git a/tests/fixtures/plugins/dependencytest/dependency/updates/version.yaml b/tests/fixtures/plugins/dependencytest/dependency/updates/version.yaml new file mode 100644 index 000000000..0849ef695 --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/dependency/updates/version.yaml @@ -0,0 +1 @@ +1.0.1: Initial version of the plugin diff --git a/tests/fixtures/plugins/dependencytest/found/Plugin.php b/tests/fixtures/plugins/dependencytest/found/Plugin.php new file mode 100644 index 000000000..df9eeddb2 --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/found/Plugin.php @@ -0,0 +1,19 @@ + 'Dependency Test - Found', + 'description' => 'This is a test plugin with a dependency that exists.', + 'author' => 'Ben Thomson' + ]; + } +} diff --git a/tests/fixtures/plugins/dependencytest/found/updates/version.yaml b/tests/fixtures/plugins/dependencytest/found/updates/version.yaml new file mode 100644 index 000000000..0849ef695 --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/found/updates/version.yaml @@ -0,0 +1 @@ +1.0.1: Initial version of the plugin diff --git a/tests/fixtures/plugins/dependencytest/notfound/Plugin.php b/tests/fixtures/plugins/dependencytest/notfound/Plugin.php new file mode 100644 index 000000000..29b009a5f --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/notfound/Plugin.php @@ -0,0 +1,19 @@ + 'Dependency Test - Not Found', + 'description' => 'This is a test plugin with a dependency that does not exist.', + 'author' => 'Ben Thomson' + ]; + } +} diff --git a/tests/fixtures/plugins/dependencytest/notfound/updates/version.yaml b/tests/fixtures/plugins/dependencytest/notfound/updates/version.yaml new file mode 100644 index 000000000..0849ef695 --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/notfound/updates/version.yaml @@ -0,0 +1 @@ +1.0.1: Initial version of the plugin diff --git a/tests/fixtures/plugins/dependencytest/wrongcase/Plugin.php b/tests/fixtures/plugins/dependencytest/wrongcase/Plugin.php new file mode 100644 index 000000000..7523b921e --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/wrongcase/Plugin.php @@ -0,0 +1,19 @@ + 'Dependency Test - Wrong Case', + 'description' => 'This is a test plugin with a dependency that exists, but is using the wrong letter case.', + 'author' => 'Ben Thomson' + ]; + } +} diff --git a/tests/fixtures/plugins/dependencytest/wrongcase/updates/version.yaml b/tests/fixtures/plugins/dependencytest/wrongcase/updates/version.yaml new file mode 100644 index 000000000..0849ef695 --- /dev/null +++ b/tests/fixtures/plugins/dependencytest/wrongcase/updates/version.yaml @@ -0,0 +1 @@ +1.0.1: Initial version of the plugin diff --git a/tests/fixtures/plugins/october/noupdates/Plugin.php b/tests/fixtures/plugins/october/noupdates/Plugin.php index 5486e2251..9a6481498 100644 --- a/tests/fixtures/plugins/october/noupdates/Plugin.php +++ b/tests/fixtures/plugins/october/noupdates/Plugin.php @@ -4,7 +4,6 @@ use System\Classes\PluginBase; class Plugin extends PluginBase { - public function pluginDetails() { return [ @@ -13,5 +12,4 @@ class Plugin extends PluginBase 'author' => 'Alexey Bobkov, Samuel Georges' ]; } - } diff --git a/tests/fixtures/plugins/october/sample/Plugin.php b/tests/fixtures/plugins/october/sample/Plugin.php index 9d934d1cb..218c89a04 100644 --- a/tests/fixtures/plugins/october/sample/Plugin.php +++ b/tests/fixtures/plugins/october/sample/Plugin.php @@ -4,7 +4,6 @@ use System\Classes\PluginBase; class Plugin extends PluginBase { - public function pluginDetails() { return [ @@ -13,5 +12,4 @@ class Plugin extends PluginBase 'author' => 'Alexey Bobkov, Samuel Georges' ]; } - } diff --git a/tests/fixtures/plugins/october/tester/Plugin.php b/tests/fixtures/plugins/october/tester/Plugin.php index 7a4c8035c..edfee8bf9 100644 --- a/tests/fixtures/plugins/october/tester/Plugin.php +++ b/tests/fixtures/plugins/october/tester/Plugin.php @@ -4,7 +4,6 @@ use System\Classes\PluginBase; class Plugin extends PluginBase { - public function pluginDetails() { return [ @@ -62,5 +61,4 @@ class Plugin extends PluginBase ] ]; } - } diff --git a/tests/fixtures/plugins/october/tester/components/Archive.php b/tests/fixtures/plugins/october/tester/components/Archive.php index 97ec581bd..37b005e02 100644 --- a/tests/fixtures/plugins/october/tester/components/Archive.php +++ b/tests/fixtures/plugins/october/tester/components/Archive.php @@ -4,7 +4,6 @@ use Cms\Classes\ComponentBase; class Archive extends ComponentBase { - public function componentDetails() { return [ @@ -39,5 +38,4 @@ class Archive extends ComponentBase { $this->page['var'] = 'page'; } - } diff --git a/tests/fixtures/plugins/october/tester/components/Categories.php b/tests/fixtures/plugins/october/tester/components/Categories.php index dd2ab1be5..3e0241496 100644 --- a/tests/fixtures/plugins/october/tester/components/Categories.php +++ b/tests/fixtures/plugins/october/tester/components/Categories.php @@ -30,5 +30,4 @@ class Categories extends ComponentBase { $this->page['var'] = 'page'; } - } diff --git a/tests/fixtures/plugins/october/tester/components/Comments.php b/tests/fixtures/plugins/october/tester/components/Comments.php index 227b02863..96658280e 100644 --- a/tests/fixtures/plugins/october/tester/components/Comments.php +++ b/tests/fixtures/plugins/october/tester/components/Comments.php @@ -8,7 +8,7 @@ class Comments extends ComponentBase { private $users; - public function __construct(CodeBase $cmsObject = null, $properties = [], Users $users) + public function __construct(CodeBase $cmsObject = null, $properties = [], Users $users = null) { parent::__construct($cmsObject, $properties); $this->users = $users; @@ -39,5 +39,4 @@ class Comments extends ComponentBase { return $this->users; } - } diff --git a/tests/fixtures/plugins/october/tester/components/ContentBlock.php b/tests/fixtures/plugins/october/tester/components/ContentBlock.php index ea02f1c8a..024ac55c2 100644 --- a/tests/fixtures/plugins/october/tester/components/ContentBlock.php +++ b/tests/fixtures/plugins/october/tester/components/ContentBlock.php @@ -4,7 +4,6 @@ use Cms\Classes\ComponentBase; class ContentBlock extends ComponentBase { - public function componentDetails() { return [ @@ -21,5 +20,4 @@ class ContentBlock extends ComponentBase return 'Pass'; } - } diff --git a/tests/fixtures/plugins/october/tester/components/MainMenu.php b/tests/fixtures/plugins/october/tester/components/MainMenu.php index 131cd2f5a..74b8d41b4 100644 --- a/tests/fixtures/plugins/october/tester/components/MainMenu.php +++ b/tests/fixtures/plugins/october/tester/components/MainMenu.php @@ -4,7 +4,6 @@ use Cms\Classes\ComponentBase; class MainMenu extends ComponentBase { - public function componentDetails() { return [ @@ -17,5 +16,4 @@ class MainMenu extends ComponentBase { return ['Home', 'Blog', 'About', 'Contact']; } - } diff --git a/tests/fixtures/plugins/october/tester/components/Post.php b/tests/fixtures/plugins/october/tester/components/Post.php index 571d99f4b..bab86b037 100644 --- a/tests/fixtures/plugins/october/tester/components/Post.php +++ b/tests/fixtures/plugins/october/tester/components/Post.php @@ -4,7 +4,6 @@ use Cms\Classes\ComponentBase; class Post extends ComponentBase { - public function componentDetails() { return [ @@ -22,5 +21,4 @@ class Post extends ComponentBase ] ]; } - } diff --git a/tests/fixtures/plugins/testvendor/goto/Plugin.php b/tests/fixtures/plugins/testvendor/goto/Plugin.php index 829c56435..3d1b816a6 100644 --- a/tests/fixtures/plugins/testvendor/goto/Plugin.php +++ b/tests/fixtures/plugins/testvendor/goto/Plugin.php @@ -4,7 +4,6 @@ use System\Classes\PluginBase; class Plugin extends PluginBase { - public function pluginDetails() { return [ diff --git a/tests/fixtures/plugins/testvendor/test/Plugin.php b/tests/fixtures/plugins/testvendor/test/Plugin.php index d2b78734e..cb9921a56 100644 --- a/tests/fixtures/plugins/testvendor/test/Plugin.php +++ b/tests/fixtures/plugins/testvendor/test/Plugin.php @@ -4,7 +4,6 @@ use System\Classes\PluginBase; class Plugin extends PluginBase { - public function pluginDetails() { return [ @@ -23,5 +22,4 @@ class Plugin extends PluginBase ] ]; } - } diff --git a/tests/functional/backend/AuthTest.php b/tests/functional/backend/AuthTest.php index 4332847f1..4c1fe925d 100644 --- a/tests/functional/backend/AuthTest.php +++ b/tests/functional/backend/AuthTest.php @@ -1,8 +1,6 @@ open('backend'); @@ -88,7 +86,5 @@ class AuthTest extends UiTestCase catch (PHPUnit_Framework_AssertionFailedError $e) { array_push($this->verificationErrors, $e->toString()); } - } - } diff --git a/tests/functional/cms/TemplateTest.php b/tests/functional/cms/TemplateTest.php index b59b851a7..5ff2a0a9e 100644 --- a/tests/functional/cms/TemplateTest.php +++ b/tests/functional/cms/TemplateTest.php @@ -1,8 +1,6 @@ signInToBackend(); @@ -141,7 +139,5 @@ class TemplateTest extends UiTestCase $this->click("xpath=(//button[@data-request='onDelete'])[1]"); $this->getSweetConfirmation('Do you really want delete this content file?'); $this->waitForElementNotPresent("name=fileName"); - } - } diff --git a/tests/js/cases/system/framework.test.js b/tests/js/cases/system/framework.test.js new file mode 100644 index 000000000..ec7323d34 --- /dev/null +++ b/tests/js/cases/system/framework.test.js @@ -0,0 +1,673 @@ +import { assert } from 'chai' +import fakeDom from 'helpers/fakeDom' +import sinon from 'sinon' + +describe('modules/system/assets/js/framework.js', function () { + describe('ajaxRequests through JS', function () { + let dom, + window, + xhr, + requests = [] + + this.timeout(1000) + + beforeEach(() => { + // Load framework.js in the fake DOM + dom = fakeDom( + '
Initial content
' + + '' + + '', + { + beforeParse: (window) => { + // Mock XHR for tests below + xhr = sinon.useFakeXMLHttpRequest() + xhr.onCreate = (request) => { + requests.push(request) + } + window.XMLHttpRequest = xhr + + // Allow window.location.assign() to be stubbed + delete window.location + window.location = { + href: 'https://october.example.org/', + assign: sinon.stub() + } + } + } + ) + window = dom.window + + // Enable CORS on jQuery + window.jqueryScript.onload = () => { + window.jQuery.support.cors = true + } + }) + + afterEach(() => { + // Close window and restore XHR functionality to default + window.XMLHttpRequest = sinon.xhr.XMLHttpRequest + window.close() + requests = [] + }) + + it('can make a successful AJAX request', function (done) { + window.frameworkScript.onload = () => { + window.$.request('test::onTest', { + success: function () { + done() + }, + error: function () { + done(new Error('AJAX call failed')) + } + }) + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'successful': true + }) + ) + } + }) + + it('can make an unsuccessful AJAX request', function (done) { + window.frameworkScript.onload = () => { + window.$.request('test::onTest', { + success: function () { + done(new Error('AJAX call succeeded')) + }, + error: function () { + done() + } + }) + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a 404 Not Found response from the server + requests[1].respond( + 404, + { + 'Content-Type': 'text/html' + }, + '' + ) + } + }) + + it('can update a partial via an ID selector', function (done) { + window.frameworkScript.onload = () => { + window.$.request('test::onTest', { + complete: function () { + let partialContent = dom.window.document.getElementById('partialId').textContent + try { + assert( + partialContent === 'Content passed through AJAX', + 'Partial content incorrect - ' + + 'expected "Content passed through AJAX", ' + + 'found "' + partialContent + '"' + ) + done() + } catch (e) { + done(e) + } + } + }) + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a response from the server that includes a partial change via ID + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + '#partialId': 'Content passed through AJAX' + }) + ) + } + }) + + it('can update a partial via a class selector', function (done) { + window.frameworkScript.onload = () => { + window.$.request('test::onTest', { + complete: function () { + let partialContent = dom.window.document.getElementById('partialId').textContent + try { + assert( + partialContent === 'Content passed through AJAX', + 'Partial content incorrect - ' + + 'expected "Content passed through AJAX", ' + + 'found "' + partialContent + '"' + ) + done() + } catch (e) { + done(e) + } + } + }) + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a response from the server that includes a partial change via a class + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + '.partialClass': 'Content passed through AJAX' + }) + ) + } + }) + + it('can redirect after a successful AJAX request', function (done) { + this.timeout(1000) + + // Detect a redirect + window.location.assign.callsFake((url) => { + try { + assert( + url === '/test/success', + 'Non-matching redirect URL' + ) + done() + } catch (e) { + done(e) + } + }) + + window.frameworkScript.onload = () => { + window.$.request('test::onTest', { + redirect: '/test/success', + }) + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'successful': true + }) + ) + } + }) + + it('can send extra data with the AJAX request', function (done) { + this.timeout(1000) + + window.frameworkScript.onload = () => { + window.$.request('test::onTest', { + data: { + test1: 'First', + test2: 'Second' + }, + success: function () { + done() + } + }) + + try { + assert( + requests[1].requestBody === 'test1=First&test2=Second', + 'Data incorrect or not included in request' + ) + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'successful': true + }) + ) + } + }) + + it('can call a beforeUpdate handler', function (done) { + const beforeUpdate = function (data, status, jqXHR) { + } + const beforeUpdateSpy = sinon.spy(beforeUpdate) + + window.frameworkScript.onload = () => { + window.$.request('test::onTest', { + beforeUpdate: beforeUpdateSpy + }) + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'successful': true + }) + ) + + try { + assert( + beforeUpdateSpy.withArgs( + { + 'successful': true + }, + 'success' + ).calledOnce + ) + done() + } catch (e) { + done(e) + } + } + }) + }) + + describe('ajaxRequests through HTML attributes', function () { + let dom, + window, + xhr, + requests = [] + + this.timeout(1000) + + beforeEach(() => { + // Load framework.js in the fake DOM + dom = fakeDom( + '' + + '' + + '' + + '
Initial content
' + + '' + + '', + { + beforeParse: (window) => { + // Mock XHR for tests below + xhr = sinon.useFakeXMLHttpRequest() + xhr.onCreate = (request) => { + requests.push(request) + } + window.XMLHttpRequest = xhr + + // Add a stub for the request handlers + window.test = sinon.stub() + + // Add a spy for the beforeUpdate handler + window.beforeUpdate = function (element, data, status) { + } + window.beforeUpdateSpy = sinon.spy(window.beforeUpdate) + + // Stub out window.alert + window.alert = sinon.stub() + + // Allow window.location.assign() to be stubbed + delete window.location + window.location = { + href: 'https://october.example.org/', + assign: sinon.stub() + } + } + } + ) + window = dom.window + + // Enable CORS on jQuery + window.jqueryScript.onload = () => { + window.jQuery.support.cors = true + } + }) + + afterEach(() => { + // Close window and restore XHR functionality to default + window.XMLHttpRequest = sinon.xhr.XMLHttpRequest + window.close() + requests = [] + }) + + it('can make a successful AJAX request', function (done) { + window.frameworkScript.onload = () => { + window.test.callsFake((response) => { + assert(response === 'success', 'Response handler was not "success"') + done() + }) + + window.$('a#standard').click() + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'successful': true + }) + ) + } + }) + + it('can make an unsuccessful AJAX request', function (done) { + window.frameworkScript.onload = () => { + window.test.callsFake((response) => { + assert(response === 'failure', 'Response handler was not "failure"') + done() + }) + + window.$('a#standard').click() + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a 404 Not Found response from the server + requests[1].respond( + 404, + { + 'Content-Type': 'text/html' + }, + '' + ) + } + }) + + + it('can update a partial via an ID selector', function (done) { + window.frameworkScript.onload = () => { + window.test.callsFake(() => { + let partialContent = dom.window.document.getElementById('partialId').textContent + try { + assert( + partialContent === 'Content passed through AJAX', + 'Partial content incorrect - ' + + 'expected "Content passed through AJAX", ' + + 'found "' + partialContent + '"' + ) + done() + } catch (e) { + done(e) + } + }) + + window.$('a#standard').click() + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a response from the server that includes a partial change via ID + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + '#partialId': 'Content passed through AJAX' + }) + ) + } + }) + + it('can update a partial via a class selector', function (done) { + window.frameworkScript.onload = () => { + window.test.callsFake(() => { + let partialContent = dom.window.document.getElementById('partialId').textContent + try { + assert( + partialContent === 'Content passed through AJAX', + 'Partial content incorrect - ' + + 'expected "Content passed through AJAX", ' + + 'found "' + partialContent + '"' + ) + done() + } catch (e) { + done(e) + } + }) + + window.$('a#standard').click() + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a response from the server that includes a partial change via a class + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + '.partialClass': 'Content passed through AJAX' + }) + ) + } + }) + + it('can redirect after a successful AJAX request', function (done) { + this.timeout(1000) + + // Detect a redirect + window.location.assign.callsFake((url) => { + try { + assert( + url === '/test/success', + 'Non-matching redirect URL' + ) + done() + } catch (e) { + done(e) + } + }) + + window.frameworkScript.onload = () => { + window.$('a#redirect').click() + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'succesful': true + }) + ) + } + }) + + it('can send extra data with the AJAX request', function (done) { + this.timeout(1000) + + window.frameworkScript.onload = () => { + window.test.callsFake((response) => { + assert(response === 'success', 'Response handler was not "success"') + done() + }) + + window.$('a#dataLink').click() + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'succesful': true + }) + ) + } + }) + + it('can call a beforeUpdate handler', function (done) { + this.timeout(1000) + + window.frameworkScript.onload = () => { + window.test.callsFake((response) => { + assert(response === 'success', 'Response handler was not "success"') + }) + + window.$('a#dataLink').click() + + try { + assert( + requests[1].requestHeaders['X-OCTOBER-REQUEST-HANDLER'] === 'test::onTest', + 'Incorrect October request handler' + ) + } catch (e) { + done(e) + } + + // Mock a successful response from the server + requests[1].respond( + 200, + { + 'Content-Type': 'application/json' + }, + JSON.stringify({ + 'successful': true + }) + ) + + try { + assert( + window.beforeUpdateSpy.withArgs( + window.$('a#dataLink').get(), + { + 'successful': true + }, + 'success' + ).calledOnce, + 'beforeUpdate handler never called, or incorrect arguments provided' + ) + done() + } catch (e) { + done(e) + } + } + }) + }) +}) diff --git a/tests/js/cases/system/ui.select.test.js b/tests/js/cases/system/ui.select.test.js new file mode 100644 index 000000000..17f854789 --- /dev/null +++ b/tests/js/cases/system/ui.select.test.js @@ -0,0 +1,111 @@ +import { assert } from 'chai' +import fakeDom from 'helpers/fakeDom' + +describe('modules/system/assets/ui/js/select.js', function () { + describe('AJAX processResults function', function () { + let dom, + window, + processResults, + keyValResultFormat = { + value1: 'text1', + value2: 'text2' + }, + select2ResultFormat = [ + { + id: 'value1', + text: 'text1', + disabled: true + }, + { + id: 'value2', + text: 'text2', + selected: false + } + ] + + this.timeout(1000) + + beforeEach((done) => { + // Load framework.js and select.js in the fake DOM + dom = fakeDom(` + + + + + `) + + window = dom.window + + window.selectScript.onload = () => { + window.jQuery.fn.select2 = function(options) { + processResults = options.ajax.processResults + done() + } + } + }) + + afterEach(() => { + window.close() + }) + + it('supports a key-value mapping on the "result" key', function () { + let result = processResults({ result: keyValResultFormat }) + assert.deepEqual(result, { results: [ + { + id: 'value1', + text: 'text1' + }, + { + id: 'value2', + text: 'text2' + } + ]}) + }) + + it('supports a key-value mapping on the "results" key', function() { + let result = processResults({ results: keyValResultFormat }) + assert.deepEqual(result, { results: [ + { + id: 'value1', + text: 'text1' + }, + { + id: 'value2', + text: 'text2' + } + ]}) + }) + + it('passes through other data provided with key-value mapping', function() { + let result = processResults({ result: keyValResultFormat, other1: 1, other2: '2' }) + assert.include(result, { other1: 1, other2: '2'}) + }) + + it('supports the Select2 result format on the "result" key', function() { + let result = processResults({ result: select2ResultFormat }) + assert.deepEqual(result, { results: select2ResultFormat }) + }) + + it('passes through the Select2 result format on the "results" key', function() { + let result = processResults({ results: select2ResultFormat }) + assert.deepEqual(result, { results: select2ResultFormat }) + }) + + it('passes through other data provided with Select2 results format', function() { + let result = processResults({ results: select2ResultFormat, pagination: { more: true }, other: 'value' }) + assert.deepInclude(result, { pagination: { more: true }, other: 'value' }) + }) + + it('passes through the Select2 format with a group as the first entry', function() { + let data = [ + { + text: 'Label', + children: select2ResultFormat + } + ] + + let result = processResults({ results: data }) + assert.deepEqual(result, { results: data }) + }) + }) +}) diff --git a/tests/js/helpers/fakeDom.js b/tests/js/helpers/fakeDom.js new file mode 100644 index 000000000..1d5c84743 --- /dev/null +++ b/tests/js/helpers/fakeDom.js @@ -0,0 +1,40 @@ +import { JSDOM } from 'jsdom' + +const defaults = { + url: 'https://october.example.org/', + referer: null, + contentType: 'text/html', + head: 'Fake document', + bodyStart: '', + bodyEnd: '', + foot: '', + beforeParse: null +} + +const fakeDom = (content, options) => { + const settings = Object.assign({}, defaults, options) + + const dom = new JSDOM( + settings.head + + settings.bodyStart + + (content + '') + + settings.bodyEnd + + settings.foot, + { + url: settings.url, + referrer: settings.referer || undefined, + contentType: settings.contenType, + includeNodeLocations: true, + runScripts: 'dangerously', + resources: 'usable', + pretendToBeVisual: true, + beforeParse: (typeof settings.beforeParse === 'function') + ? settings.beforeParse + : undefined + } + ) + + return dom +} + +export default fakeDom diff --git a/tests/unit/backend/classes/AuthManagerTest.php b/tests/unit/backend/classes/AuthManagerTest.php new file mode 100644 index 000000000..6bb9f9684 --- /dev/null +++ b/tests/unit/backend/classes/AuthManagerTest.php @@ -0,0 +1,150 @@ +createApplication(); + + $this->instance = AuthManager::instance(); + $this->instance->registerPermissions('October.TestCase', [ + 'test.permission_one' => [ + 'label' => 'Test Permission 1', + 'tab' => 'Test', + 'order' => 200 + ], + 'test.permission_two' => [ + 'label' => 'Test Permission 2', + 'tab' => 'Test', + 'order' => 300 + ] + ]); + } + + public function tearDown() + { + AuthManager::forgetInstance(); + } + + public function testListPermissions() + { + $permissions = $this->instance->listPermissions(); + $this->assertCount(2, $permissions); + $this->assertEquals([ + 'test.permission_one', + 'test.permission_two' + ], collect($permissions)->pluck('code')->toArray()); + } + + public function testRegisterPermissions() + { + $this->instance->registerPermissions('October.TestCase', [ + 'test.permission_three' => [ + 'label' => 'Test Permission 3', + 'tab' => 'Test', + 'order' => 100 + ] + ]); + + $permissions = $this->instance->listPermissions(); + $this->assertCount(3, $permissions); + $this->assertEquals([ + 'test.permission_three', + 'test.permission_one', + 'test.permission_two' + ], collect($permissions)->pluck('code')->toArray()); + } + + public function testRegisterPermissionsThroughCallbacks() + { + // Callback one + $this->instance->registerCallback(function ($manager) { + $manager->registerPermissions('October.TestCase', [ + 'test.permission_three' => [ + 'label' => 'Test Permission 3', + 'tab' => 'Test', + 'order' => 100 + ] + ]); + }); + + // Callback two + $this->instance->registerCallback(function ($manager) { + $manager->registerPermissions('October.TestCase', [ + 'test.permission_four' => [ + 'label' => 'Test Permission 4', + 'tab' => 'Test', + 'order' => 400 + ] + ]); + }); + + $permissions = $this->instance->listPermissions(); + $this->assertCount(4, $permissions); + $this->assertEquals([ + 'test.permission_three', + 'test.permission_one', + 'test.permission_two', + 'test.permission_four' + ], collect($permissions)->pluck('code')->toArray()); + } + + public function testRegisterAdditionalTab() + { + $this->instance->registerPermissions('October.TestCase', [ + 'test.permission_three' => [ + 'label' => 'Test Permission 3', + 'tab' => 'Test 2', + 'order' => 100 + ] + ]); + + $this->instance->registerCallback(function ($manager) { + $manager->registerPermissions('October.TestCase', [ + 'test.permission_four' => [ + 'label' => 'Test Permission 4', + 'tab' => 'Test 2', + 'order' => 400 + ] + ]); + }); + + $tabs = $this->instance->listTabbedPermissions(); + $this->assertCount(2, $tabs); + $this->assertEquals([ + 'Test 2', + 'Test' + ], array_keys($tabs)); + $this->assertEquals([ + 'test.permission_three', + 'test.permission_four' + ], collect($tabs['Test 2'])->pluck('code')->toArray()); + $this->assertEquals([ + 'test.permission_one', + 'test.permission_two', + ], collect($tabs['Test'])->pluck('code')->toArray()); + } + + public function testRemovePermission() + { + $this->instance->removePermission('October.TestCase', 'test.permission_one'); + + $permissions = $this->instance->listPermissions(); + $this->assertCount(1, $permissions); + $this->assertEquals([ + 'test.permission_two' + ], collect($permissions)->pluck('code')->toArray()); + } + + public function testCannotRemovePermissionsBeforeLoaded() + { + $this->expectException(SystemException::class); + $this->expectExceptionMessage('Unable to remove permissions before they are loaded.'); + + AuthManager::forgetInstance(); + $this->instance = AuthManager::instance(); + $this->instance->removePermission('October.TestCase', 'test.permission_one'); + } +} diff --git a/tests/unit/backend/classes/WidgetManagerTest.php b/tests/unit/backend/classes/WidgetManagerTest.php index 5e88096ae..819997385 100644 --- a/tests/unit/backend/classes/WidgetManagerTest.php +++ b/tests/unit/backend/classes/WidgetManagerTest.php @@ -44,5 +44,4 @@ class WidgetManagerTest extends TestCase $this->assertCount(1, $widgets); } - } diff --git a/tests/unit/backend/helpers/BackendHelperTest.php b/tests/unit/backend/helpers/BackendHelperTest.php new file mode 100644 index 000000000..61752a3c0 --- /dev/null +++ b/tests/unit/backend/helpers/BackendHelperTest.php @@ -0,0 +1,33 @@ +decompileAsset('tests/fixtures/backend/assets/compilation.js'); + + $this->assertCount(2, $assets); + $this->assertContains('file1.js', $assets[0]); + $this->assertContains('file2.js', $assets[1]); + } + + public function testDecompileMissingFile() + { + $this->expectException(DecompileException::class); + + $backendHelper = new Backend; + $assets = $backendHelper->decompileAsset('tests/fixtures/backend/assets/missing.js'); + } + + public function testDecompileNonCompilationFile() + { + $this->expectException(DecompileException::class); + + $backendHelper = new Backend; + $assets = $backendHelper->decompileAsset('tests/fixtures/backend/assets/not-compilation.js'); + } +} diff --git a/tests/unit/backend/models/ExportModelTest.php b/tests/unit/backend/models/ExportModelTest.php index 367bc1e9d..4fd55b6f0 100644 --- a/tests/unit/backend/models/ExportModelTest.php +++ b/tests/unit/backend/models/ExportModelTest.php @@ -2,7 +2,9 @@ use Backend\Models\ExportModel; -if (!class_exists('Model')) class_alias('October\Rain\Database\Model', 'Model'); +if (!class_exists('Model')) { + class_alias('October\Rain\Database\Model', 'Model'); +} class ExampleExportModel extends ExportModel { diff --git a/tests/unit/backend/models/ImportModelTest.php b/tests/unit/backend/models/ImportModelTest.php index 0512e82e9..1f803bd06 100644 --- a/tests/unit/backend/models/ImportModelTest.php +++ b/tests/unit/backend/models/ImportModelTest.php @@ -2,7 +2,9 @@ use Backend\Models\ImportModel; -if (!class_exists('Model')) class_alias('October\Rain\Database\Model', 'Model'); +if (!class_exists('Model')) { + class_alias('October\Rain\Database\Model', 'Model'); +} class ExampleImportModel extends ImportModel { @@ -36,5 +38,4 @@ class ImportModelTest extends TestCase $result = self::callProtectedMethod($model, 'decodeArrayValue', [$data, '-']); $this->assertEquals(['art direction', 'roman empire', 'sci-fi'], $result); } - } diff --git a/tests/unit/backend/traits/WidgetMakerTest.php b/tests/unit/backend/traits/WidgetMakerTest.php index cf9453abf..9a290b509 100644 --- a/tests/unit/backend/traits/WidgetMakerTest.php +++ b/tests/unit/backend/traits/WidgetMakerTest.php @@ -58,5 +58,4 @@ class WidgetMakerTest extends TestCase $this->assertInstanceOf('Backend\Widgets\Search', $widget); $this->assertEquals('config', $widget->getConfig('test')); } - } diff --git a/tests/unit/backend/widgets/FilterTest.php b/tests/unit/backend/widgets/FilterTest.php new file mode 100644 index 000000000..05d0f0d78 --- /dev/null +++ b/tests/unit/backend/widgets/FilterTest.php @@ -0,0 +1,139 @@ +actingAs($user); + + $filter = $this->restrictedFilterFixture(); + $filter->render(); + + $this->assertNotNull($filter->getScope('id')); + + // Expect an exception + $this->expectException(ApplicationException::class); + $this->expectExceptionMessage('No definition for scope email'); + $scope = $filter->getScope('email'); + } + + public function testRestrictedScopeWithUserWithWrongPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.wrong_permission', true)); + + $filter = $this->restrictedFilterFixture(); + $filter->render(); + + $this->assertNotNull($filter->getScope('id')); + + // Expect an exception + $this->expectException(ApplicationException::class); + $this->expectExceptionMessage('No definition for scope email'); + $scope = $filter->getScope('email'); + } + + public function testRestrictedScopeWithUserWithRightPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $filter = $this->restrictedFilterFixture(); + $filter->render(); + + $this->assertNotNull($filter->getScope('id')); + $this->assertNotNull($filter->getScope('email')); + } + + public function testRestrictedScopeWithUserWithRightWildcardPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $filter = new Filter(null, [ + 'model' => new User, + 'arrayName' => 'array', + 'scopes' => [ + 'id' => [ + 'type' => 'text', + 'label' => 'ID' + ], + 'email' => [ + 'type' => 'text', + 'label' => 'Email', + 'permission' => 'test.*' + ] + ] + ]); + $filter->render(); + + $this->assertNotNull($filter->getScope('id')); + $this->assertNotNull($filter->getScope('email')); + } + + public function testRestrictedScopeWithSuperuser() + { + $user = new UserFixture; + $this->actingAs($user->asSuperUser()); + + $filter = $this->restrictedFilterFixture(); + $filter->render(); + + $this->assertNotNull($filter->getScope('id')); + $this->assertNotNull($filter->getScope('email')); + } + + public function testRestrictedScopeSinglePermissionWithUserWithWrongPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.wrong_permission', true)); + + $filter = $this->restrictedFilterFixture(true); + $filter->render(); + + $this->assertNotNull($filter->getScope('id')); + + // Expect an exception + $this->expectException(ApplicationException::class); + $this->expectExceptionMessage('No definition for scope email'); + $scope = $filter->getScope('email'); + } + + public function testRestrictedScopeSinglePermissionWithUserWithRightPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $filter = $this->restrictedFilterFixture(true); + $filter->render(); + + $this->assertNotNull($filter->getScope('id')); + $this->assertNotNull($filter->getScope('email')); + } + + protected function restrictedFilterFixture(bool $singlePermission = false) + { + return new Filter(null, [ + 'model' => new User, + 'arrayName' => 'array', + 'scopes' => [ + 'id' => [ + 'type' => 'text', + 'label' => 'ID' + ], + 'email' => [ + 'type' => 'text', + 'label' => 'Email', + 'permissions' => ($singlePermission) ? 'test.access_field' : [ + 'test.access_field' + ] + ] + ] + ]); + } +} diff --git a/tests/unit/backend/widgets/FormTest.php b/tests/unit/backend/widgets/FormTest.php index 8fdaa7e4c..d373a1d86 100644 --- a/tests/unit/backend/widgets/FormTest.php +++ b/tests/unit/backend/widgets/FormTest.php @@ -2,14 +2,106 @@ use Backend\Widgets\Form; use Illuminate\Database\Eloquent\Model; +use October\Tests\Fixtures\Backend\Models\UserFixture; class FormTestModel extends Model { } -class FormTest extends TestCase +class FormTest extends PluginTestCase { + public function testRestrictedFieldWithUserWithNoPermissions() + { + $user = new UserFixture; + $this->actingAs($user); + + $form = $this->restrictedFormFixture(); + + $form->render(); + $this->assertNull($form->getField('testRestricted')); + } + + public function testRestrictedFieldWithUserWithWrongPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.wrong_permission', true)); + + $form = $this->restrictedFormFixture(); + + $form->render(); + $this->assertNull($form->getField('testRestricted')); + } + + public function testRestrictedFieldWithUserWithRightPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $form = $this->restrictedFormFixture(); + + $form->render(); + $this->assertNotNull($form->getField('testRestricted')); + } + + public function testRestrictedFieldWithUserWithRightWildcardPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $form = new Form(null, [ + 'model' => new FormTestModel, + 'arrayName' => 'array', + 'fields' => [ + 'testField' => [ + 'type' => 'text', + 'label' => 'Test 1' + ], + 'testRestricted' => [ + 'type' => 'text', + 'label' => 'Test 2', + 'permission' => 'test.*' + ] + ] + ]); + + $form->render(); + $this->assertNotNull($form->getField('testRestricted')); + } + + public function testRestrictedFieldWithSuperuser() + { + $user = new UserFixture; + $this->actingAs($user->asSuperUser()); + + $form = $this->restrictedFormFixture(); + + $form->render(); + $this->assertNotNull($form->getField('testRestricted')); + } + + public function testRestrictedFieldSinglePermissionWithUserWithWrongPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.wrong_permission', true)); + + $form = $this->restrictedFormFixture(true); + + $form->render(); + $this->assertNull($form->getField('testRestricted')); + } + + public function testRestrictedFieldSinglePermissionWithUserWithRightPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $form = $this->restrictedFormFixture(true); + + $form->render(); + $this->assertNotNull($form->getField('testRestricted')); + } + public function testCheckboxlistTrigger() { $form = new Form(null, [ @@ -38,4 +130,25 @@ class FormTest extends TestCase $attributes = $form->getField('triggered')->getAttributes('container', false); $this->assertEquals('[name="array[trigger][]"]', array_get($attributes, 'data-trigger')); } + + protected function restrictedFormFixture(bool $singlePermission = false) + { + return new Form(null, [ + 'model' => new FormTestModel, + 'arrayName' => 'array', + 'fields' => [ + 'testField' => [ + 'type' => 'text', + 'label' => 'Test 1' + ], + 'testRestricted' => [ + 'type' => 'text', + 'label' => 'Test 2', + 'permissions' => ($singlePermission) ? 'test.access_field' : [ + 'test.access_field' + ] + ] + ] + ]); + } } diff --git a/tests/unit/backend/widgets/ListsTest.php b/tests/unit/backend/widgets/ListsTest.php new file mode 100644 index 000000000..e74aa877d --- /dev/null +++ b/tests/unit/backend/widgets/ListsTest.php @@ -0,0 +1,140 @@ +actingAs($user); + + $list = $this->restrictedListsFixture(); + $list->render(); + + $this->assertNotNull($list->getColumn('id')); + + // Expect an exception + $this->expectException(ApplicationException::class); + $this->expectExceptionMessage('No definition for column email'); + $column = $list->getColumn('email'); + } + + public function testRestrictedColumnWithUserWithWrongPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.wrong_permission', true)); + + $list = $this->restrictedListsFixture(); + $list->render(); + + $this->assertNotNull($list->getColumn('id')); + + // Expect an exception + $this->expectException(ApplicationException::class); + $this->expectExceptionMessage('No definition for column email'); + $column = $list->getColumn('email'); + } + + public function testRestrictedColumnWithUserWithRightPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $list = $this->restrictedListsFixture(); + $list->render(); + + $this->assertNotNull($list->getColumn('id')); + $this->assertNotNull($list->getColumn('email')); + } + + public function testRestrictedColumnWithUserWithRightWildcardPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $list = new Lists(null, [ + 'model' => new User, + 'arrayName' => 'array', + 'columns' => [ + 'id' => [ + 'type' => 'text', + 'label' => 'ID' + ], + 'email' => [ + 'type' => 'text', + 'label' => 'Email', + 'permission' => 'test.*' + ] + ] + ]); + $list->render(); + + $this->assertNotNull($list->getColumn('id')); + $this->assertNotNull($list->getColumn('email')); + } + + public function testRestrictedColumnWithSuperuser() + { + $user = new UserFixture; + $this->actingAs($user->asSuperUser()); + + $list = $this->restrictedListsFixture(); + $list->render(); + + $this->assertNotNull($list->getColumn('id')); + $this->assertNotNull($list->getColumn('email')); + } + + public function testRestrictedColumnSinglePermissionWithUserWithWrongPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.wrong_permission', true)); + + $list = $this->restrictedListsFixture(true); + $list->render(); + + $this->assertNotNull($list->getColumn('id')); + + // Expect an exception + $this->expectException(ApplicationException::class); + $this->expectExceptionMessage('No definition for column email'); + $column = $list->getColumn('email'); + } + + public function testRestrictedColumnSinglePermissionWithUserWithRightPermissions() + { + $user = new UserFixture; + $this->actingAs($user->withPermission('test.access_field', true)); + + $list = $this->restrictedListsFixture(true); + $list->render(); + + $this->assertNotNull($list->getColumn('id')); + $this->assertNotNull($list->getColumn('email')); + } + + protected function restrictedListsFixture(bool $singlePermission = false) + { + return new Lists(null, [ + 'model' => new User, + 'arrayName' => 'array', + 'columns' => [ + 'id' => [ + 'type' => 'text', + 'label' => 'ID' + ], + 'email' => [ + 'type' => 'text', + 'label' => 'Email', + 'permissions' => ($singlePermission) ? 'test.access_field' : [ + 'test.access_field' + ] + ] + ] + ]); + } +} diff --git a/tests/unit/cms/classes/CmsCompoundObjectTest.php b/tests/unit/cms/classes/CmsCompoundObjectTest.php index 58e9a5ef0..c370dd290 100644 --- a/tests/unit/cms/classes/CmsCompoundObjectTest.php +++ b/tests/unit/cms/classes/CmsCompoundObjectTest.php @@ -9,7 +9,9 @@ class TestCmsCompoundObject extends CmsCompoundObject { protected $dirName = 'testobjects'; - protected function parseSettings() {} + protected function parseSettings() + { + } } class TestParsedCmsCompoundObject extends CmsCompoundObject @@ -21,7 +23,9 @@ class TestTemporaryCmsCompoundObject extends CmsCompoundObject { protected $dirName = 'temporary'; - protected function parseSettings() {} + protected function parseSettings() + { + } } class CmsCompoundObjectTest extends TestCase @@ -128,8 +132,9 @@ class CmsCompoundObjectTest extends TestCase $this->assertNotEmpty($testContent); $filePath = $themePath .= '/temporary/testcompound.htm'; - if (file_exists($filePath)) + if (file_exists($filePath)) { @unlink($filePath); + } $this->assertFileNotExists($filePath); file_put_contents($filePath, $testContent); @@ -195,8 +200,9 @@ class CmsCompoundObjectTest extends TestCase $theme = Theme::load('apitest'); $destFilePath = $theme->getPath().'/testobjects/compound-markup.htm'; - if (file_exists($destFilePath)) + if (file_exists($destFilePath)) { unlink($destFilePath); + } $this->assertFileNotExists($destFilePath); @@ -219,8 +225,9 @@ class CmsCompoundObjectTest extends TestCase $theme = Theme::load('apitest'); $destFilePath = $theme->getPath().'/testobjects/compound-markup-settings.htm'; - if (file_exists($destFilePath)) + if (file_exists($destFilePath)) { unlink($destFilePath); + } $this->assertFileNotExists($destFilePath); @@ -320,5 +327,4 @@ class CmsCompoundObjectTest extends TestCase $this->assertEquals($expected, $actual); } - } diff --git a/tests/unit/cms/classes/CmsExceptionTest.php b/tests/unit/cms/classes/CmsExceptionTest.php index fbe493b98..6e270fb7a 100644 --- a/tests/unit/cms/classes/CmsExceptionTest.php +++ b/tests/unit/cms/classes/CmsExceptionTest.php @@ -40,5 +40,4 @@ class CmsExceptionTest extends TestCase $this->assertEquals('PHP Content', $exception->getErrorType()); $this->assertEquals('This is a general error', $exception->getMessage()); } - } diff --git a/tests/unit/cms/classes/CmsObjectTest.php b/tests/unit/cms/classes/CmsObjectTest.php index 81ca29201..95d4f50d9 100644 --- a/tests/unit/cms/classes/CmsObjectTest.php +++ b/tests/unit/cms/classes/CmsObjectTest.php @@ -61,8 +61,9 @@ class CmsObjectTest extends TestCase $themePath = $theme->getPath(); $filePath = $themePath .= '/temporary/test.htm'; - if (file_exists($filePath)) + if (file_exists($filePath)) { @unlink($filePath); + } $this->assertFileNotExists($filePath); @@ -216,8 +217,9 @@ class CmsObjectTest extends TestCase $theme = Theme::load('apitest'); $destFilePath = $theme->getPath().'/testobjects/mytestobj.htm'; - if (file_exists($destFilePath)) + if (file_exists($destFilePath)) { unlink($destFilePath); + } $this->assertFileNotExists($destFilePath); @@ -244,8 +246,9 @@ class CmsObjectTest extends TestCase $this->assertFileExists($srcFilePath); $destFilePath = $theme->getPath().'/testobjects/anotherobj.htm'; - if (file_exists($destFilePath)) + if (file_exists($destFilePath)) { unlink($destFilePath); + } $testContents = 'mytestcontent'; $obj = TestCmsObject::load($theme, 'mytestobj.htm'); @@ -274,8 +277,9 @@ class CmsObjectTest extends TestCase $this->assertFileExists($srcFilePath); $destFilePath = $theme->getPath().'/testobjects/existingobj.htm'; - if (!file_exists($destFilePath)) + if (!file_exists($destFilePath)) { file_put_contents($destFilePath, 'str'); + } $this->assertFileExists($destFilePath); $obj = TestCmsObject::load($theme, 'anotherobj.htm'); @@ -311,12 +315,14 @@ class CmsObjectTest extends TestCase $theme = Theme::load('apitest'); $destFilePath = $theme->getPath().'/testobjects/testsubdir/mytestobj.htm'; - if (file_exists($destFilePath)) + if (file_exists($destFilePath)) { unlink($destFilePath); + } $destDirPath = dirname($destFilePath); - if (file_exists($destDirPath) && is_dir($destDirPath)) + if (file_exists($destDirPath) && is_dir($destDirPath)) { rmdir($destDirPath); + } $this->assertFileNotExists($destFilePath); $this->assertFileNotExists($destDirPath); diff --git a/tests/unit/cms/classes/CodeParserTest.php b/tests/unit/cms/classes/CodeParserTest.php index 8eeacd3ca..ac3af4ca1 100644 --- a/tests/unit/cms/classes/CodeParserTest.php +++ b/tests/unit/cms/classes/CodeParserTest.php @@ -313,5 +313,4 @@ class CodeParserTest extends TestCase $content = preg_replace('~\R~u', PHP_EOL, $content); // Normalize EOL return $content; } - } diff --git a/tests/unit/cms/classes/ContentTest.php b/tests/unit/cms/classes/ContentTest.php index 2c0de6888..790501c6f 100644 --- a/tests/unit/cms/classes/ContentTest.php +++ b/tests/unit/cms/classes/ContentTest.php @@ -32,5 +32,4 @@ class ContentTest extends TestCase $this->assertEquals('Stephen Saucier changed his profile picture — 7 hrs ago
', $content->markup); $this->assertEquals('Stephen Saucier changed his profile picture — 7 hrs ago
', $content->parsedMarkup); } - } diff --git a/tests/unit/cms/classes/ControllerTest.php b/tests/unit/cms/classes/ControllerTest.php index a0dbc5061..53b3c911e 100644 --- a/tests/unit/cms/classes/ControllerTest.php +++ b/tests/unit/cms/classes/ControllerTest.php @@ -170,13 +170,13 @@ class ControllerTest extends TestCase $requestMock = $this ->getMockBuilder('Illuminate\Http\Request') ->disableOriginalConstructor() - ->setMethods(array('ajax', 'method', 'header')) + ->setMethods(['ajax', 'method', 'header']) ->getMock(); - $map = array( - array('X_OCTOBER_REQUEST_HANDLER', null, $handler), - array('X_OCTOBER_REQUEST_PARTIALS', null, $partials), - ); + $map = [ + ['X_OCTOBER_REQUEST_HANDLER', null, $handler], + ['X_OCTOBER_REQUEST_PARTIALS', null, $partials], + ]; $requestMock->expects($this->any()) ->method('ajax') @@ -485,5 +485,4 @@ Custom output: And tell him about his brush strokes? ESC; $this->assertEquals($content, $response); } - } diff --git a/tests/unit/cms/classes/PartialStackTest.php b/tests/unit/cms/classes/PartialStackTest.php index 58bd05d66..f9371a3b6 100644 --- a/tests/unit/cms/classes/PartialStackTest.php +++ b/tests/unit/cms/classes/PartialStackTest.php @@ -55,5 +55,4 @@ class PartialStackTest extends TestCase $stack = new PartialStack; $this->assertNull($stack->getComponent('xxx')); } - } diff --git a/tests/unit/cms/classes/RouterTest.php b/tests/unit/cms/classes/RouterTest.php index 0a32a9acf..5dd9e57db 100644 --- a/tests/unit/cms/classes/RouterTest.php +++ b/tests/unit/cms/classes/RouterTest.php @@ -60,12 +60,12 @@ class RouterTest extends TestCase { $router = new Router(self::$theme); $method = self::getMethod('getCachedUrlFileName'); - $urlList = array(); + $urlList = []; /* * The first time the page should be loaded from the disk. */ - $result = $method->invokeArgs($router, array('/', &$urlList)); + $result = $method->invokeArgs($router, ['/', &$urlList]); $this->assertNull($result); /* @@ -78,14 +78,14 @@ class RouterTest extends TestCase /* * The second time the page should be loaded from the cache. */ - $result = $method->invokeArgs($router, array('/', &$urlList)); + $result = $method->invokeArgs($router, ['/', &$urlList]); $this->assertEquals('index.htm', $result); /* * Clear the cache */ $router->clearCache(); - $result = $method->invokeArgs($router, array('/', &$urlList)); + $result = $method->invokeArgs($router, ['/', &$urlList]); $this->assertNull($result); } diff --git a/tests/unit/cms/classes/ThemeTest.php b/tests/unit/cms/classes/ThemeTest.php index b8594c496..85802af7b 100644 --- a/tests/unit/cms/classes/ThemeTest.php +++ b/tests/unit/cms/classes/ThemeTest.php @@ -21,8 +21,9 @@ class ThemeTest extends TestCase $it->rewind(); while ($it->valid()) { - if (!$it->isDot() && !$it->isDir() && $it->getExtension() == 'htm') + if (!$it->isDot() && !$it->isDir() && $it->getExtension() == 'htm') { $result++; + } $it->next(); } @@ -75,7 +76,9 @@ class ThemeTest extends TestCase public function testApiTheme() { Event::flush('cms.theme.getActiveTheme'); - Event::listen('cms.theme.getActiveTheme', function () { return 'apitest'; }); + Event::listen('cms.theme.getActiveTheme', function () { + return 'apitest'; + }); $activeTheme = Theme::getActiveTheme(); $this->assertNotNull($activeTheme); diff --git a/tests/unit/plugins/database/DeferredBindingTest.php b/tests/unit/plugins/database/DeferredBindingTest.php index a0e897aa8..e36460255 100644 --- a/tests/unit/plugins/database/DeferredBindingTest.php +++ b/tests/unit/plugins/database/DeferredBindingTest.php @@ -105,5 +105,4 @@ class DeferredBindingTest extends PluginTestCase $author->commitDeferred($sessionKey); $this->assertEquals(0, DeferredBinding::count()); } - } diff --git a/tests/unit/plugins/database/SoftDeleteModelTest.php b/tests/unit/plugins/database/SoftDeleteModelTest.php index 686ce4306..fc5212433 100644 --- a/tests/unit/plugins/database/SoftDeleteModelTest.php +++ b/tests/unit/plugins/database/SoftDeleteModelTest.php @@ -94,5 +94,4 @@ class SoftDeleteModelTest extends PluginTestCase $this->assertNotNull(SoftDeleteAuthor::find($authorId)); } - } diff --git a/tests/unit/system/classes/AutoDatasourceTest.php b/tests/unit/system/classes/AutoDatasourceTest.php new file mode 100644 index 000000000..43410cc77 --- /dev/null +++ b/tests/unit/system/classes/AutoDatasourceTest.php @@ -0,0 +1,115 @@ +fixtures = []; + + // Create fixtures of template data + $this->fixtures[] = CmsThemeTemplateFixture::create([ + 'source' => 'test', + 'path' => 'partials/page-partial.htm', + 'content' => 'AutoDatasource partials/page-partial.htm', + 'file_size' => 40 + ]); + + $this->fixtures[] = CmsThemeTemplateFixture::create([ + 'source' => 'test', + 'path' => 'partials/testpost/default.htm', + 'content' => 'AutoDatasource partials/testpost/default.htm', + 'file_size' => 44 + ]); + + $this->fixtures[] = CmsThemeTemplateFixture::create([ + 'source' => 'test', + 'path' => 'partials/subdir/test.htm', + 'content' => 'AutoDatasource partials/subdir/test.htm', + 'file_size' => 39 + ]); + + $this->fixtures[] = CmsThemeTemplateFixture::create([ + 'source' => 'test', + 'path' => 'partials/nesting/level2.htm', + 'content' => 'AutoDatasource partials/nesting/level2.htm', + 'file_size' => 42, + 'deleted_at' => '2019-01-01 00:00:00' + ]); + + // Create AutoDatasource + $this->datasource = new AutoDatasource([ + 'database' => new DbDatasource('test', 'cms_theme_templates'), + 'filesystem' => new FileDatasource(base_path('tests/fixtures/themes/test'), App::make('files')), + ]); + } + + public function tearDown() + { + foreach ($this->fixtures as $fixture) { + $fixture->delete(); + } + + parent::tearDown(); + } + + public function testSelect() + { + $results = collect($this->datasource->select('partials')) + ->keyBy('fileName') + ->toArray(); + + // Should be 14 partials in filesystem (tests/fixtures/themes/test), and 1 created directly in database. + // 1 of the filesystem partials should be marked deleted in database. + $this->assertCount(14, $results); + + // Database-only partial should be available + $this->assertArrayHasKey('subdir/test.htm', $results); + $this->assertEquals( + 'AutoDatasource partials/subdir/test.htm', + $results['subdir/test.htm']['content'] + ); + + // Two filesystem partials should be overriden by database + $this->assertEquals( + 'AutoDatasource partials/page-partial.htm', + $results['page-partial.htm']['content'] + ); + $this->assertEquals( + 'AutoDatasource partials/testpost/default.htm', + $results['testpost/default.htm']['content'] + ); + + // One filesystem partial should be marked deleted in database + $this->assertArrayNotHasKey('nesting/level2.htm', $results); + } +} diff --git a/tests/unit/system/classes/CombineAssetsTest.php b/tests/unit/system/classes/CombineAssetsTest.php index 66fbd56a9..0e1860d89 100644 --- a/tests/unit/system/classes/CombineAssetsTest.php +++ b/tests/unit/system/classes/CombineAssetsTest.php @@ -40,7 +40,8 @@ class CombineAssetsTest extends TestCase { $combiner = CombineAssets::instance(); - $url = $combiner->combine([ + $url = $combiner->combine( + [ 'assets/css/style1.css', 'assets/css/style2.css' ], @@ -49,9 +50,10 @@ class CombineAssetsTest extends TestCase $this->assertNotNull($url); $this->assertRegExp('/\w+[-]\d+/i', $url); // Must contain hash-number - $url = $combiner->combine([ - 'assets/js/script1.js', - 'assets/js/script2.js' + $url = $combiner->combine( + [ + 'assets/js/script1.js', + 'assets/js/script2.js' ], base_path().'/tests/fixtures/themes/test' ); @@ -99,5 +101,4 @@ class CombineAssetsTest extends TestCase $combiner = CombineAssets::instance(); $this->assertNull($combiner->resetCache()); } - } diff --git a/tests/unit/system/classes/CoreLangTest.php b/tests/unit/system/classes/CoreLangTest.php index f3600edc1..54899a812 100644 --- a/tests/unit/system/classes/CoreLangTest.php +++ b/tests/unit/system/classes/CoreLangTest.php @@ -10,8 +10,8 @@ class CoreLangTest extends TestCase $translator->setLocale('en'); $validator = Validator::make( - array('name' => 'me'), - array('name' => 'required|min:5') + ['name' => 'me'], + ['name' => 'required|min:5'] ); $this->assertTrue($validator->fails()); @@ -35,7 +35,9 @@ class CoreLangTest extends TestCase foreach ($locales as $locale) { foreach ($files as $file) { $srcPath = base_path() . '/modules/'.$module.'/lang/'.$locale.'/'.$file; - if (!file_exists($srcPath)) continue; + if (!file_exists($srcPath)) { + continue; + } $messages = require $srcPath; $this->assertNotEmpty($messages); $this->assertNotCount(0, $messages); diff --git a/tests/unit/system/classes/MarkupManagerTest.php b/tests/unit/system/classes/MarkupManagerTest.php index 057d9501f..f4ee00a32 100644 --- a/tests/unit/system/classes/MarkupManagerTest.php +++ b/tests/unit/system/classes/MarkupManagerTest.php @@ -31,7 +31,9 @@ class MarkupManagerTest extends TestCase $result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]); $this->assertFalse($result); - $callable = function () { return 'O, Hai!'; }; + $callable = function () { + return 'O, Hai!'; + }; $result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]); $this->assertFalse($result); @@ -78,5 +80,4 @@ class MarkupManagerTest extends TestCase $this->assertEquals('MyFood', $result[0]); $this->assertEquals('myFood', $result[1]); } - } diff --git a/tests/unit/system/classes/PluginManagerTest.php b/tests/unit/system/classes/PluginManagerTest.php index de82f3c7f..e41d860f5 100644 --- a/tests/unit/system/classes/PluginManagerTest.php +++ b/tests/unit/system/classes/PluginManagerTest.php @@ -1,15 +1,20 @@ loadPlugins(); + self::callProtectedMethod($manager, 'loadDependencies'); + + $this->manager = $manager; } // @@ -18,15 +23,19 @@ class PluginManagerTest extends TestCase public function testLoadPlugins() { - $manager = PluginManager::instance(); - $result = self::callProtectedMethod($manager, 'loadPlugins'); + $result = $this->manager->loadPlugins(); - $this->assertCount(5, $result); + $this->assertCount(9, $result); $this->assertArrayHasKey('October.NoUpdates', $result); $this->assertArrayHasKey('October.Sample', $result); $this->assertArrayHasKey('October.Tester', $result); $this->assertArrayHasKey('Database.Tester', $result); $this->assertArrayHasKey('TestVendor.Test', $result); + $this->assertArrayHasKey('DependencyTest.Found', $result); + $this->assertArrayHasKey('DependencyTest.NotFound', $result); + $this->assertArrayHasKey('DependencyTest.WrongCase', $result); + $this->assertArrayHasKey('DependencyTest.Dependency', $result); + $this->assertArrayNotHasKey('TestVendor.Goto', $result); $this->assertInstanceOf('October\NoUpdates\Plugin', $result['October.NoUpdates']); @@ -34,35 +43,41 @@ class PluginManagerTest extends TestCase $this->assertInstanceOf('October\Tester\Plugin', $result['October.Tester']); $this->assertInstanceOf('Database\Tester\Plugin', $result['Database.Tester']); $this->assertInstanceOf('TestVendor\Test\Plugin', $result['TestVendor.Test']); + $this->assertInstanceOf('DependencyTest\Found\Plugin', $result['DependencyTest.Found']); + $this->assertInstanceOf('DependencyTest\NotFound\Plugin', $result['DependencyTest.NotFound']); + $this->assertInstanceOf('DependencyTest\WrongCase\Plugin', $result['DependencyTest.WrongCase']); + $this->assertInstanceOf('DependencyTest\Dependency\Plugin', $result['DependencyTest.Dependency']); } public function testUnloadablePlugin() { - $manager = PluginManager::instance(); - $pluginNamespaces = $manager->getPluginNamespaces(); - $result = $manager->loadPlugin('\\testvendor\\goto', $pluginNamespaces['\\testvendor\\goto']); + $pluginNamespaces = $this->manager->getPluginNamespaces(); + $result = $this->manager->loadPlugin('\\testvendor\\goto', $pluginNamespaces['\\testvendor\\goto']); $this->assertNull($result); } public function testGetPluginPath() { - $manager = PluginManager::instance(); - $result = $manager->getPluginPath('October\Tester'); + $result = $this->manager->getPluginPath('October\Tester'); $basePath = str_replace('\\', '/', base_path()); $this->assertEquals($basePath . '/tests/fixtures/plugins/october/tester', $result); } public function testGetPlugins() { - $manager = PluginManager::instance(); - $result = $manager->getPlugins(); + $result = $this->manager->getPlugins(); - $this->assertCount(5, $result); + $this->assertCount(8, $result); $this->assertArrayHasKey('October.NoUpdates', $result); $this->assertArrayHasKey('October.Sample', $result); $this->assertArrayHasKey('October.Tester', $result); $this->assertArrayHasKey('Database.Tester', $result); $this->assertArrayHasKey('TestVendor.Test', $result); + $this->assertArrayHasKey('DependencyTest.Found', $result); + $this->assertArrayHasKey('DependencyTest.WrongCase', $result); + $this->assertArrayHasKey('DependencyTest.Dependency', $result); + + $this->assertArrayNotHasKey('DependencyTest.NotFound', $result); $this->assertArrayNotHasKey('TestVendor.Goto', $result); $this->assertInstanceOf('October\NoUpdates\Plugin', $result['October.NoUpdates']); @@ -70,57 +85,91 @@ class PluginManagerTest extends TestCase $this->assertInstanceOf('October\Tester\Plugin', $result['October.Tester']); $this->assertInstanceOf('Database\Tester\Plugin', $result['Database.Tester']); $this->assertInstanceOf('TestVendor\Test\Plugin', $result['TestVendor.Test']); + $this->assertInstanceOf('DependencyTest\Found\Plugin', $result['DependencyTest.Found']); + $this->assertInstanceOf('DependencyTest\WrongCase\Plugin', $result['DependencyTest.WrongCase']); + $this->assertInstanceOf('DependencyTest\Dependency\Plugin', $result['DependencyTest.Dependency']); } public function testFindByNamespace() { - $manager = PluginManager::instance(); - $result = $manager->findByNamespace('October\Tester'); + $result = $this->manager->findByNamespace('October\Tester'); $this->assertInstanceOf('October\Tester\Plugin', $result); } public function testHasPlugin() { - $manager = PluginManager::instance(); - $result = $manager->hasPlugin('October\Tester'); + $result = $this->manager->hasPlugin('October\Tester'); $this->assertTrue($result); - $result = $manager->hasPlugin('October\XXXXX'); + $result = $this->manager->hasPlugin('DependencyTest.Found'); + $this->assertTrue($result); + + $result = $this->manager->hasPlugin('DependencyTest\WrongCase'); + $this->assertTrue($result); + + $result = $this->manager->hasPlugin('DependencyTest\NotFound'); + $this->assertTrue($result); + + $result = $this->manager->hasPlugin('October\XXXXX'); $this->assertFalse($result); + + /** + * Test case for https://github.com/octobercms/october/pull/4337 + */ + $result = $this->manager->hasPlugin('dependencyTest\Wrongcase'); + $this->assertTrue($result); + + $result = $this->manager->hasPlugin('dependencyTest.Wrongcase'); + $this->assertTrue($result); } public function testGetPluginNamespaces() { - $manager = PluginManager::instance(); - $result = $manager->getPluginNamespaces(); + $result = $this->manager->getPluginNamespaces(); - $this->assertCount(6, $result); + $this->assertCount(10, $result); $this->assertArrayHasKey('\october\noupdates', $result); $this->assertArrayHasKey('\october\sample', $result); $this->assertArrayHasKey('\october\tester', $result); $this->assertArrayHasKey('\database\tester', $result); $this->assertArrayHasKey('\testvendor\test', $result); $this->assertArrayHasKey('\testvendor\goto', $result); + $this->assertArrayHasKey('\dependencytest\found', $result); + $this->assertArrayHasKey('\dependencytest\notfound', $result); + $this->assertArrayHasKey('\dependencytest\wrongcase', $result); + $this->assertArrayHasKey('\dependencytest\dependency', $result); } public function testGetVendorAndPluginNames() { - $manager = PluginManager::instance(); - $vendors = $manager->getVendorAndPluginNames(); + $vendors = $this->manager->getVendorAndPluginNames(); + $this->assertCount(4, $vendors); $this->assertArrayHasKey('october', $vendors); + $this->assertArrayHasKey('noupdates', $vendors['october']); + $this->assertArrayHasKey('sample', $vendors['october']); + $this->assertArrayHasKey('tester', $vendors['october']); + $this->assertArrayHasKey('database', $vendors); + $this->assertArrayHasKey('tester', $vendors['database']); + $this->assertArrayHasKey('testvendor', $vendors); - $this->assertCount(3, $vendors); + $this->assertArrayHasKey('test', $vendors['testvendor']); + $this->assertArrayHasKey('goto', $vendors['testvendor']); + + $this->assertArrayHasKey('dependencytest', $vendors); + $this->assertArrayHasKey('found', $vendors['dependencytest']); + $this->assertArrayHasKey('notfound', $vendors['dependencytest']); + $this->assertArrayHasKey('wrongcase', $vendors['dependencytest']); + $this->assertArrayHasKey('dependency', $vendors['dependencytest']); } public function testPluginDetails() { - $manager = PluginManager::instance(); - $testPlugin = $manager->findByNamespace('October\XXXXX'); + $testPlugin = $this->manager->findByNamespace('October\XXXXX'); $this->assertNull($testPlugin); - $testPlugin = $manager->findByNamespace('October\Tester'); + $testPlugin = $this->manager->findByNamespace('October\Tester'); $this->assertNotNull($testPlugin); $pluginDetails = $testPlugin->pluginDetails(); @@ -131,12 +180,37 @@ class PluginManagerTest extends TestCase public function testUnregisterall() { - $manager = PluginManager::instance(); - $result = $manager->getPlugins(); - $this->assertCount(5, $result); + $result = $this->manager->getPlugins(); + $this->assertCount(8, $result); - $manager->unregisterAll(); - $this->assertEmpty($manager->getPlugins()); + $this->manager->unregisterAll(); + $this->assertEmpty($this->manager->getPlugins()); } + public function testGetDependencies() + { + $result = $this->manager->getDependencies('DependencyTest.Found'); + $this->assertCount(1, $result); + $this->assertContains('DependencyTest.Dependency', $result); + + $result = $this->manager->getDependencies('DependencyTest.WrongCase'); + $this->assertCount(1, $result); + $this->assertContains('Dependencytest.dependency', $result); + + $result = $this->manager->getDependencies('DependencyTest.NotFound'); + $this->assertCount(1, $result); + $this->assertContains('DependencyTest.Missing', $result); + } + + public function testIsDisabled() + { + $result = $this->manager->isDisabled('DependencyTest.Found'); + $this->assertFalse($result); + + $result = $this->manager->isDisabled('DependencyTest.WrongCase'); + $this->assertFalse($result); + + $result = $this->manager->isDisabled('DependencyTest.NotFound'); + $this->assertTrue($result); + } } diff --git a/themes/demo/assets/vendor/font-awesome/css/font-awesome.css b/themes/demo/assets/vendor/font-awesome/css/font-awesome.css index 7ede1828a..766707987 100644 --- a/themes/demo/assets/vendor/font-awesome/css/font-awesome.css +++ b/themes/demo/assets/vendor/font-awesome/css/font-awesome.css @@ -41,7 +41,6 @@ font-style: normal; text-decoration: inherit; -webkit-font-smoothing: antialiased; - *margin-right: .3em; } [class^="icon-"]:before, [class*=" icon-"]:before { diff --git a/themes/demo/assets/vendor/font-awesome/less/mixins.less b/themes/demo/assets/vendor/font-awesome/less/mixins.less index f7fdda590..9478aae4a 100644 --- a/themes/demo/assets/vendor/font-awesome/less/mixins.less +++ b/themes/demo/assets/vendor/font-awesome/less/mixins.less @@ -12,7 +12,6 @@ font-style: normal; text-decoration: inherit; -webkit-font-smoothing: antialiased; - *margin-right: .3em; // fixes ie7 issues } .border-radius(@radius) { diff --git a/themes/demo/pages/ajax.htm b/themes/demo/pages/ajax.htm index 181fe5273..9d82ab843 100644 --- a/themes/demo/pages/ajax.htm +++ b/themes/demo/pages/ajax.htm @@ -44,7 +44,7 @@ function onTest()

Calculator

-
+