diff --git a/.github/assets/Github Banner.png b/.github/assets/Github Banner.png
new file mode 100644
index 0000000000..aedade011a
Binary files /dev/null and b/.github/assets/Github Banner.png differ
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 0000000000..d9d1ca589b
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,36 @@
+image:
+ file: ./.gitpod/Dockerfile
+
+tasks:
+ - init: ./.gitpod/gitpod-init
+ command: ./.gitpod/gitpod-cmd
+
+ports:
+ - port: 1025
+ onOpen: ignore
+ - port: 3306
+ onOpen: ignore
+ - port: 33060
+ onOpen: ignore
+ - port: 8000
+ visibility: public
+ onOpen: ignore
+ - port: 8025
+ onOpen: ignore
+ - port: 9003
+ onOpen: ignore
+
+vscode:
+ extensions:
+ - felixfbecker.php-debug
+ - bmewburn.vscode-intelephense-client
+
+github:
+ prebuilds:
+ master: true
+ branches: false
+ pullRequests: true
+ pullRequestsFromForks: false
+ addCheck: true
+ addComment: false
+ addBadge: true
diff --git a/.gitpod/Dockerfile b/.gitpod/Dockerfile
new file mode 100644
index 0000000000..50d7443ee6
--- /dev/null
+++ b/.gitpod/Dockerfile
@@ -0,0 +1,71 @@
+FROM gitpod/workspace-mysql
+
+# Install XDebug extension
+RUN sudo apt-get update -q \
+ && sudo apt-get install -y \
+ php-dev \
+ golang-go \
+ && sudo pecl install xdebug
+
+# Install Composer 2 (Gitpod comes pre-installed with Composer 1) - borrowed from official Composer Docker image
+ENV COMPOSER_ALLOW_SUPERUSER 1
+ENV COMPOSER_HOME /tmp
+ENV COMPOSER_VERSION 2.1.6
+
+RUN set -eux; \
+ curl \
+ --silent \
+ --fail \
+ --location \
+ --retry 3 \
+ --output /tmp/keys.dev.pub \
+ --url https://raw.githubusercontent.com/composer/composer.github.io/e7f28b7200249f8e5bc912b42837d4598c74153a/snapshots.pub \
+ ; \
+ php -r " \
+ \$signature = '4ac45767e5ec22652f0c1167cbbb8a2b0c708369153e328cad90147dafe50952'; \
+ \$hash = hash('sha256', preg_replace('{\s}', '', file_get_contents('/tmp/keys.dev.pub'))); \
+ if (!hash_equals(\$signature, \$hash)) { \
+ echo 'Integrity check failed, dev public key is either corrupt or worse.' . PHP_EOL; \
+ exit(1); \
+ }" \
+ ; \
+ curl \
+ --silent \
+ --fail \
+ --location \
+ --retry 3 \
+ --output /tmp/keys.tags.pub \
+ --url https://raw.githubusercontent.com/composer/composer.github.io/e7f28b7200249f8e5bc912b42837d4598c74153a/releases.pub \
+ ; \
+ php -r " \
+ \$signature = '57815ba27e54dc317ecc7cc5573090d087719ba68f3bb7234e5d42d084a14642'; \
+ \$hash = hash('sha256', preg_replace('{\s}', '', file_get_contents('/tmp/keys.tags.pub'))); \
+ if (!hash_equals(\$signature, \$hash)) { \
+ echo 'Integrity check failed, tags public key is either corrupt or worse.' . PHP_EOL; \
+ exit(1); \
+ }" \
+ ; \
+ curl \
+ --silent \
+ --fail \
+ --location \
+ --retry 3 \
+ --output /tmp/installer.php \
+ --url https://raw.githubusercontent.com/composer/getcomposer.org/f24b8f860b95b52167f91bbd3e3a7bcafe043038/web/installer \
+ ; \
+ php -r " \
+ \$signature = '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3'; \
+ \$hash = hash('sha384', file_get_contents('/tmp/installer.php')); \
+ if (!hash_equals(\$signature, \$hash)) { \
+ echo 'Integrity check failed, installer is either corrupt or worse.' . PHP_EOL; \
+ exit(1); \
+ }" \
+ ; \
+ sudo php /tmp/installer.php --no-ansi --install-dir=/usr/bin --filename=composer --version=${COMPOSER_VERSION}; \
+ composer --ansi --version --no-interaction; \
+ composer diagnose; \
+ rm -f /tmp/installer.php; \
+ sudo find /tmp -type d -exec chmod -v 1777 {} +
+
+# Configure Xdebug
+RUN sudo bash -c "echo -e '\nzend_extension = /usr/lib/php/20190902/xdebug.so\n\n[XDebug]\nxdebug.mode=debug\nxdebug.start_with_request = 1\nxdebug.client_host = 127.0.0.1\n' >> /etc/php/7.4/cli/php.ini"
\ No newline at end of file
diff --git a/.gitpod/README.md b/.gitpod/README.md
new file mode 100644
index 0000000000..183ca41dd3
--- /dev/null
+++ b/.gitpod/README.md
@@ -0,0 +1,53 @@
+# Winter CMS on Gitpod
+
+Winter CMS now supports the [Gitpod.io](https://gitpod.io) service to provide near-instant development and testing environments for Winter CMS.
+
+This service allows you to check out the Winter CMS codebase at any commit, any branch or any pull request and be given a full Visual Studio Code environment that is completely configured and bootstrapped to run Winter CMS immediately.
+
+Each instance contains the following:
+
+- Winter CMS with the [DebugBar plugin](https://github.com/wintercms/wn-debugbar-plugin).
+- VSCode.
+- MySQL 5.7.
+- PHP 7.4 with all required extensions.
+- PHP Xdebug extension.
+- Composer 2.
+- [MailHog service](https://github.com/mailhog/MailHog/) for capturing emails.
+
+To use this service, you will need an account on Gitpod - one can easily be created by using your GitHub login. You will receive 50 hours per month free for use on Gitpod, but can opt to increase your hours (or even get unlimited hours) by [purchasing a higher plan](https://www.gitpod.io/pricing) on Gitpod.
+
+## Creating a Gitpod instance
+
+There are several ways to create a Gitpod instance of Winter CMS:
+
+- Use one of the **Open in Gitpod** button, which will be available in the README of Winter CMS, as well as any pull request submitted to Winter CMS.
+- Install the [Gitpod extension](https://www.gitpod.io/docs/browser-extension#browser-extension) for Chrome or Firefox, which provides a **Gitpod** button in GitHub.
+- Manually create an instance by copying a GitHub address within the Winter CMS repo, and prefixing the address with `https://gitpod.io/#/`
+
+The Gitpod instance may take a minute or two to boot up if it has not been pre-built.
+
+> **Note:** For brevity, Gitpod instances have the initial admin account set to **admin / admin** as the username and password to login. If you intend to share the URL, we recommend you change this password.
+
+## Accessing the services
+
+The Gitpod instance is set-up to boot all necessary services and then provides two web-facing services - the Winter CMS install itself, which is run on port 8000, and MailHog, which is run on port 8025.
+
+You can click the **Ports** section in the status bar of VSCode, which will take you to the available ports, and click on one of these ports to view the actions for the port. The "globe" icon will open up a special URL which will access the service on that port. We automatically load up Winter CMS on boot in a new tab.
+
+### MySQL
+
+By default, MySQL only runs locally within the Gitpod instance, and cannot be connected to from the outside. However, you can use the [Gitpod Local Companion](https://www.gitpod.io/blog/local-app) service to tunnel into the running Gitpod instance and access its services on your own computer.
+
+Install the app for your OS, then run `gitpod-local-companion-[darwin|linux|windows]` to set up the tunneling service. For MySQL, this will make the database available on port 3306 on your computer.
+
+You can then connect to it using any MySQL management program of your choice.
+
+## Config files
+
+By default, the Winter CMS Gitpod instance will use `php artisan winter:env` to create an `.env` file that will contain your config. Because this command rewrites the main config files in the `config` directory, which will appear as changes in Git, we mark these files as "unwatched" in Git so that they are not committed to GitHub.
+
+If you are editing a pull request that does contain config changes that you wish to include in the PR, you can use the `.gitpod/gitpod-show-config` helper script inside your Gitpod instance to make these files appear in the Git changes.
+
+## Debugging
+
+Debugging Winter CMS in Gitpod is super simple - all necessary setup has already been done! You can access the Debugging tab in VSCode and press start on the debugging tool to use breakpoints within your code. Note that Gitpod does impose a time limit on responses from the web server, so you may find that using the debugging will result in timeouts when viewing your Winter CMS instance, however, debugging should still continue without issue.
diff --git a/.gitpod/gitpod-cmd b/.gitpod/gitpod-cmd
new file mode 100755
index 0000000000..fd285b6e84
--- /dev/null
+++ b/.gitpod/gitpod-cmd
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Create database
+gp await-port 3306 && mysql -e "DROP DATABASE IF EXISTS winter"
+mysql -e "CREATE DATABASE winter"
+
+# Create environment file
+php artisan winter:env
+
+# Hide config file changes from Git
+./.gitpod/gitpod-hide-config
+
+# Exclude Debugbar files from Git
+echo -e "plugins/winter/debugbar\nstorage/debugbar" >> ./.git/info/exclude
+
+# Rewrite configuration
+sed -i "s|APP_URL=.*$|APP_URL=${GITPOD_WORKSPACE_URL}|g" .env
+sed -i "s|APP_URL=https://|APP_URL=https://8000-|g" .env
+sed -i "s|APP_KEY=.*$|APP_KEY=$(cat /dev/urandom | base64 | head -c 32)|g" .env
+sed -i "s|DB_CONNECTION=.*$|DB_CONNECTION=mysql|g" .env
+sed -i "s|DB_HOST=.*$|DB_HOST=127.0.0.1|g" .env
+sed -i "s|DB_PORT=.*$|DB_PORT=3306|g" .env
+sed -i "s|DB_DATABASE=.*$|DB_DATABASE=winter|g" .env
+sed -i "s|DB_USERNAME=.*$|DB_USERNAME=root|g" .env
+sed -i "s|DB_PASSWORD=.*$|DB_PASSWORD=|g" .env
+sed -i "s|MAIL_DRIVER=.*$|MAIL_DRIVER=smtp|g" .env
+sed -i "s|MAIL_HOST=.*$|MAIL_HOST=127.0.0.1|g" .env
+sed -i "s|MAIL_PORT=.*$|MAIL_PORT=1025|g" .env
+sed -i "s|MAIL_ENCRYPTION=.*$|MAIL_ENCRYPTION=null|g" .env
+sed -i "s|MAIL_USERNAME=.*$|MAIL_USERNAME=null|g" .env
+sed -i "s|MAIL_PASSWORD=.*$|MAIL_PASSWORD=null|g" .env
+sed -i "s|'trustedProxies' => null|'trustedProxies' => '*'|g" config/app.php
+
+# Run migrations
+php artisan winter:up
+
+# Set admin password
+php artisan winter:passwd admin admin
+
+# Run Mailhog in background
+/workspace/go/bin/MailHog >/dev/null 2>&1 &
+
+# Serve site
+php artisan serve >/dev/null 2>&1 &
+gp await-port 8000 && gp preview --external $(gp url 8000)
+
+# Open README
+gp open ./.gitpod/README.md
diff --git a/.gitpod/gitpod-hide-config b/.gitpod/gitpod-hide-config
new file mode 100755
index 0000000000..60928c054d
--- /dev/null
+++ b/.gitpod/gitpod-hide-config
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Hide config file edits from Git changes
+git update-index --skip-worktree config/app.php
+git update-index --skip-worktree config/cache.php
+git update-index --skip-worktree config/cms.php
+git update-index --skip-worktree config/database.php
+git update-index --skip-worktree config/mail.php
+git update-index --skip-worktree config/queue.php
+git update-index --skip-worktree config/session.php
diff --git a/.gitpod/gitpod-init b/.gitpod/gitpod-init
new file mode 100755
index 0000000000..086705c791
--- /dev/null
+++ b/.gitpod/gitpod-init
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Install Mailhog
+go get github.com/mailhog/MailHog
+
+# Add Debug Bar plugin
+composer require --no-update "winter/wn-debugbar-plugin" "4.0.0"
+
+# Install Composer dependencies
+composer update --no-progress
+git reset --hard
+
+# Setup VSCode config
+mkdir -p ./.vscode
+cp ./.gitpod/vscode-launch.json ./.vscode/launch.json
diff --git a/.gitpod/gitpod-show-config b/.gitpod/gitpod-show-config
new file mode 100755
index 0000000000..f7d4d6f88c
--- /dev/null
+++ b/.gitpod/gitpod-show-config
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Hide config file edits from Git changes
+git update-index --no-skip-worktree config/app.php
+git update-index --no-skip-worktree config/cache.php
+git update-index --no-skip-worktree config/cms.php
+git update-index --no-skip-worktree config/database.php
+git update-index --no-skip-worktree config/mail.php
+git update-index --no-skip-worktree config/queue.php
+git update-index --no-skip-worktree config/session.php
diff --git a/.gitpod/vscode-launch.json b/.gitpod/vscode-launch.json
new file mode 100644
index 0000000000..542060d33f
--- /dev/null
+++ b/.gitpod/vscode-launch.json
@@ -0,0 +1,14 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Listen for Xdebug",
+ "type": "php",
+ "request": "launch",
+ "port": 9003
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index c04ce35af6..1185b210f3 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,47 @@
-
+
-[Winter](https://wintercms.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.
+[Winter](https://wintercms.com) is a free, open-source content management system based on the [Laravel](https://laravel.com) PHP framework. Developers and agencies all around the world rely upon Winter for its quick prototyping and development, safe and secure codebase and dedication to simplicity.
-Winter's mission is to show the world that web development is not rocket science.
+No matter how large or small your project is, Winter provides a rich development environment, regardless of your level of experience.
[![Version](https://img.shields.io/github/v/release/wintercms/winter?sort=semver&style=flat-square)](https://github.com/wintercms/winter/releases)
[![Tests](https://img.shields.io/github/workflow/status/wintercms/winter/Tests/develop?label=tests&style=flat-square)](https://github.com/wintercms/winter/actions)
[![License](https://img.shields.io/github/license/wintercms/winter?label=open%20source&style=flat-square)](https://packagist.org/packages/wintercms/winter)
[![Discord](https://img.shields.io/discord/816852513684193281?label=discord&style=flat-square)](https://discord.gg/D5MFSPH6Ux)
+[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/wintercms/winter)
+
## Installing Winter
-Instructions on how to install Winter can be found at the [installation guide](https://wintercms.com/docs/setup/installation).
+Winter can be installed in several ways for both new users and experienced developers - see our [Installation page](https://wintercms.com/install) for more information.
-### Quick Start Installation
+### Quick start with Composer
-For advanced users, run this in your terminal to install Winter from command line:
+For advanced users, run the following command in your terminal to install Winter via Composer:
```shell
composer create-project wintercms/winter example.com "dev-develop"
```
-If you plan on using a database, run this command inside the application directory.
+Run the following command with the folder created by the previous command to generate an environment file which will contain your configuration settings:
```shell
-php artisan winter:install
+php artisan winter:env
```
-## Learning Winter
+After configuring your installation, you can run the following command to run the database migrations and automatically create an administrator account with the username `admin`. The password of this account will be automatically generated and displayed in your terminal.
+
+```shell
+php artisan winter:up
+```
-The best place to learn Winter is by [reading the documentation](https://wintercms.com/docs), [watching some screencasts](https://octobercms.com/support/topic/screencast) or [following some tutorials](https://octobercms.com/support/articles/tutorials).
+## Learning Winter
-You may also watch these introductory videos for [beginners](https://vimeo.com/79963873) and [advanced users](https://vimeo.com/172202661).
+The best place to learn Winter is by [reading the documentation](https://wintercms.com/docs) or [following some tutorials](https://wintercms.com/blog/category/tutorials). You can also join the maintenance team and our active community on [Discord](https://discord.gg/D5MFSPH6Ux) who are always willing to help out with questions.
-## Development Team
+## Development team
Winter was forked from October CMS in March 2021 due to a difference in open source management philosophies between the core maintainer team and the two founders of October.
@@ -52,18 +58,18 @@ The development of Winter is lead by [Luke Towers](https://luketowers.ca/), alon
## Foundation library
-The CMS is built on top of the wildly-popular [Laravel](https://laravel.com) PHP framework, with the in-house [Storm](https://github.com/wintercms/storm) Library as a buffer between the Laravel Framework and the CMS project to minimize breaking changes and improve stability.
+Winter is built on top of the wildly-popular [Laravel](https://laravel.com) framework for PHP, with the in-house [Storm](https://github.com/wintercms/storm) library as a buffer between the Laravel framework and the Winter project, to minimize breaking changes and improve stability.
-## Contact
+## Getting in touch
-You can communicate with us using the following mediums:
+You can get in touch with the maintainer team using the following mediums:
* [Follow us on Twitter](https://twitter.com/usewintercms) for announcements and updates.
* [Join us on Discord](https://discord.gg/D5MFSPH6Ux) to chat with us.
## Contributing
-Before sending or reviewing Pull Requests, be sure to review the [Contributing Guidelines](https://github.com/wintercms/.github/blob/master/CONTRIBUTING.md) first.
+Before contributing issues or pull requests, be sure to review the [Contributing Guidelines](https://github.com/wintercms/.github/blob/master/CONTRIBUTING.md) first.
### Coding standards
@@ -73,14 +79,14 @@ Please follow the following guides and code standards:
* [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
+### Code of conduct
-In order to ensure that the Winter CMS community is welcoming to all, please review and abide by the [Code of Conduct](https://github.com/wintercms/.github/blob/master/CODE_OF_CONDUCT.md).
+In order to ensure that the Winter community is welcoming to all, please review and abide by the [Code of Conduct](https://github.com/wintercms/.github/blob/master/CODE_OF_CONDUCT.md).
## License
-The Winter CMS platform is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
+The Winter platform is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
-## Security Vulnerabilities
+## Security vulnerabilities
Please review [our security policy](https://github.com/wintercms/winter/security/policy) on how to report security vulnerabilities.
diff --git a/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm b/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm
index 5e7b020398..d8df34c81d 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_button_delete.htm
@@ -3,7 +3,7 @@
class="btn btn-sm btn-secondary wn-icon-trash-o"
data-request="onRelationButtonDelete"
data-request-confirm="= e(trans('backend::lang.relation.delete_confirm')) ?>"
- data-request-success="$.wn.relationBehavior.changed('= e($this->vars['relationField']) ?>', 'deleted')"
+ data-request-success="$.wn.relationBehavior.changed('= e($relationField) ?>', 'deleted')"
data-stripe-load-indicator>
= e(trans($text)) ?>
@@ -16,7 +16,7 @@
disabled="disabled"
data-request="onRelationButtonDelete"
data-request-confirm="= e(trans('backend::lang.relation.delete_confirm')) ?>"
- data-request-success="$.wn.relationBehavior.changed('= e($this->vars['relationField']) ?>', 'deleted')"
+ data-request-success="$.wn.relationBehavior.changed('= e($relationField) ?>', 'deleted')"
data-trigger-action="enable"
data-trigger="#= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
data-trigger-condition="checked"
diff --git a/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm b/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm
index 3c5a12f771..a5326e4f4d 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_button_remove.htm
@@ -2,7 +2,7 @@
@@ -14,7 +14,7 @@
})"
disabled="disabled"
data-request="onRelationButtonRemove"
- data-request-success="$.wn.relationBehavior.changed('= e($this->vars['relationField']) ?>', 'removed')"
+ data-request-success="$.wn.relationBehavior.changed('= e($relationField) ?>', 'removed')"
data-trigger-action="enable"
data-trigger="#= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
data-trigger-condition="checked"
diff --git a/modules/backend/behaviors/relationcontroller/partials/_button_unlink.htm b/modules/backend/behaviors/relationcontroller/partials/_button_unlink.htm
index d7ea202cb0..59ea3ec221 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_button_unlink.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_button_unlink.htm
@@ -2,7 +2,7 @@
href="javascript:;"
class="btn btn-sm btn-secondary wn-icon-unlink"
data-request="onRelationButtonUnlink"
- data-request-success="$.wn.relationBehavior.changed('= e($this->vars['relationField']) ?>', 'removed')"
+ data-request-success="$.wn.relationBehavior.changed('= e($relationField) ?>', 'removed')"
data-request-confirm="= e(trans('backend::lang.relation.unlink_confirm')) ?>"
data-stripe-load-indicator>
= e(trans($text)) ?>
diff --git a/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm b/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm
index c127f91c77..b60295bdf9 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_manage_form.htm
@@ -4,7 +4,7 @@
= Form::ajax('onRelationManageUpdate', [
'data-popup-load-indicator' => true,
'sessionKey' => $newSessionKey,
- 'data-request-success' => "$.wn.relationBehavior.changed('" . e($this->vars['relationField']) . "', 'updated')",
+ 'data-request-success' => "$.wn.relationBehavior.changed('" . e($relationField) . "', 'updated')",
]) ?>
@@ -53,7 +53,7 @@
= e(trans('backend::lang.relation.related_data', ['nam
= Form::close() ?>
-
-
-
-
diff --git a/modules/backend/behaviors/relationcontroller/partials/_view.htm b/modules/backend/behaviors/relationcontroller/partials/_view.htm
index 7f154eb884..2b14e438e5 100644
--- a/modules/backend/behaviors/relationcontroller/partials/_view.htm
+++ b/modules/backend/behaviors/relationcontroller/partials/_view.htm
@@ -2,4 +2,4 @@
= $relationViewFilterWidget->render() ?>
-= $relationViewWidget->render() ?>
\ No newline at end of file
+= $relationViewWidget->render() ?>
diff --git a/modules/backend/formwidgets/ColorPicker.php b/modules/backend/formwidgets/ColorPicker.php
index e7efb76bc1..d4d3712b91 100644
--- a/modules/backend/formwidgets/ColorPicker.php
+++ b/modules/backend/formwidgets/ColorPicker.php
@@ -3,6 +3,7 @@
use Lang;
use Backend\Classes\FormWidgetBase;
use ApplicationException;
+use Backend\Models\BrandSetting;
/**
* Color picker
@@ -10,9 +11,13 @@
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
+ * @author Winter CMS
*/
class ColorPicker extends FormWidgetBase
{
+ // All color formats supported
+ const ALL_FORMATS = ['cmyk', 'hex', 'hsl', 'rgb'];
+
//
// Configurable properties
//
@@ -20,24 +25,18 @@ class ColorPicker extends FormWidgetBase
/**
* @var array Default available colors
*/
- public $availableColors = [
- '#1abc9c', '#16a085',
- '#2ecc71', '#27ae60',
- '#3498db', '#2980b9',
- '#9b59b6', '#8e44ad',
- '#34495e', '#2b3e50',
- '#f1c40f', '#f39c12',
- '#e67e22', '#d35400',
- '#e74c3c', '#c0392b',
- '#ecf0f1', '#bdc3c7',
- '#95a5a6', '#7f8c8d',
- ];
+ public $availableColors = null;
/**
* @var bool Allow empty value
*/
public $allowEmpty = false;
+ /**
+ * @var bool Allow a custom color
+ */
+ public $allowCustom = true;
+
/**
* @var bool Show opacity slider
*/
@@ -53,6 +52,13 @@ class ColorPicker extends FormWidgetBase
*/
public $disabled = false;
+ /**
+ * @var string|array Color format(s) to allow for the resulting color value. Specify "all" as a string to allow all
+ * formats.
+ * Allowed values: 'cmyk', 'hex', 'hsl', 'rgb', 'all'
+ */
+ public $formats = 'hex';
+
//
// Object properties
//
@@ -69,7 +75,9 @@ public function init()
{
$this->fillFromConfig([
'availableColors',
+ 'formats',
'allowEmpty',
+ 'allowCustom',
'showAlpha',
'readOnly',
'disabled',
@@ -91,13 +99,14 @@ public function render()
public function prepareVars()
{
$this->vars['name'] = $this->getFieldName();
- $this->vars['value'] = $value = $this->getLoadValue();
- $this->vars['availableColors'] = $availableColors = $this->getAvailableColors();
- $this->vars['allowEmpty'] = $this->allowEmpty;
- $this->vars['showAlpha'] = $this->showAlpha;
- $this->vars['readOnly'] = $this->readOnly;
- $this->vars['disabled'] = $this->disabled;
- $this->vars['isCustomColor'] = !in_array($value, $availableColors);
+ $this->vars['value'] = $this->getLoadValue();
+ $this->vars['availableColors'] = $this->getAvailableColors();
+ $this->vars['formats'] = $this->getFormats();
+ $this->vars['allowEmpty'] = (bool) $this->allowEmpty;
+ $this->vars['allowCustom'] = (bool) $this->allowCustom;
+ $this->vars['showAlpha'] = (bool) $this->showAlpha;
+ $this->vars['readOnly'] = (bool) $this->readOnly;
+ $this->vars['disabled'] = (bool) $this->disabled;
}
/**
@@ -108,10 +117,10 @@ public function prepareVars()
protected function getAvailableColors()
{
$availableColors = $this->availableColors;
+
if (is_array($availableColors)) {
return $availableColors;
- }
- elseif (is_string($availableColors) && !empty($availableColors)) {
+ } elseif (is_string($availableColors) && !empty($availableColors)) {
if ($this->model->methodExists($availableColors)) {
return $this->availableColors = $this->model->{$availableColors}(
$this->formField->fieldName,
@@ -125,16 +134,110 @@ protected function getAvailableColors()
'field' => $this->formField->fieldName
]));
}
+ } else {
+ return $this->availableColors = array_map(function ($color) {
+ return $color['color'];
+ }, BrandSetting::get('default_colors', [
+ [
+ 'color' => '#1abc9c',
+ ],
+ [
+ 'color' => '#16a085',
+ ],
+ [
+ 'color' => '#2ecc71',
+ ],
+ [
+ 'color' => '#27ae60',
+ ],
+ [
+ 'color' => '#3498db',
+ ],
+ [
+ 'color' => '#2980b9',
+ ],
+ [
+ 'color' => '#9b59b6',
+ ],
+ [
+ 'color' => '#8e44ad',
+ ],
+ [
+ 'color' => '#34495e',
+ ],
+ [
+ 'color' => '#2b3e50',
+ ],
+ [
+ 'color' => '#f1c40f',
+ ],
+ [
+ 'color' => '#f39c12',
+ ],
+ [
+ 'color' => '#e67e22',
+ ],
+ [
+ 'color' => '#d35400',
+ ],
+ [
+ 'color' => '#e74c3c',
+ ],
+ [
+ 'color' => '#c0392b',
+ ],
+ [
+ 'color' => '#ecf0f1',
+ ],
+ [
+ 'color' => '#bdc3c7',
+ ],
+ [
+ 'color' => '#95a5a6',
+ ],
+ [
+ 'color' => '#7f8c8d',
+ ],
+ ]));
}
}
+ /**
+ * Returns the allowed color formats.
+ *
+ * If no valid formats are specified, the "hex" format will be used.
+ *
+ * @return array
+ */
+ protected function getFormats()
+ {
+ if ($this->formats === 'all') {
+ return static::ALL_FORMATS;
+ }
+
+ $availableFormats = [];
+ $configFormats = (is_string($this->formats))
+ ? [$this->formats]
+ : $this->formats;
+
+ foreach ($configFormats as $format) {
+ if (in_array($format, static::ALL_FORMATS)) {
+ $availableFormats[] = $format;
+ }
+ }
+
+ return (count($availableFormats))
+ ? $availableFormats
+ : ['hex'];
+ }
+
/**
* @inheritDoc
*/
protected function loadAssets()
{
- $this->addCss('vendor/spectrum/spectrum.css', 'core');
- $this->addJs('vendor/spectrum/spectrum.js', 'core');
+ $this->addCss('vendor/pickr/pickr-nano.min.css', 'core');
+ $this->addJs('vendor/pickr/pickr.min.js', 'core');
$this->addCss('css/colorpicker.css', 'core');
$this->addJs('js/colorpicker.js', 'core');
}
diff --git a/modules/backend/formwidgets/Relation.php b/modules/backend/formwidgets/Relation.php
index 2e2b3b8c4f..e15462fcd7 100644
--- a/modules/backend/formwidgets/Relation.php
+++ b/modules/backend/formwidgets/Relation.php
@@ -3,7 +3,9 @@
use Db;
use Backend\Classes\FormField;
use Backend\Classes\FormWidgetBase;
+use Lang;
use Winter\Storm\Database\Relations\Relation as RelationBase;
+use Winter\Storm\Exception\SystemException;
/**
* Form Relationship
@@ -95,6 +97,7 @@ public function prepareVars()
/**
* Makes the form object used for rendering a simple field type
+ * @throws SystemException if an unsupported relation type is used.
*/
protected function makeRenderFormField()
{
@@ -110,9 +113,14 @@ protected function makeRenderFormField()
if (in_array($relationType, ['belongsToMany', 'morphToMany', 'morphedByMany', 'hasMany'])) {
$field->type = 'checkboxlist';
- }
- elseif (in_array($relationType, ['belongsTo', 'hasOne'])) {
+ } elseif (in_array($relationType, ['belongsTo', 'hasOne'])) {
$field->type = 'dropdown';
+ } else {
+ throw new SystemException(
+ Lang::get('backend::lang.relation.relationwidget_unsupported_type', [
+ 'type' => $relationType
+ ])
+ );
}
// Order query by the configured option.
diff --git a/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css b/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css
index 097f0733e2..3f2591eaf2 100644
--- a/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css
+++ b/modules/backend/formwidgets/codeeditor/assets/css/codeeditor.css
@@ -24,4 +24,4 @@
.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 .ace_search {z-index:303}
\ No newline at end of file
+.field-codeeditor.editor-fullscreen .ace_search {z-index:303}
diff --git a/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css b/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css
index a18ca9f3b4..53498005b6 100644
--- a/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css
+++ b/modules/backend/formwidgets/colorpicker/assets/css/colorpicker.css
@@ -1,113 +1,12 @@
-.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}
-.field-colorpicker.disabled >ul li:hover {cursor:not-allowed;background-color:transparent}
-.field-colorpicker.disabled >ul li:hover span {border:1px solid #cecece}
-.field-colorpicker.disabled >ul li.active {background-color:transparent}
-.field-colorpicker.disabled >ul li.custom-color {position:relative}
-.field-colorpicker.disabled >ul li.custom-color span {border-style:dashed}
-.field-colorpicker.disabled >ul li.custom-color span.disabled.is-empty:before,
-.field-colorpicker.disabled >ul li.custom-color span.disabled.is-empty:after {position:absolute;left:15px;content:' ';height:24px;width:1px;background-color:#cecece}
-.field-colorpicker.disabled >ul li.custom-color span.disabled.is-empty:before {transform:rotate(45deg)}
-.field-colorpicker.disabled >ul li.custom-color span.disabled.is-empty:after {transform:rotate(-45deg)}
-.field-colorpicker.disabled >ul li.custom-color:hover span {border-style:dashed}
-.field-colorpicker.disabled >ul li.custom-color.active span {border:1px solid #cecece}
-.sp-hue,
-.sp-slider {cursor:row-resize}
-.sp-color,
-.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-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-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:#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,
-.sp-container button:focus,
-.sp-container button:hover:focus,
-.sp-container button:active:focus,
-.sp-container button:active,
-.sp-container button:hover:active,
-.sp-container button:active:active,
-.sp-container button.active,
-.sp-container button:hover.active,
-.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:#fff;background:#1681ba;border-color:#1681ba}
-.sp-container button:active,
-.sp-container button:hover:active,
-.sp-container button:active:active,
-.sp-container button.active,
-.sp-container button:hover.active,
-.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}
-.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.disabled,
-.sp-container button:hover.disabled,
-.sp-container button:active.disabled,
-.sp-container button[disabled],
-.sp-container button:hover[disabled],
-.sp-container button:active[disabled],
-.sp-container button.disabled:hover,
-.sp-container button:hover.disabled:hover,
-.sp-container button:active.disabled:hover,
-.sp-container button[disabled]:hover,
-.sp-container button:hover[disabled]:hover,
-.sp-container button:active[disabled]:hover,
-.sp-container button.disabled:focus,
-.sp-container button:hover.disabled:focus,
-.sp-container button:active.disabled:focus,
-.sp-container button[disabled]:focus,
-.sp-container button:hover[disabled]:focus,
-.sp-container button:active[disabled]:focus,
-.sp-container button.disabled:active,
-.sp-container button:hover.disabled:active,
-.sp-container button:active.disabled:active,
-.sp-container button[disabled]:active,
-.sp-container button:hover[disabled]:active,
-.sp-container button:active[disabled]:active,
-.sp-container button.disabled.active,
-.sp-container button:hover.disabled.active,
-.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 .badge,
-.sp-container button:hover .badge,
-.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-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,
-.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
+.field-colorpicker .colorpicker-container{display:flex;flex-direction:row-reverse;position:relative;max-width:300px;width:100%}
+.field-colorpicker [data-color-preview]{flex-grow:0;flex-shrink:0;width:40px;height:38px;background:#fff;cursor:pointer;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);border-top-left-radius:3px;border-bottom-left-radius:3px}
+.field-colorpicker [data-color-value]{flex:1;border-left:none;border-top-left-radius:0;border-bottom-left-radius:0}
+.field-colorpicker .pcr-app[data-theme="nano"]{width:18em}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection{grid-template-rows:1fr 5fr auto auto;gap:0}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview{grid-row-start:1;grid-column-start:1;grid-row-end:1;grid-column-end:3;margin:0;flex-direction:row-reverse}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview .pcr-current-color{flex-grow:0;flex-shrink:0;width:80%;border-radius:0;border-bottom:1px solid rgba(0,0,0,0.5);font-family:monospace;height:28px;line-height:29px;text-align:center;font-size:0.85em}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview .pcr-current-color::before{content:none}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection .pcr-color-preview .pcr-last-color{display:block;flex-grow:0;flex-shrink:0;width:20%;border-radius:0;background:var(--pcr-color);border-bottom:1px solid rgba(0,0,0,0.5)}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection .pcr-color-palette{grid-row-start:2;grid-column-start:1;grid-row-end:2;grid-column-end:3}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection .pcr-color-chooser{margin-top:0.8rem;grid-row-start:3;grid-column-start:1;grid-row-end:3;grid-column-end:3}
+.field-colorpicker .pcr-app[data-theme="nano"] .pcr-selection .pcr-color-opacity{margin-top:0.8em;grid-row-start:4;grid-column-start:1;grid-row-end:4;grid-column-end:3}
\ 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 90f2b3c3eb..8f30d5d6ee 100644
--- a/modules/backend/formwidgets/colorpicker/assets/js/colorpicker.js
+++ b/modules/backend/formwidgets/colorpicker/assets/js/colorpicker.js
@@ -9,7 +9,7 @@
* $('div#someElement').colorPicker({ dataLocker: 'input#locker' })
*
* Dependences:
- * - Some other plugin (filename.js)
+ * - Pickr (https://github.com/Simonwep/pickr)
*/
+function ($) { "use strict";
@@ -17,94 +17,391 @@
// COLORPICKER CLASS DEFINITION
// ============================
+ var Base = $.wn.foundation.base,
+ BaseProto = Base.prototype
+
var ColorPicker = function(element, options) {
this.options = options
this.$el = $(element)
+ $.wn.foundation.controlUtils.markDisposable(element)
+ Base.call(this)
+
// Init
this.init()
}
ColorPicker.DEFAULTS = {
showAlpha: false,
+ format: 'hex',
allowEmpty: false,
dataLocker: null,
disabled: false
}
+ ColorPicker.prototype = Object.create(BaseProto)
+ ColorPicker.prototype.constructor = ColorPicker
+
+ /**
+ * Initialise the widget.
+ */
ColorPicker.prototype.init = function() {
- var self = this
- this.$dataLocker = $(this.options.dataLocker, this.$el)
- this.$colorList = $('>ul', this.$el)
- this.$customColor = $('[data-custom-color]', this.$el)
- this.$customColorSpan = $('>span', this.$customColor)
- this.originalColor = this.$customColor.data('hexColor')
-
- if (!this.options.disabled) {
- this.$colorList.on('click', '>li', function(){
- self.selectColor(this)
- self.$dataLocker.trigger('change')
- })
- }
+ this.$dataLocker = $(this.options.dataLocker, this.$el)
+ this.$container = $('.colorpicker-container', this.$el)
+ this.$colorPreview = $('[data-color-preview]', this.$el)
+ this.$colorValue = $('[data-color-value]', this.$el)
+ this.keyboardEntry = false
+ this.originalColor = null
+ this.originalFormat = null
- /*
- * Custom color
- */
- if (this.$customColor.length) {
- this.$customColor.spectrum({
- preferredFormat: 'hex',
- showInput: true,
- showAlpha: this.options.showAlpha,
- allowEmpty: this.options.allowEmpty,
- color: this.$customColor.data('hexColor'),
- chooseText: $.wn.lang.get('colorpicker.choose', 'Ok'),
- cancelText: '⨯',
- appendTo: 'parent',
- disabled: this.options.disabled,
- hide: function(color) {
- var hex = color ? color.toHexString() : ''
- self.$customColorSpan.css('background', hex)
- },
- show: function(color) {
- self.selectColor(self.$customColor)
- },
- move: function(color) {
- var hex = color ? color.toHexString() : ''
- self.$customColorSpan.css('background', hex)
- },
- change: function(color) {
- var hex = color ? color.toHexString() : ''
- self.setCustomColor(hex)
+ // Create a Pickr instance
+ this.pickr = Pickr.create({
+ el: this.$colorPreview.get(0),
+ theme: 'nano',
+ disabled: this.options.disabled,
+ swatches: this.options.availableColors,
+ lockOpacity: !this.options.showAlpha,
+ useAsButton: true,
+ container: this.$el.get(0),
+ comparison: true,
+ showAlways: true,
+ position: 'top-middle',
+ components: {
+ palette: this.options.allowCustom,
+ preview: this.options.allowCustom,
+ hue: this.options.allowCustom,
+ opacity: this.options.showAlpha,
+ interaction: {
+ hex: (this.options.formats.length > 1 && this.options.formats.indexOf('hex') !== -1),
+ rgba: (this.options.formats.length > 1 && this.options.formats.indexOf('rgb') !== -1),
+ hsla: (this.options.formats.length > 1 && this.options.formats.indexOf('hsl') !== -1),
+ cmyk: (this.options.formats.length > 1 && this.options.formats.indexOf('cmyk') !== -1),
+
+ input: false,
+ cancel: false,
+ clear: this.options.allowEmpty,
+ save: false,
}
- })
+ },
+ i18n: {
+ 'btn:last-color': $.wn.lang.get('colorpicker.last_color', 'Use previously selected color'),
+ 'aria:palette': $.wn.lang.get('colorpicker.aria_palette', 'Color selection area'),
+ 'aria:hue': $.wn.lang.get('colorpicker.aria_hue', 'Hue selection slider'),
+ 'aria:opacity': $.wn.lang.get('colorpicker.aria_opacity', 'Opacity selection slider'),
+ },
+ })
+
+ // Set up events on various elements
+ this.$colorValue.on('focus', this.proxy(this.onFocus))
+ this.$colorValue.on('blur', this.proxy(this.onBlur))
+ this.$colorValue.on('keydown', this.proxy(this.onKeydown))
+ this.$colorValue.on('keyup', this.proxy(this.onKeyup))
+ this.$colorPreview.on('click', this.proxy(this.onColorClick))
+ this.pickr.on('init', () => this.onPickerInit())
+ this.pickr.on('change', (hsva) => this.onPickerChange(hsva))
+ this.pickr.on('changestop', () => this.onPickerStopChange())
+ this.pickr.on('swatchselect', (hsva) => this.onPickerSwatch(hsva))
+ this.pickr.on('cancel', () => this.onPickerStopChange())
+ this.pickr.on('hide', () => this.onPickerHide())
+ }
+
+ /**
+ * Disposes the element.
+ */
+ ColorPicker.prototype.dispose = function () {
+ this.$colorValue.off('focus', this.proxy(this.onFocus))
+ this.$colorValue.off('blur', this.proxy(this.onBlur))
+ this.$colorValue.off('keydown', this.proxy(this.onKeydown))
+ this.$colorValue.off('keyup', this.proxy(this.onKeyup))
+ this.$colorPreview.off('click', this.proxy(this.onColorClick))
+
+ if (this.pickr) {
+ this.pickr.destroyAndRemove()
+ }
+
+ this.$container = null
+ this.$colorPreview = null
+ this.$colorValue = null
+ this.$el = null
+ BaseProto.dispose.call(this)
+ }
+
+ /**
+ * Show picker when focusing on text field for widget.
+ */
+ ColorPicker.prototype.onFocus = function () {
+ this.showPicker()
+ }
+
+ /**
+ * Show picker when the color preview next to the text field is clicked.
+ * @param {Event} event
+ */
+ ColorPicker.prototype.onColorClick = function (event) {
+ if ($(event.currentTarget).is(this.$colorValue) === false) {
+ this.$colorValue.focus()
}
+ this.showPicker()
}
- ColorPicker.prototype.setCustomColor = function(hexColor) {
- if (this.$customColor.length) {
- this.$customColor.data('hexColor', hexColor)
- this.$customColor.spectrum('set', hexColor)
+ /**
+ * Hide picker when the text field loses focus.
+ */
+ ColorPicker.prototype.onBlur = function () {
+ this.hidePicker()
+ }
+
+ /**
+ * Fired when a key is pressed down.
+ *
+ * We use this to disable the Enter key submitting the form by mistake.
+ *
+ * @param {Event} event
+ */
+ ColorPicker.prototype.onKeydown = function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ }
+ }
+
+ /**
+ * Fired when a key is pressed while in the picker, or within the text field of the widget.
+ * @param {Event} event
+ * @returns null
+ */
+ ColorPicker.prototype.onKeyup = function (event) {
+ // Escape always acts as a cancel
+ if (event.key === 'Escape') {
+ this.keyboardEntry = false
+ this.setColor(this.originalColor)
+ this.hidePicker()
+ this.$colorValue.blur()
+ event.stopPropagation()
+ return
+ }
+
+ // Enter will select the current color and hide the picker
+ if (event.key === 'Enter') {
+ this.setColor(this.pickr.getColor())
+ this.hidePicker()
+ this.$colorValue.blur()
+ event.preventDefault()
+ event.stopPropagation()
+ return
+ }
+
+ if (this.pickr.setColor(this.$colorValue.val())) {
+ this.keyboardEntry = true
+ } else {
+ this.keyboardEntry = false
}
+ }
+
+ /*
+ * PICKER METHODS
+ */
+
+ /**
+ * Shows the picker.
+ *
+ * This also prevents mouse clicks within the picker from making the text field lose focus.
+ */
+ ColorPicker.prototype.showPicker = function () {
+ this.keyboardEntry = false
+ this.originalColor = this.pickr.getColor().clone()
+ this.originalFormat = this.getCurrentFormat()
+ this.pickr.show()
- this.setColor(hexColor)
+ $(this.pickr.getRoot().app).on('mousedown.pickr.overrideBlur', function (event) {
+ // Prevent blur event of the text field firing if the mouse click started inside picker,
+ // even if it ends up outside. This prevents the whitespace in the picker firing a blur
+ // event and hiding the picker.
+ event.preventDefault()
+ event.stopPropagation()
+ })
}
- ColorPicker.prototype.setColor = function(hexColor) {
- this.$dataLocker.val(hexColor)
+ /**
+ * Hides the picker.
+ */
+ ColorPicker.prototype.hidePicker = function () {
+ $(this.pickr.getRoot().app).off('mousedown.pickr.overrideBlur')
+ this.pickr.hide()
}
- ColorPicker.prototype.selectColor = function(el) {
- var $item = $(el)
+ /**
+ * Fires when the picker is first initialized for a widget.
+ */
+ ColorPicker.prototype.onPickerInit = function () {
+ this.pickr.setColor(this.$dataLocker.val())
+ this.hidePicker()
- $item
- .addClass('active')
- .siblings().removeClass('active')
+ if (this.options.formats.length === 1) {
+ this.setColorFormat(this.options.formats[0])
+ }
- this.setColor($item.data('hexColor'))
+ this.setColor(this.pickr.getColor())
+ }
- if($item.data('hexColor').length > 0) {
- $item.addClass('sp-clear-display')
+ /**
+ * Updates the selected color when the user stops dragging on the palette.
+ */
+ ColorPicker.prototype.onPickerStopChange = function () {
+ this.setColor(this.pickr.getColor())
+ if (this.options.formats.length === 1) {
+ this.setColorFormat(this.options.formats[0])
}
+ this.onPickerChange(this.pickr.getColor())
+ }
+
+ /**
+ * Fires when the user drags the selector on the color palette, or the hue/opacity sliders.
+ * @param {HSVaColor} hsva
+ */
+ ColorPicker.prototype.onPickerChange = function (hsva) {
+ this.keyboardEntry = false
+ $(this.pickr.getRoot().preview.currentColor).text(this.valueFromHSVA(hsva))
+
+ // If the format changes, change the value
+ if (this.getCurrentFormat() !== this.originalFormat) {
+ this.setColor(hsva)
+ this.originalFormat = this.getCurrentFormat()
+ }
+
+ // Set the color selection text to black or white depending on which color is picked
+ if (this.isLightColor(hsva)) {
+ $(this.pickr.getRoot().preview.currentColor).css({ color: '#000' })
+ } else {
+ $(this.pickr.getRoot().preview.currentColor).css({ color: '#fff' })
+ }
+ }
+
+ /**
+ * Fires when the user picks a swatch color.
+ * @param {HSVaColor} hsva
+ */
+ ColorPicker.prototype.onPickerSwatch = function (hsva) {
+ this.keyboardEntry = false
+ this.setColor(hsva)
+ }
+
+ /**
+ * Fires when the picker is hidden.
+ */
+ ColorPicker.prototype.onPickerHide = function () {
+ if (this.keyboardEntry) {
+ this.setColor(this.pickr.getColor())
+
+ if (this.options.formats.length === 1) {
+ this.setColorFormat(this.options.formats[0])
+ }
+ }
+
+ this.pickr.setColor(this.$dataLocker.val())
+ this.$colorValue.val(this.$dataLocker.val())
+ }
+
+ /**
+ * Gets the necessary color value based on the selected format.
+ *
+ * @param {HSVaColor} hsva
+ * @param {String} overrideFormat Overrides the format specified in the widget config. Can be one of "rgb", "hsl",
+ * "cmyk" and "hex".
+ * @returns {String}
+ */
+ ColorPicker.prototype.valueFromHSVA = function (hsva, overrideFormat) {
+ var format = overrideFormat || this.getCurrentFormat()
+
+ switch (format) {
+ case 'rgb':
+ return hsva.toRGBA().toString(1)
+ case 'hsl':
+ return hsva.toHSLA().toString(1)
+ case 'cmyk':
+ return hsva.toCMYK().toString(1)
+ case 'hex':
+ default:
+ return hsva.toHEXA().toString()
+ }
+ }
+
+ /**
+ * Gets the current color representation from Pickr and translates it to our lowercase color format.
+ *
+ * @returns {String}
+ */
+ ColorPicker.prototype.getCurrentFormat = function () {
+ var currentFormat = this.pickr.getColorRepresentation()
+
+ switch (currentFormat) {
+ case 'RGBA':
+ return 'rgb'
+ case 'HSLA':
+ return 'hsl'
+ case 'CMYK':
+ return 'cmyk'
+ case 'HEXA':
+ default:
+ return 'hex'
+ }
+ }
+
+ /**
+ * Sets the color value for the widget and updates the color preview.
+ *
+ * @param {HSVaColor} hsva
+ */
+ ColorPicker.prototype.setColor = function(hsva) {
+ this.$colorPreview.css('background', this.valueFromHSVA(hsva, 'hex'))
+ this.$colorValue.val(this.valueFromHSVA(hsva))
+ this.$dataLocker.val(this.valueFromHSVA(hsva))
+ }
+
+ /**
+ * Sets the color format used for the value.
+ *
+ * @param {String} format One of "rgb", "hsl", "cmyk", "hex"
+ */
+ ColorPicker.prototype.setColorFormat = function (format) {
+ switch (format) {
+ case 'rgb':
+ this.pickr.setColorRepresentation('RGBA')
+ break
+ case 'hsl':
+ this.pickr.setColorRepresentation('HSLA')
+ break
+ case 'cmyk':
+ this.pickr.setColorRepresentation('CMYK')
+ break
+ case 'hex':
+ default:
+ this.pickr.setColorRepresentation('HEX')
+ break
+ }
+ }
+
+ /**
+ * Determines if the given HSVAColor is a "light" color.
+ *
+ * @param {HSVaColor} hsva
+ * @returns {Boolean}
+ */
+ ColorPicker.prototype.isLightColor = function (hsva) {
+ var rgba = hsva.toRGBA()
+
+ // Borrowed from https://awik.io/determine-color-bright-dark-using-javascript/
+ var ratio = Math.sqrt(
+ 0.299 * (rgba[0] * rgba[0]) +
+ 0.587 * (rgba[1] * rgba[1]) +
+ 0.114 * (rgba[2] * rgba[2])
+ )
+
+ // If alpha drops by 30%, then assume it is a light color
+ if (rgba[3] < 0.7) {
+ return true
+ }
+
+ return (ratio > 127.5)
}
// COLORPICKER PLUGIN DEFINITION
@@ -116,9 +413,9 @@
var args = Array.prototype.slice.call(arguments, 1), result
this.each(function () {
var $this = $(this)
- var data = $this.data('oc.colorpicker')
+ var data = $this.data('wn.colorpicker')
var options = $.extend({}, ColorPicker.DEFAULTS, $this.data(), typeof option == 'object' && option)
- if (!data) $this.data('oc.colorpicker', (data = new ColorPicker(this, options)))
+ if (!data) $this.data('wn.colorpicker', (data = new ColorPicker(this, options)))
if (typeof option == 'string') result = data[option].apply(data, args)
if (typeof result != 'undefined') return false
})
diff --git a/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less b/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less
index a2c59666dd..1b4f7b67ad 100644
--- a/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less
+++ b/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less
@@ -1,249 +1,106 @@
@import "../../../../assets/less/core/boot.less";
-
-@color-colorpicker-border: #cecece;
-@color-colorpicker-active-border: #fff;
-@color-colorpicker-active: #5fb6f5;
-
.field-colorpicker {
- > ul {
- list-style-type: none;
- padding: 0;
- margin: 0;
- margin-top: -8px;
-
- li {
- float: left;
- text-indent: -9999px;
- padding: 3px;
- margin: 14px 14px 0 0;
- cursor: pointer;
- border-radius: @border-radius-base;
-
- span {
- display: block;
- width: 25px;
- height: 25px;
- border: 1px solid @color-colorpicker-border;
- }
-
- &:hover,
- &.active {
- background-color: @color-colorpicker-border;
- span {
- border: 1px solid @color-colorpicker-active-border;
- }
- }
-
- &.custom-color {
- position: relative;
- span {
- border-style: dashed;
- }
- &:hover,
- &.active {
- span {
- border-style: solid;
- }
- }
+ .colorpicker-container {
+ display: flex;
+ flex-direction: row-reverse;
+ position: relative;
+ max-width: 300px;
+ width: 100%;
+ }
- &:before {
- text-indent: 0;
- .icon-FontAutumn();
- content: @caret-up;
- 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;
- }
- }
- }
+ [data-color-preview] {
+ flex-grow: 0;
+ flex-shrink: 0;
+ width: 40px;
+ height: @input-height-base;
+ background: @input-bg;
+ cursor: pointer;
+ border: 1px solid @input-border;
+ .box-shadow(@input-box-shadow);
+ border-top-left-radius: @input-border-radius;
+ border-bottom-left-radius: @input-border-radius;
+ }
+ [data-color-value] {
+ flex: 1;
+ border-left: none;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
}
- &.disabled {
- > ul {
- li {
- &:hover {
- cursor: not-allowed;
- background-color: transparent;
+ // PICKR OVERRIDES
+ .pcr-app[data-theme="nano"] {
+
+ width: 18em;
+
+ .pcr-selection {
+
+ // Add an additional row to the grid for color selection and preview
+ grid-template-rows: 1fr 5fr auto auto;
+ gap: 0;
+
+ .pcr-color-preview {
+ grid-row-start: 1;
+ grid-column-start: 1;
+ grid-row-end: 1;
+ grid-column-end: 3;
+ margin: 0;
+ flex-direction: row-reverse;
+
+ .pcr-current-color {
+ flex-grow: 0;
+ flex-shrink: 0;
+ width: 80%;
+ border-radius: 0;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.5);
+ font-family: monospace;
+ height: 28px;
+ line-height: 29px;
+ text-align: center;
+ font-size: 0.85em;
- span {
- border: 1px solid @color-colorpicker-border;
+ &::before {
+ content: none;
}
}
- &.active {
- background-color: transparent;
- }
-
- &.custom-color {
- position: relative;
-
- span {
- border-style: dashed;
-
- &.disabled.is-empty {
- &:before, &:after {
- position: absolute;
- left: 15px;
- content: ' ';
- height: 24px;
- width: 1px;
- background-color: #cecece;
- }
- &:before {
- transform: rotate(45deg);
- }
- &:after {
- transform: rotate(-45deg);
- }
- }
- }
-
- &:hover {
- span {
- border-style: dashed;
- }
- }
- &.active {
- span {
- border: 1px solid @color-colorpicker-border;
- }
- }
+ .pcr-last-color {
+ display: block;
+ flex-grow: 0;
+ flex-shrink: 0;
+ width: 20%;
+ border-radius: 0;
+ background: var(--pcr-color);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.5);
}
}
- }
- }
-
-}
-//
-// Spectrum
-//
-
-.sp-hue, .sp-slider { cursor: row-resize }
-.sp-color, .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: @border-radius-base;
- z-index: @zindex-datepicker;
- .box-shadow(@overlay-box-shadow);
- font-family: @font-family-base;
-}
-
-.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 @input-border;
- .box-shadow(@input-box-shadow);
- .border-radius(@input-border-radius);
-
- &:focus {
- border: 1px solid @color-form-field-border-focus;
- }
-}
+ .pcr-color-palette {
+ grid-row-start: 2;
+ grid-column-start: 1;
+ grid-row-end: 2;
+ grid-column-end: 3;
+ }
-.sp-button-container {
- width: 100%;
- position: relative;
- text-align: right;
- padding-top: 4px;
-}
+ .pcr-color-chooser {
+ margin-top: 0.8rem;
+ grid-row-start: 3;
+ grid-column-start: 1;
+ grid-row-end: 3;
+ grid-column-end: 3;
+ }
-.sp-container button,
-.sp-container button:hover,
-.sp-container button:active {
- text-shadow: none;
- font-size: @font-size-base - 1;
- text-align: left;
- outline: none !important;
- font-weight: @btn-font-weight;
- .box-shadow(~"inset 0 -2px 0 rgba(0,0,0,.15)");
- border: 1px solid transparent;
- .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border; @btn-primary-bg; @btn-primary-bg);
- .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small);
-}
+ .pcr-color-opacity {
+ margin-top: 0.8em;
+ grid-row-start: 4;
+ grid-column-start: 1;
+ grid-row-end: 4;
+ grid-column-end: 3;
+ }
-.sp-cancel {
- bottom: -3px;
- left: 0;
- position: absolute;
- text-decoration: none;
-
- font-family: serif;
- font-size: (@font-size-base * 1.5);
- font-weight: @close-font-weight;
- line-height: 1;
- color: @close-color !important;
- text-shadow: @close-text-shadow;
- .opacity(.2);
-
- &:hover,
- &:focus {
- text-decoration: none;
- cursor: pointer;
- .opacity(.5);
+ }
}
-}
-
-.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,
-.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/pickr/LICENSE b/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/LICENSE
new file mode 100644
index 0000000000..0f97438a95
--- /dev/null
+++ b/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 - 2021 Simon Reinisch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/pickr-nano.min.css b/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/pickr-nano.min.css
new file mode 100644
index 0000000000..9df04c30cc
--- /dev/null
+++ b/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/pickr-nano.min.css
@@ -0,0 +1,2 @@
+/*! Pickr 1.8.2 MIT | https://github.com/Simonwep/pickr */
+.pickr{position:relative;overflow:visible;transform:translateY(0)}.pickr *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr .pcr-button{position:relative;height:2em;width:2em;padding:0.5em;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;border-radius:.15em;background:url('data:image/svg+xml;utf8, ') no-repeat center;background-size:0;transition:all 0.3s}.pickr .pcr-button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pickr .pcr-button::before{z-index:initial}.pickr .pcr-button::after{position:absolute;content:'';top:0;left:0;height:100%;width:100%;transition:background 0.3s;background:var(--pcr-color);border-radius:.15em}.pickr .pcr-button.clear{background-size:70%}.pickr .pcr-button.clear::before{opacity:0}.pickr .pcr-button.clear:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-button.disabled{cursor:not-allowed}.pickr *,.pcr-app *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr input:focus,.pickr input.pcr-active,.pickr button:focus,.pickr button.pcr-active,.pcr-app input:focus,.pcr-app input.pcr-active,.pcr-app button:focus,.pcr-app button.pcr-active{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-palette,.pickr .pcr-slider,.pcr-app .pcr-palette,.pcr-app .pcr-slider{transition:box-shadow 0.3s}.pickr .pcr-palette:focus,.pickr .pcr-slider:focus,.pcr-app .pcr-palette:focus,.pcr-app .pcr-slider:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(0,0,0,0.25)}.pcr-app{position:fixed;display:flex;flex-direction:column;z-index:10000;border-radius:0.1em;background:#fff;opacity:0;visibility:hidden;transition:opacity 0.3s, visibility 0s 0.3s;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;box-shadow:0 0.15em 1.5em 0 rgba(0,0,0,0.1),0 0 1em 0 rgba(0,0,0,0.03);left:0;top:0}.pcr-app.visible{transition:opacity 0.3s;visibility:visible;opacity:1}.pcr-app .pcr-swatches{display:flex;flex-wrap:wrap;margin-top:0.75em}.pcr-app .pcr-swatches.pcr-last{margin:0}@supports (display: grid){.pcr-app .pcr-swatches{display:grid;align-items:center;grid-template-columns:repeat(auto-fit, 1.75em)}}.pcr-app .pcr-swatches>button{font-size:1em;position:relative;width:calc(1.75em - 5px);height:calc(1.75em - 5px);border-radius:0.15em;cursor:pointer;margin:2.5px;flex-shrink:0;justify-self:center;transition:all 0.15s;overflow:hidden;background:transparent;z-index:1}.pcr-app .pcr-swatches>button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:6px;border-radius:.15em;z-index:-1}.pcr-app .pcr-swatches>button::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:var(--pcr-color);border:1px solid rgba(0,0,0,0.05);border-radius:0.15em;box-sizing:border-box}.pcr-app .pcr-swatches>button:hover{filter:brightness(1.05)}.pcr-app .pcr-swatches>button:not(.pcr-active){box-shadow:none}.pcr-app .pcr-interaction{display:flex;flex-wrap:wrap;align-items:center;margin:0 -0.2em 0 -0.2em}.pcr-app .pcr-interaction>*{margin:0 0.2em}.pcr-app .pcr-interaction input{letter-spacing:0.07em;font-size:0.75em;text-align:center;cursor:pointer;color:#75797e;background:#f1f3f4;border-radius:.15em;transition:all 0.15s;padding:0.45em 0.5em;margin-top:0.75em}.pcr-app .pcr-interaction input:hover{filter:brightness(0.975)}.pcr-app .pcr-interaction input:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(66,133,244,0.75)}.pcr-app .pcr-interaction .pcr-result{color:#75797e;text-align:left;flex:1 1 8em;min-width:8em;transition:all 0.2s;border-radius:.15em;background:#f1f3f4;cursor:text}.pcr-app .pcr-interaction .pcr-result::-moz-selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-result::selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-type.active{color:#fff;background:#4285f4}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff;width:auto}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff}.pcr-app .pcr-interaction .pcr-save:hover,.pcr-app .pcr-interaction .pcr-cancel:hover,.pcr-app .pcr-interaction .pcr-clear:hover{filter:brightness(0.925)}.pcr-app .pcr-interaction .pcr-save{background:#4285f4}.pcr-app .pcr-interaction .pcr-clear,.pcr-app .pcr-interaction .pcr-cancel{background:#f44250}.pcr-app .pcr-interaction .pcr-clear:focus,.pcr-app .pcr-interaction .pcr-cancel:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(244,66,80,0.75)}.pcr-app .pcr-selection .pcr-picker{position:absolute;height:18px;width:18px;border:2px solid #fff;border-radius:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pcr-app .pcr-selection .pcr-color-palette,.pcr-app .pcr-selection .pcr-color-chooser,.pcr-app .pcr-selection .pcr-color-opacity{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;flex-direction:column;cursor:grab;cursor:-webkit-grab}.pcr-app .pcr-selection .pcr-color-palette:active,.pcr-app .pcr-selection .pcr-color-chooser:active,.pcr-app .pcr-selection .pcr-color-opacity:active{cursor:grabbing;cursor:-webkit-grabbing}.pcr-app[data-theme='nano']{width:14.25em;max-width:95vw}.pcr-app[data-theme='nano'] .pcr-swatches{margin-top:.6em;padding:0 .6em}.pcr-app[data-theme='nano'] .pcr-interaction{padding:0 .6em .6em .6em}.pcr-app[data-theme='nano'] .pcr-selection{display:grid;grid-gap:.6em;grid-template-columns:1fr 4fr;grid-template-rows:5fr auto auto;align-items:center;height:10.5em;width:100%;align-self:flex-start}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview{grid-area:2 / 1 / 4 / 1;height:100%;width:100%;display:flex;flex-direction:row;justify-content:center;margin-left:.6em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-last-color{display:none}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-current-color{position:relative;background:var(--pcr-color);width:2em;height:2em;border-radius:50em;overflow:hidden}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-current-color::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette{grid-area:1 / 1 / 2 / 3;width:100%;height:100%;z-index:1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette .pcr-palette{border-radius:.15em;width:100%;height:100%}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette .pcr-palette::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser{grid-area:2 / 2 / 2 / 2}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity{grid-area:3 / 2 / 3 / 2}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity{height:0.5em;margin:0 .6em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-picker,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-picker{top:50%;transform:translateY(-50%)}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-slider,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-slider{flex-grow:1;border-radius:50em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-slider{background:linear-gradient(to right, red, #ff0, lime, cyan, blue, #f0f, red)}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-slider{background:linear-gradient(to right, transparent, black),url('data:image/svg+xml;utf8, ');background-size:100%, 0.25em}
diff --git a/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/pickr.min.js b/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/pickr.min.js
new file mode 100644
index 0000000000..5270a1d1a2
--- /dev/null
+++ b/modules/backend/formwidgets/colorpicker/assets/vendor/pickr/pickr.min.js
@@ -0,0 +1,3 @@
+/*! Pickr 1.8.2 MIT | https://github.com/Simonwep/pickr */
+!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Pickr=e():t.Pickr=e()}(self,(function(){return(()=>{"use strict";var t={d:(e,o)=>{for(var n in o)t.o(o,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:o[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.d(e,{default:()=>L});var o={};function n(t,e,o,n,i={}){e instanceof HTMLCollection||e instanceof NodeList?e=Array.from(e):Array.isArray(e)||(e=[e]),Array.isArray(o)||(o=[o]);for(const s of e)for(const e of o)s[t](e,n,{capture:!1,...i});return Array.prototype.slice.call(arguments,1)}t.r(o),t.d(o,{adjustableInputNumbers:()=>p,createElementFromString:()=>r,createFromTemplate:()=>a,eventPath:()=>l,off:()=>s,on:()=>i,resolveElement:()=>c});const i=n.bind(null,"addEventListener"),s=n.bind(null,"removeEventListener");function r(t){const e=document.createElement("div");return e.innerHTML=t.trim(),e.firstElementChild}function a(t){const e=(t,e)=>{const o=t.getAttribute(e);return t.removeAttribute(e),o},o=(t,n={})=>{const i=e(t,":obj"),s=e(t,":ref"),r=i?n[i]={}:n;s&&(n[s]=t);for(const n of Array.from(t.children)){const t=e(n,":arr"),i=o(n,t?{}:r);t&&(r[t]||(r[t]=[])).push(Object.keys(i).length?i:n)}return n};return o(r(t))}function l(t){let e=t.path||t.composedPath&&t.composedPath();if(e)return e;let o=t.target.parentElement;for(e=[t.target,o];o=o.parentElement;)e.push(o);return e.push(document,window),e}function c(t){return t instanceof Element?t:"string"==typeof t?t.split(/>>/g).reduce(((t,e,o,n)=>(t=t.querySelector(e),ot)){function o(o){const n=[.001,.01,.1][Number(o.shiftKey||2*o.ctrlKey)]*(o.deltaY<0?1:-1);let i=0,s=t.selectionStart;t.value=t.value.replace(/[\d.]+/g,((t,o)=>o<=s&&o+t.length>=s?(s=o,e(Number(t),n,i)):(i++,t))),t.focus(),t.setSelectionRange(s,s),o.preventDefault(),t.dispatchEvent(new Event("input"))}i(t,"focus",(()=>i(window,"wheel",o,{passive:!1}))),i(t,"blur",(()=>s(window,"wheel",o)))}const{min:u,max:h,floor:d,round:m}=Math;function f(t,e,o){e/=100,o/=100;const n=d(t=t/360*6),i=t-n,s=o*(1-e),r=o*(1-i*e),a=o*(1-(1-i)*e),l=n%6;return[255*[o,r,s,s,a,o][l],255*[a,o,o,r,s,s][l],255*[s,s,a,o,o,r][l]]}function v(t,e,o){const n=(2-(e/=100))*(o/=100)/2;return 0!==n&&(e=1===n?0:n<.5?e*o/(2*n):e*o/(2-2*n)),[t,100*e,100*n]}function b(t,e,o){const n=u(t/=255,e/=255,o/=255),i=h(t,e,o),s=i-n;let r,a;if(0===s)r=a=0;else{a=s/i;const n=((i-t)/6+s/2)/s,l=((i-e)/6+s/2)/s,c=((i-o)/6+s/2)/s;t===i?r=c-l:e===i?r=1/3+n-c:o===i&&(r=2/3+l-n),r<0?r+=1:r>1&&(r-=1)}return[360*r,100*a,100*i]}function y(t,e,o,n){e/=100,o/=100;return[...b(255*(1-u(1,(t/=100)*(1-(n/=100))+n)),255*(1-u(1,e*(1-n)+n)),255*(1-u(1,o*(1-n)+n)))]}function g(t,e,o){e/=100;const n=2*(e*=(o/=100)<.5?o:1-o)/(o+e)*100,i=100*(o+e);return[t,isNaN(n)?0:n,i]}function _(t){return b(...t.match(/.{2}/g).map((t=>parseInt(t,16))))}function w(t){t=t.match(/^[a-zA-Z]+$/)?function(t){if("black"===t.toLowerCase())return"#000";const e=document.createElement("canvas").getContext("2d");return e.fillStyle=t,"#000"===e.fillStyle?null:e.fillStyle}(t):t;const e={cmyk:/^cmyk[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)/i,rgba:/^((rgba)|rgb)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hsla:/^((hsla)|hsl)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hsva:/^((hsva)|hsv)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hexa:/^#?(([\dA-Fa-f]{3,4})|([\dA-Fa-f]{6})|([\dA-Fa-f]{8}))$/i},o=t=>t.map((t=>/^(|\d+)\.\d+|\d+$/.test(t)?Number(t):void 0));let n;t:for(const i in e){if(!(n=e[i].exec(t)))continue;const s=t=>!!n[2]==("number"==typeof t);switch(i){case"cmyk":{const[,t,e,s,r]=o(n);if(t>100||e>100||s>100||r>100)break t;return{values:y(t,e,s,r),type:i}}case"rgba":{const[,,,t,e,r,a]=o(n);if(t>255||e>255||r>255||a<0||a>1||!s(a))break t;return{values:[...b(t,e,r),a],a,type:i}}case"hexa":{let[,t]=n;4!==t.length&&3!==t.length||(t=t.split("").map((t=>t+t)).join(""));const e=t.substring(0,6);let o=t.substring(6);return o=o?parseInt(o,16)/255:void 0,{values:[..._(e),o],a:o,type:i}}case"hsla":{const[,,,t,e,r,a]=o(n);if(t>360||e>100||r>100||a<0||a>1||!s(a))break t;return{values:[...g(t,e,r),a],a,type:i}}case"hsva":{const[,,,t,e,r,a]=o(n);if(t>360||e>100||r>100||a<0||a>1||!s(a))break t;return{values:[t,e,r,a],a,type:i}}}}return{values:null,type:null}}function A(t=0,e=0,o=0,n=1){const i=(t,e)=>(o=-1)=>e(~o?t.map((t=>Number(t.toFixed(o)))):t),s={h:t,s:e,v:o,a:n,toHSVA(){const t=[s.h,s.s,s.v,s.a];return t.toString=i(t,(t=>`hsva(${t[0]}, ${t[1]}%, ${t[2]}%, ${s.a})`)),t},toHSLA(){const t=[...v(s.h,s.s,s.v),s.a];return t.toString=i(t,(t=>`hsla(${t[0]}, ${t[1]}%, ${t[2]}%, ${s.a})`)),t},toRGBA(){const t=[...f(s.h,s.s,s.v),s.a];return t.toString=i(t,(t=>`rgba(${t[0]}, ${t[1]}, ${t[2]}, ${s.a})`)),t},toCMYK(){const t=function(t,e,o){const n=f(t,e,o),i=n[0]/255,s=n[1]/255,r=n[2]/255,a=u(1-i,1-s,1-r);return[100*(1===a?0:(1-i-a)/(1-a)),100*(1===a?0:(1-s-a)/(1-a)),100*(1===a?0:(1-r-a)/(1-a)),100*a]}(s.h,s.s,s.v);return t.toString=i(t,(t=>`cmyk(${t[0]}%, ${t[1]}%, ${t[2]}%, ${t[3]}%)`)),t},toHEXA(){const t=function(t,e,o){return f(t,e,o).map((t=>m(t).toString(16).padStart(2,"0")))}(s.h,s.s,s.v),e=s.a>=1?"":Number((255*s.a).toFixed(0)).toString(16).toUpperCase().padStart(2,"0");return e&&t.push(e),t.toString=()=>`#${t.join("").toUpperCase()}`,t},clone:()=>A(s.h,s.s,s.v,s.a)};return s}const C=t=>Math.max(Math.min(t,1),0);function $(t){const e={options:Object.assign({lock:null,onchange:()=>0,onstop:()=>0},t),_keyboard(t){const{options:o}=e,{type:n,key:i}=t;if(document.activeElement===o.wrapper){const{lock:o}=e.options,s="ArrowUp"===i,r="ArrowRight"===i,a="ArrowDown"===i,l="ArrowLeft"===i;if("keydown"===n&&(s||r||a||l)){let n=0,i=0;"v"===o?n=s||r?1:-1:"h"===o?n=s||r?-1:1:(i=s?-1:a?1:0,n=l?-1:r?1:0),e.update(C(e.cache.x+.01*n),C(e.cache.y+.01*i)),t.preventDefault()}else i.startsWith("Arrow")&&(e.options.onstop(),t.preventDefault())}},_tapstart(t){i(document,["mouseup","touchend","touchcancel"],e._tapstop),i(document,["mousemove","touchmove"],e._tapmove),t.cancelable&&t.preventDefault(),e._tapmove(t)},_tapmove(t){const{options:o,cache:n}=e,{lock:i,element:s,wrapper:r}=o,a=r.getBoundingClientRect();let l=0,c=0;if(t){const e=t&&t.touches&&t.touches[0];l=t?(e||t).clientX:0,c=t?(e||t).clientY:0,la.left+a.width&&(l=a.left+a.width),ca.top+a.height&&(c=a.top+a.height),l-=a.left,c-=a.top}else n&&(l=n.x*a.width,c=n.y*a.height);"h"!==i&&(s.style.left=`calc(${l/a.width*100}% - ${s.offsetWidth/2}px)`),"v"!==i&&(s.style.top=`calc(${c/a.height*100}% - ${s.offsetHeight/2}px)`),e.cache={x:l/a.width,y:c/a.height};const p=C(l/a.width),u=C(c/a.height);switch(i){case"v":return o.onchange(p);case"h":return o.onchange(u);default:return o.onchange(p,u)}},_tapstop(){e.options.onstop(),s(document,["mouseup","touchend","touchcancel"],e._tapstop),s(document,["mousemove","touchmove"],e._tapmove)},trigger(){e._tapmove()},update(t=0,o=0){const{left:n,top:i,width:s,height:r}=e.options.wrapper.getBoundingClientRect();"h"===e.options.lock&&(o=t),e._tapmove({clientX:n+s*t,clientY:i+r*o})},destroy(){const{options:t,_tapstart:o,_keyboard:n}=e;s(document,["keydown","keyup"],n),s([t.wrapper,t.element],"mousedown",o),s([t.wrapper,t.element],"touchstart",o,{passive:!1})}},{options:o,_tapstart:n,_keyboard:r}=e;return i([o.wrapper,o.element],"mousedown",n),i([o.wrapper,o.element],"touchstart",n,{passive:!1}),i(document,["keydown","keyup"],r),e}function k(t={}){t=Object.assign({onchange:()=>0,className:"",elements:[]},t);const e=i(t.elements,"click",(e=>{t.elements.forEach((o=>o.classList[e.target===o?"add":"remove"](t.className))),t.onchange(e),e.stopPropagation()}));return{destroy:()=>s(...e)}}const S={variantFlipOrder:{start:"sme",middle:"mse",end:"ems"},positionFlipOrder:{top:"tbrl",right:"rltb",bottom:"btrl",left:"lrbt"},position:"bottom",margin:8},O=(t,e,o)=>{const{container:n,margin:i,position:s,variantFlipOrder:r,positionFlipOrder:a}={container:document.documentElement.getBoundingClientRect(),...S,...o},{left:l,top:c}=e.style;e.style.left="0",e.style.top="0";const p=t.getBoundingClientRect(),u=e.getBoundingClientRect(),h={t:p.top-u.height-i,b:p.bottom+i,r:p.right+i,l:p.left-u.width-i},d={vs:p.left,vm:p.left+p.width/2+-u.width/2,ve:p.left+p.width-u.width,hs:p.top,hm:p.bottom-p.height/2-u.height/2,he:p.bottom-u.height},[m,f="middle"]=s.split("-"),v=a[m],b=r[f],{top:y,left:g,bottom:_,right:w}=n;for(const t of v){const o="t"===t||"b"===t,n=h[t],[i,s]=o?["top","left"]:["left","top"],[r,a]=o?[u.height,u.width]:[u.width,u.height],[l,c]=o?[_,w]:[w,_],[p,m]=o?[y,g]:[g,y];if(!(n
l))for(const r of b){const l=d[(o?"v":"h")+r];if(!(lc))return e.style[s]=l-u[s]+"px",e.style[i]=n-u[i]+"px",t+r}}return e.style.left=l,e.style.top=c,null};function E(t,e,o){return e in t?Object.defineProperty(t,e,{value:o,enumerable:!0,configurable:!0,writable:!0}):t[e]=o,t}class L{constructor(t){E(this,"_initializingActive",!0),E(this,"_recalc",!0),E(this,"_nanopop",null),E(this,"_root",null),E(this,"_color",A()),E(this,"_lastColor",A()),E(this,"_swatchColors",[]),E(this,"_setupAnimationFrame",null),E(this,"_eventListener",{init:[],save:[],hide:[],show:[],clear:[],change:[],changestop:[],cancel:[],swatchselect:[]}),this.options=t=Object.assign({...L.DEFAULT_OPTIONS},t);const{swatches:e,components:o,theme:n,sliders:i,lockOpacity:s,padding:r}=t;["nano","monolith"].includes(n)&&!i&&(t.sliders="h"),o.interaction||(o.interaction={});const{preview:a,opacity:l,hue:c,palette:p}=o;o.opacity=!s&&l,o.palette=p||a||l||c,this._preBuild(),this._buildComponents(),this._bindEvents(),this._finalBuild(),e&&e.length&&e.forEach((t=>this.addSwatch(t)));const{button:u,app:h}=this._root;this._nanopop=((t,e,o)=>{const n="object"!=typeof t||t instanceof HTMLElement?{reference:t,popper:e,...o}:t;return{update(t=n){const{reference:e,popper:o}=Object.assign(n,t);if(!o||!e)throw new Error("Popper- or reference-element missing.");return O(e,o,n)}}})(u,h,{margin:r}),u.setAttribute("role","button"),u.setAttribute("aria-label",this._t("btn:toggle"));const d=this;this._setupAnimationFrame=requestAnimationFrame((function e(){if(!h.offsetWidth)return requestAnimationFrame(e);d.setColor(t.default),d._rePositioningPicker(),t.defaultRepresentation&&(d._representation=t.defaultRepresentation,d.setColorRepresentation(d._representation)),t.showAlways&&d.show(),d._initializingActive=!1,d._emit("init")}))}_preBuild(){const{options:t}=this;for(const e of["el","container"])t[e]=c(t[e]);this._root=(t=>{const{components:e,useAsButton:o,inline:n,appClass:i,theme:s,lockOpacity:r}=t.options,l=t=>t?"":'style="display:none" hidden',c=e=>t._t(e),p=a(`\n
\n\n ${o?"":''}\n\n
\n
\n
\n \n \n
\n\n
\n \n \n
\n\n
\n \n \n
\n\n
\n \n \n
\n
\n\n \n\n
\n \n\n \n \n \n \n \n\n \n \n \n
\n
\n
\n `),u=p.interaction;return u.options.find((t=>!t.hidden&&!t.classList.add("active"))),u.type=()=>u.options.find((t=>t.classList.contains("active"))),p})(this),t.useAsButton&&(this._root.button=t.el),t.container.appendChild(this._root.root)}_finalBuild(){const t=this.options,e=this._root;if(t.container.removeChild(e.root),t.inline){const o=t.el.parentElement;t.el.nextSibling?o.insertBefore(e.app,t.el.nextSibling):o.appendChild(e.app)}else t.container.appendChild(e.app);t.useAsButton?t.inline&&t.el.remove():t.el.parentNode.replaceChild(e.root,t.el),t.disabled&&this.disable(),t.comparison||(e.button.style.transition="none",t.useAsButton||(e.preview.lastColor.style.transition="none")),this.hide()}_buildComponents(){const t=this,e=this.options.components,o=(t.options.sliders||"v").repeat(2),[n,i]=o.match(/^[vh]+$/g)?o:[],s=()=>this._color||(this._color=this._lastColor.clone()),r={palette:$({element:t._root.palette.picker,wrapper:t._root.palette.palette,onstop:()=>t._emit("changestop","slider",t),onchange(o,n){if(!e.palette)return;const i=s(),{_root:r,options:a}=t,{lastColor:l,currentColor:c}=r.preview;t._recalc&&(i.s=100*o,i.v=100-100*n,i.v<0&&(i.v=0),t._updateOutput("slider"));const p=i.toRGBA().toString(0);this.element.style.background=p,this.wrapper.style.background=`\n linear-gradient(to top, rgba(0, 0, 0, ${i.a}), transparent),\n linear-gradient(to left, hsla(${i.h}, 100%, 50%, ${i.a}), rgba(255, 255, 255, ${i.a}))\n `,a.comparison?a.useAsButton||t._lastColor||l.style.setProperty("--pcr-color",p):(r.button.style.setProperty("--pcr-color",p),r.button.classList.remove("clear"));const u=i.toHEXA().toString();for(const{el:e,color:o}of t._swatchColors)e.classList[u===o.toHEXA().toString()?"add":"remove"]("pcr-active");c.style.setProperty("--pcr-color",p)}}),hue:$({lock:"v"===i?"h":"v",element:t._root.hue.picker,wrapper:t._root.hue.slider,onstop:()=>t._emit("changestop","slider",t),onchange(o){if(!e.hue||!e.palette)return;const n=s();t._recalc&&(n.h=360*o),this.element.style.backgroundColor=`hsl(${n.h}, 100%, 50%)`,r.palette.trigger()}}),opacity:$({lock:"v"===n?"h":"v",element:t._root.opacity.picker,wrapper:t._root.opacity.slider,onstop:()=>t._emit("changestop","slider",t),onchange(o){if(!e.opacity||!e.palette)return;const n=s();t._recalc&&(n.a=Math.round(100*o)/100),this.element.style.background=`rgba(0, 0, 0, ${n.a})`,r.palette.trigger()}}),selectable:k({elements:t._root.interaction.options,className:"active",onchange(e){t._representation=e.target.getAttribute("data-type").toUpperCase(),t._recalc&&t._updateOutput("swatch")}})};this._components=r}_bindEvents(){const{_root:t,options:e}=this,o=[i(t.interaction.clear,"click",(()=>this._clearColor())),i([t.interaction.cancel,t.preview.lastColor],"click",(()=>{this.setHSVA(...(this._lastColor||this._color).toHSVA(),!0),this._emit("cancel")})),i(t.interaction.save,"click",(()=>{!this.applyColor()&&!e.showAlways&&this.hide()})),i(t.interaction.result,["keyup","input"],(t=>{this.setColor(t.target.value,!0)&&!this._initializingActive&&(this._emit("change",this._color,"input",this),this._emit("changestop","input",this)),t.stopImmediatePropagation()})),i(t.interaction.result,["focus","blur"],(t=>{this._recalc="blur"===t.type,this._recalc&&this._updateOutput(null)})),i([t.palette.palette,t.palette.picker,t.hue.slider,t.hue.picker,t.opacity.slider,t.opacity.picker],["mousedown","touchstart"],(()=>this._recalc=!0),{passive:!0})];if(!e.showAlways){const n=e.closeWithKey;o.push(i(t.button,"click",(()=>this.isOpen()?this.hide():this.show())),i(document,"keyup",(t=>this.isOpen()&&(t.key===n||t.code===n)&&this.hide())),i(document,["touchstart","mousedown"],(e=>{this.isOpen()&&!l(e).some((e=>e===t.app||e===t.button))&&this.hide()}),{capture:!0}))}if(e.adjustableNumbers){const e={rgba:[255,255,255,1],hsva:[360,100,100,1],hsla:[360,100,100,1],cmyk:[100,100,100,100]};p(t.interaction.result,((t,o,n)=>{const i=e[this.getColorRepresentation().toLowerCase()];if(i){const e=i[n],s=t+(e>=100?1e3*o:o);return s<=0?0:Number((s{n.isOpen()&&(e.closeOnScroll&&n.hide(),null===t?(t=setTimeout((()=>t=null),100),requestAnimationFrame((function e(){n._rePositioningPicker(),null!==t&&requestAnimationFrame(e)}))):(clearTimeout(t),t=setTimeout((()=>t=null),100)))}),{capture:!0}))}this._eventBindings=o}_rePositioningPicker(){const{options:t}=this;if(!t.inline){if(!this._nanopop.update({container:document.body.getBoundingClientRect(),position:t.position})){const t=this._root.app,e=t.getBoundingClientRect();t.style.top=(window.innerHeight-e.height)/2+"px",t.style.left=(window.innerWidth-e.width)/2+"px"}}}_updateOutput(t){const{_root:e,_color:o,options:n}=this;if(e.interaction.type()){const t=`to${e.interaction.type().getAttribute("data-type")}`;e.interaction.result.value="function"==typeof o[t]?o[t]().toString(n.outputPrecision):""}!this._initializingActive&&this._recalc&&this._emit("change",o,t,this)}_clearColor(t=!1){const{_root:e,options:o}=this;o.useAsButton||e.button.style.setProperty("--pcr-color","rgba(0, 0, 0, 0.15)"),e.button.classList.add("clear"),o.showAlways||this.hide(),this._lastColor=null,this._initializingActive||t||(this._emit("save",null),this._emit("clear"))}_parseLocalColor(t){const{values:e,type:o,a:n}=w(t),{lockOpacity:i}=this.options,s=void 0!==n&&1!==n;return e&&3===e.length&&(e[3]=void 0),{values:!e||i&&s?null:e,type:o}}_t(t){return this.options.i18n[t]||L.I18N_DEFAULTS[t]}_emit(t,...e){this._eventListener[t].forEach((t=>t(...e,this)))}on(t,e){return this._eventListener[t].push(e),this}off(t,e){const o=this._eventListener[t]||[],n=o.indexOf(e);return~n&&o.splice(n,1),this}addSwatch(t){const{values:e}=this._parseLocalColor(t);if(e){const{_swatchColors:t,_root:o}=this,n=A(...e),s=r(``);return o.swatches.appendChild(s),t.push({el:s,color:n}),this._eventBindings.push(i(s,"click",(()=>{this.setHSVA(...n.toHSVA(),!0),this._emit("swatchselect",n),this._emit("change",n,"swatch",this)}))),!0}return!1}removeSwatch(t){const e=this._swatchColors[t];if(e){const{el:o}=e;return this._root.swatches.removeChild(o),this._swatchColors.splice(t,1),!0}return!1}applyColor(t=!1){const{preview:e,button:o}=this._root,n=this._color.toRGBA().toString(0);return e.lastColor.style.setProperty("--pcr-color",n),this.options.useAsButton||o.style.setProperty("--pcr-color",n),o.classList.remove("clear"),this._lastColor=this._color.clone(),this._initializingActive||t||this._emit("save",this._color),this}destroy(){cancelAnimationFrame(this._setupAnimationFrame),this._eventBindings.forEach((t=>s(...t))),Object.keys(this._components).forEach((t=>this._components[t].destroy()))}destroyAndRemove(){this.destroy();const{root:t,app:e}=this._root;t.parentElement&&t.parentElement.removeChild(t),e.parentElement.removeChild(e),Object.keys(this).forEach((t=>this[t]=null))}hide(){return!!this.isOpen()&&(this._root.app.classList.remove("visible"),this._emit("hide"),!0)}show(){return!this.options.disabled&&!this.isOpen()&&(this._root.app.classList.add("visible"),this._rePositioningPicker(),this._emit("show",this._color),this)}isOpen(){return this._root.app.classList.contains("visible")}setHSVA(t=360,e=0,o=0,n=1,i=!1){const s=this._recalc;if(this._recalc=!1,t<0||t>360||e<0||e>100||o<0||o>100||n<0||n>1)return!1;this._color=A(t,e,o,n);const{hue:r,opacity:a,palette:l}=this._components;return r.update(t/360),a.update(n),l.update(e/100,1-o/100),i||this.applyColor(),s&&this._updateOutput(),this._recalc=s,!0}setColor(t,e=!1){if(null===t)return this._clearColor(e),!0;const{values:o,type:n}=this._parseLocalColor(t);if(o){const t=n.toUpperCase(),{options:i}=this._root.interaction,s=i.find((e=>e.getAttribute("data-type")===t));if(s&&!s.hidden)for(const t of i)t.classList[t===s?"add":"remove"]("active");return!!this.setHSVA(...o,e)&&this.setColorRepresentation(t)}return!1}setColorRepresentation(t){return t=t.toUpperCase(),!!this._root.interaction.options.find((e=>e.getAttribute("data-type").startsWith(t)&&!e.click()))}getColorRepresentation(){return this._representation}getColor(){return this._color}getSelectedColor(){return this._lastColor}getRoot(){return this._root}disable(){return this.hide(),this.options.disabled=!0,this._root.button.classList.add("disabled"),this}enable(){return this.options.disabled=!1,this._root.button.classList.remove("disabled"),this}}return E(L,"utils",o),E(L,"version","1.8.2"),E(L,"I18N_DEFAULTS",{"ui:dialog":"color picker dialog","btn:toggle":"toggle color picker dialog","btn:swatch":"color swatch","btn:last-color":"use previous color","btn:save":"Save","btn:cancel":"Cancel","btn:clear":"Clear","aria:btn:save":"save and close","aria:btn:cancel":"cancel and close","aria:btn:clear":"clear and close","aria:input":"color input field","aria:palette":"color selection area","aria:hue":"hue selection slider","aria:opacity":"selection slider"}),E(L,"DEFAULT_OPTIONS",{appClass:null,theme:"classic",useAsButton:!1,padding:8,disabled:!1,comparison:!0,closeOnScroll:!1,outputPrecision:0,lockOpacity:!1,autoReposition:!0,container:"body",components:{interaction:{}},i18n:{},swatches:null,inline:!1,sliders:null,default:"#42445a",defaultRepresentation:null,position:"bottom-middle",adjustableNumbers:!0,showAlways:!1,closeWithKey:"Escape"}),E(L,"create",(t=>new L(t))),e=e.default})()}));
+//# sourceMappingURL=pickr.min.js.map
diff --git a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/LICENSE b/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/LICENSE
deleted file mode 100644
index 212de3c72e..0000000000
--- a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/README.md b/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/README.md
deleted file mode 100644
index 0de67ecb11..0000000000
--- a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/README.md
+++ /dev/null
@@ -1,69 +0,0 @@
-# Spectrum
-## The No Hassle Colorpicker
-
-See the demo and docs: http://bgrins.github.io/spectrum.
-
-I wanted a colorpicker that didn't require images, and that had an API that made sense to me as a developer who has worked with color in a number of applications. I had tried a number of existing plugins, but decided to try and make a smaller, simpler one.
-
-I started using canvas, then switched to CSS gradients, since it turned out to be easier to manage, and provided better cross browser support.
-
-### Basic Usage
-
-Head over to the [docs](http://bgrins.github.io/spectrum) for more information. There is a visual demo of the different options hosted at: http://bgrins.github.io/spectrum.
-
-
-
-
-
-
-
-
-### npm
-
-Spectrum is registered as package with npm. It can be installed with:
-
- npm install spectrum-colorpicker
-
-### Bower
-
-Spectrum is registered as a package with [Bower](http://bower.io/), so it can be pulled down using:
-
- bower install spectrum
-
-### Using spectrum with a CDN
-
-CDN provided by [cdnjs](https://cdnjs.com/libraries/spectrum)
-
-
-
-
-### Continuous Integration
-
-[![Build Status](https://secure.travis-ci.org/bgrins/spectrum.png?branch=master)](http://travis-ci.org/bgrins/spectrum)
-
-Visit https://travis-ci.org/bgrins/spectrum to view the status of the automated tests.
-
-### Building Spectrum Locally
-
-If you'd like to download and use the plugin, head over to http://bgrins.github.io/spectrum/ and click the 'Download Zip' button.
-
-If you'd like to run the development version, spectrum uses Grunt to automate the testing, linting, and building. Head over to http://gruntjs.com/getting-started for more information. First, clone the repository, then run:
-
- npm install -g grunt-cli
- npm install
-
- # runs jshint and the unit test suite
- grunt
-
- # runs jshint, the unit test suite, and builds a minified version of the file.
- grunt build
-
-### Internationalization
-
-If you are able to translate the text in the UI to another language, please do! You can do so by either [filing a pull request](https://github.com/bgrins/spectrum/pulls) or [opening an issue]( https://github.com/bgrins/spectrum/issues) with the translation. The existing languages are listed at: https://github.com/bgrins/spectrum/tree/master/i18n.
-
-For an example, see the [Dutch translation](i18n/jquery.spectrum-nl.js).
diff --git a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.css b/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.css
deleted file mode 100644
index a8ad9e4f82..0000000000
--- a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.css
+++ /dev/null
@@ -1,507 +0,0 @@
-/***
-Spectrum Colorpicker v1.8.0
-https://github.com/bgrins/spectrum
-Author: Brian Grinstead
-License: MIT
-***/
-
-.sp-container {
- position:absolute;
- top:0;
- left:0;
- display:inline-block;
- *display: inline;
- *zoom: 1;
- /* https://github.com/bgrins/spectrum/issues/40 */
- z-index: 9999994;
- overflow: hidden;
-}
-.sp-container.sp-flat {
- position: relative;
-}
-
-/* Fix for * { box-sizing: border-box; } */
-.sp-container,
-.sp-container * {
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
-}
-
-/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
-.sp-top {
- position:relative;
- width: 100%;
- display:inline-block;
-}
-.sp-top-inner {
- position:absolute;
- top:0;
- left:0;
- bottom:0;
- right:0;
-}
-.sp-color {
- position: absolute;
- top:0;
- left:0;
- bottom:0;
- right:20%;
-}
-.sp-hue {
- position: absolute;
- top:0;
- right:0;
- bottom:0;
- left:84%;
- height: 100%;
-}
-
-.sp-clear-enabled .sp-hue {
- top:33px;
- height: 77.5%;
-}
-
-.sp-fill {
- padding-top: 80%;
-}
-.sp-sat, .sp-val {
- position: absolute;
- top:0;
- left:0;
- right:0;
- bottom:0;
-}
-
-.sp-alpha-enabled .sp-top {
- margin-bottom: 18px;
-}
-.sp-alpha-enabled .sp-alpha {
- display: block;
-}
-.sp-alpha-handle {
- position:absolute;
- top:-4px;
- bottom: -4px;
- width: 6px;
- left: 50%;
- cursor: pointer;
- border: 1px solid black;
- background: white;
- opacity: .8;
-}
-.sp-alpha {
- display: none;
- position: absolute;
- bottom: -14px;
- right: 0;
- left: 0;
- height: 8px;
-}
-.sp-alpha-inner {
- border: solid 1px #333;
-}
-
-.sp-clear {
- display: none;
-}
-
-.sp-clear.sp-clear-display {
- background-position: center;
-}
-
-.sp-clear-enabled .sp-clear {
- display: block;
- position:absolute;
- top:0px;
- right:0;
- bottom:0;
- left:84%;
- height: 28px;
-}
-
-/* Don't allow text selection */
-.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button {
- -webkit-user-select:none;
- -moz-user-select: -moz-none;
- -o-user-select:none;
- user-select: none;
-}
-
-.sp-container.sp-input-disabled .sp-input-container {
- display: none;
-}
-.sp-container.sp-buttons-disabled .sp-button-container {
- display: none;
-}
-.sp-container.sp-palette-buttons-disabled .sp-palette-button-container {
- display: none;
-}
-.sp-palette-only .sp-picker-container {
- display: none;
-}
-.sp-palette-disabled .sp-palette-container {
- display: none;
-}
-
-.sp-initial-disabled .sp-initial {
- display: none;
-}
-
-
-/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */
-.sp-sat {
- background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0)));
- background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0));
- background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
- background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
- background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
- background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
- -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";
- filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81');
-}
-.sp-val {
- background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0)));
- background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0));
- background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
- background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
- background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
- background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
- -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";
- filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000');
-}
-
-.sp-hue {
- background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
- background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
- background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
- background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000));
- background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
- background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
-}
-
-/* IE filters do not support multiple color stops.
- Generate 6 divs, line them up, and do two color gradients for each.
- Yes, really.
- */
-.sp-1 {
- height:17%;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00');
-}
-.sp-2 {
- height:16%;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00');
-}
-.sp-3 {
- height:17%;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff');
-}
-.sp-4 {
- height:17%;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff');
-}
-.sp-5 {
- height:16%;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff');
-}
-.sp-6 {
- height:17%;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000');
-}
-
-.sp-hidden {
- display: none !important;
-}
-
-/* Clearfix hack */
-.sp-cf:before, .sp-cf:after { content: ""; display: table; }
-.sp-cf:after { clear: both; }
-.sp-cf { *zoom: 1; }
-
-/* Mobile devices, make hue slider bigger so it is easier to slide */
-@media (max-device-width: 480px) {
- .sp-color { right: 40%; }
- .sp-hue { left: 63%; }
- .sp-fill { padding-top: 60%; }
-}
-.sp-dragger {
- border-radius: 5px;
- height: 5px;
- width: 5px;
- border: 1px solid #fff;
- background: #000;
- cursor: pointer;
- position:absolute;
- top:0;
- left: 0;
-}
-.sp-slider {
- position: absolute;
- top:0;
- cursor:pointer;
- height: 3px;
- left: -1px;
- right: -1px;
- border: 1px solid #000;
- background: white;
- opacity: .8;
-}
-
-/*
-Theme authors:
-Here are the basic themeable display options (colors, fonts, global widths).
-See http://bgrins.github.io/spectrum/themes/ for instructions.
-*/
-
-.sp-container {
- border-radius: 0;
- background-color: #ECECEC;
- border: solid 1px #f0c49B;
- padding: 0;
-}
-.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear {
- font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- -ms-box-sizing: border-box;
- box-sizing: border-box;
-}
-.sp-top {
- margin-bottom: 3px;
-}
-.sp-color, .sp-hue, .sp-clear {
- border: solid 1px #666;
-}
-
-/* Input */
-.sp-input-container {
- float:right;
- width: 100px;
- margin-bottom: 4px;
-}
-.sp-initial-disabled .sp-input-container {
- width: 100%;
-}
-.sp-input {
- font-size: 12px !important;
- border: 1px inset;
- padding: 4px 5px;
- margin: 0;
- width: 100%;
- background:transparent;
- border-radius: 3px;
- color: #222;
-}
-.sp-input:focus {
- border: 1px solid orange;
-}
-.sp-input.sp-validation-error {
- border: 1px solid red;
- background: #fdd;
-}
-.sp-picker-container , .sp-palette-container {
- float:left;
- position: relative;
- padding: 10px;
- padding-bottom: 300px;
- margin-bottom: -290px;
-}
-.sp-picker-container {
- width: 172px;
- border-left: solid 1px #fff;
-}
-
-/* Palettes */
-.sp-palette-container {
- border-right: solid 1px #ccc;
-}
-
-.sp-palette-only .sp-palette-container {
- border: 0;
-}
-
-.sp-palette .sp-thumb-el {
- display: block;
- position:relative;
- float:left;
- width: 24px;
- height: 15px;
- margin: 3px;
- cursor: pointer;
- border:solid 2px transparent;
-}
-.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active {
- border-color: orange;
-}
-.sp-thumb-el {
- position:relative;
-}
-
-/* Initial */
-.sp-initial {
- float: left;
- border: solid 1px #333;
-}
-.sp-initial span {
- width: 30px;
- height: 25px;
- border:none;
- display:block;
- float:left;
- margin:0;
-}
-
-.sp-initial .sp-clear-display {
- background-position: center;
-}
-
-/* Buttons */
-.sp-palette-button-container,
-.sp-button-container {
- float: right;
-}
-
-/* Replacer (the little preview div that shows up instead of the ) */
-.sp-replacer {
- margin:0;
- overflow:hidden;
- cursor:pointer;
- padding: 4px;
- display:inline-block;
- *zoom: 1;
- *display: inline;
- border: solid 1px #91765d;
- background: #eee;
- color: #333;
- vertical-align: middle;
-}
-.sp-replacer:hover, .sp-replacer.sp-active {
- border-color: #F0C49B;
- color: #111;
-}
-.sp-replacer.sp-disabled {
- cursor:default;
- border-color: silver;
- color: silver;
-}
-.sp-dd {
- padding: 2px 0;
- height: 16px;
- line-height: 16px;
- float:left;
- font-size:10px;
-}
-.sp-preview {
- position:relative;
- width:25px;
- height: 20px;
- border: solid 1px #222;
- margin-right: 5px;
- float:left;
- z-index: 0;
-}
-
-.sp-palette {
- *width: 220px;
- max-width: 220px;
-}
-.sp-palette .sp-thumb-el {
- width:16px;
- height: 16px;
- margin:2px 1px;
- border: solid 1px #d0d0d0;
-}
-
-.sp-container {
- padding-bottom:0;
-}
-
-
-/* Buttons: http://hellohappy.org/css3-buttons/ */
-.sp-container button {
- background-color: #eeeeee;
- background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
- background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
- background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
- background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
- background-image: linear-gradient(to bottom, #eeeeee, #cccccc);
- border: 1px solid #ccc;
- border-bottom: 1px solid #bbb;
- border-radius: 3px;
- color: #333;
- font-size: 14px;
- line-height: 1;
- padding: 5px 4px;
- text-align: center;
- text-shadow: 0 1px 0 #eee;
- vertical-align: middle;
-}
-.sp-container button:hover {
- background-color: #dddddd;
- background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
- background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
- background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
- background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
- background-image: linear-gradient(to bottom, #dddddd, #bbbbbb);
- border: 1px solid #bbb;
- border-bottom: 1px solid #999;
- cursor: pointer;
- text-shadow: 0 1px 0 #ddd;
-}
-.sp-container button:active {
- border: 1px solid #aaa;
- border-bottom: 1px solid #888;
- -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
- -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
- -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
- -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
- box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-}
-.sp-cancel {
- font-size: 11px;
- color: #d93f3f !important;
- margin:0;
- padding:2px;
- margin-right: 5px;
- vertical-align: middle;
- text-decoration:none;
-
-}
-.sp-cancel:hover {
- color: #d93f3f !important;
- text-decoration: underline;
-}
-
-
-.sp-palette span:hover, .sp-palette span.sp-thumb-active {
- border-color: #000;
-}
-
-.sp-preview, .sp-alpha, .sp-thumb-el {
- position:relative;
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
-}
-.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner {
- display:block;
- position:absolute;
- top:0;left:0;bottom:0;right:0;
-}
-
-.sp-palette .sp-thumb-inner {
- background-position: 50% 50%;
- background-repeat: no-repeat;
-}
-
-.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=);
-}
-
-.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=);
-}
-
-.sp-clear-display {
- background-repeat:no-repeat;
- background-position: center;
- background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==);
-}
diff --git a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.js b/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.js
deleted file mode 100644
index e129e18409..0000000000
--- a/modules/backend/formwidgets/colorpicker/assets/vendor/spectrum/spectrum.js
+++ /dev/null
@@ -1,2341 +0,0 @@
-// Spectrum Colorpicker v1.8.0
-// https://github.com/bgrins/spectrum
-// Author: Brian Grinstead
-// License: MIT
-
-(function (factory) {
- "use strict";
-
- if (typeof define === 'function' && define.amd) { // AMD
- define(['jquery'], factory);
- }
- else if (typeof exports == "object" && typeof module == "object") { // CommonJS
- module.exports = factory(require('jquery'));
- }
- else { // Browser
- factory(jQuery);
- }
-})(function($, undefined) {
- "use strict";
-
- var defaultOpts = {
-
- // Callbacks
- beforeShow: noop,
- move: noop,
- change: noop,
- show: noop,
- hide: noop,
-
- // Options
- color: false,
- flat: false,
- showInput: false,
- allowEmpty: false,
- showButtons: true,
- clickoutFiresChange: true,
- showInitial: false,
- showPalette: false,
- showPaletteOnly: false,
- hideAfterPaletteSelect: false,
- togglePaletteOnly: false,
- showSelectionPalette: true,
- localStorageKey: false,
- appendTo: "body",
- maxSelectionSize: 7,
- cancelText: "cancel",
- chooseText: "choose",
- togglePaletteMoreText: "more",
- togglePaletteLessText: "less",
- clearText: "Clear Color Selection",
- noColorSelectedText: "No Color Selected",
- preferredFormat: false,
- className: "", // Deprecated - use containerClassName and replacerClassName instead.
- containerClassName: "",
- replacerClassName: "",
- showAlpha: false,
- theme: "sp-light",
- palette: [["#ffffff", "#000000", "#ff0000", "#ff8000", "#ffff00", "#008000", "#0000ff", "#4b0082", "#9400d3"]],
- selectionPalette: [],
- disabled: false,
- offset: null
- },
- spectrums = [],
- IE = !!/msie/i.exec( window.navigator.userAgent ),
- rgbaSupport = (function() {
- function contains( str, substr ) {
- return !!~('' + str).indexOf(substr);
- }
-
- var elem = document.createElement('div');
- var style = elem.style;
- style.cssText = 'background-color:rgba(0,0,0,.5)';
- return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');
- })(),
- replaceInput = [
- "
",
- "
",
- "
▼
",
- "
"
- ].join(''),
- markup = (function () {
-
- // IE does not support gradients with multiple stops, so we need to simulate
- // that for the rainbow slider with 8 divs that each have a single gradient
- var gradientFix = "";
- if (IE) {
- for (var i = 1; i <= 6; i++) {
- gradientFix += "";
- }
- }
-
- return [
- "
",
- "
",
- "",
- "
",
- "",
- "
",
- "
",
- "
",
- "
",
- "",
- "
",
- "
",
- "
",
- "
",
- "",
- "
",
- "
",
- "
",
- "
",
- "
",
- "
",
- "",
- gradientFix,
- "
",
- "
",
- "
",
- "
",
- "
",
- "",
- "
",
- "",
- "
",
- "",
- "",
- "
",
- "
",
- "
"
- ].join("");
- })();
-
- function paletteTemplate (p, color, className, opts) {
- var html = [];
- for (var i = 0; i < p.length; i++) {
- var current = p[i];
- if(current) {
- var tiny = tinycolor(current);
- var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light";
- c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : "";
- var formattedString = tiny.toString(opts.preferredFormat || "rgb");
- var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter();
- html.push('');
- } else {
- var cls = 'sp-clear-display';
- html.push($('')
- .append($('')
- .attr('title', opts.noColorSelectedText)
- )
- .html()
- );
- }
- }
- return "
" + html.join('') + "
";
- }
-
- function hideAll() {
- for (var i = 0; i < spectrums.length; i++) {
- if (spectrums[i]) {
- spectrums[i].hide();
- }
- }
- }
-
- function instanceOptions(o, callbackContext) {
- var opts = $.extend({}, defaultOpts, o);
- opts.callbacks = {
- 'move': bind(opts.move, callbackContext),
- 'change': bind(opts.change, callbackContext),
- 'show': bind(opts.show, callbackContext),
- 'hide': bind(opts.hide, callbackContext),
- 'beforeShow': bind(opts.beforeShow, callbackContext)
- };
-
- return opts;
- }
-
- function spectrum(element, o) {
-
- var opts = instanceOptions(o, element),
- flat = opts.flat,
- showSelectionPalette = opts.showSelectionPalette,
- localStorageKey = opts.localStorageKey,
- theme = opts.theme,
- callbacks = opts.callbacks,
- resize = throttle(reflow, 10),
- visible = false,
- isDragging = false,
- dragWidth = 0,
- dragHeight = 0,
- dragHelperHeight = 0,
- slideHeight = 0,
- slideWidth = 0,
- alphaWidth = 0,
- alphaSlideHelperWidth = 0,
- slideHelperHeight = 0,
- currentHue = 0,
- currentSaturation = 0,
- currentValue = 0,
- currentAlpha = 1,
- palette = [],
- paletteArray = [],
- paletteLookup = {},
- selectionPalette = opts.selectionPalette.slice(0),
- maxSelectionSize = opts.maxSelectionSize,
- draggingClass = "sp-dragging",
- shiftMovementDirection = null;
-
- var doc = element.ownerDocument,
- body = doc.body,
- boundElement = $(element),
- disabled = false,
- container = $(markup, doc).addClass(theme),
- pickerContainer = container.find(".sp-picker-container"),
- dragger = container.find(".sp-color"),
- dragHelper = container.find(".sp-dragger"),
- slider = container.find(".sp-hue"),
- slideHelper = container.find(".sp-slider"),
- alphaSliderInner = container.find(".sp-alpha-inner"),
- alphaSlider = container.find(".sp-alpha"),
- alphaSlideHelper = container.find(".sp-alpha-handle"),
- textInput = container.find(".sp-input"),
- paletteContainer = container.find(".sp-palette"),
- initialColorContainer = container.find(".sp-initial"),
- cancelButton = container.find(".sp-cancel"),
- clearButton = container.find(".sp-clear"),
- chooseButton = container.find(".sp-choose"),
- toggleButton = container.find(".sp-palette-toggle"),
- isInput = boundElement.is("input"),
- isInputTypeColor = isInput && boundElement.attr("type") === "color" && inputTypeColorSupport(),
- shouldReplace = isInput && !flat,
- replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]),
- offsetElement = (shouldReplace) ? replacer : boundElement,
- previewElement = replacer.find(".sp-preview-inner"),
- initialColor = opts.color || (isInput && boundElement.val()),
- colorOnShow = false,
- currentPreferredFormat = opts.preferredFormat,
- clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange,
- isEmpty = !initialColor,
- allowEmpty = opts.allowEmpty && !isInputTypeColor;
-
- function applyOptions() {
-
- if (opts.showPaletteOnly) {
- opts.showPalette = true;
- }
-
- toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);
-
- if (opts.palette) {
- palette = opts.palette.slice(0);
- paletteArray = $.isArray(palette[0]) ? palette : [palette];
- paletteLookup = {};
- for (var i = 0; i < paletteArray.length; i++) {
- for (var j = 0; j < paletteArray[i].length; j++) {
- var rgb = tinycolor(paletteArray[i][j]).toRgbString();
- paletteLookup[rgb] = true;
- }
- }
- }
-
- container.toggleClass("sp-flat", flat);
- container.toggleClass("sp-input-disabled", !opts.showInput);
- container.toggleClass("sp-alpha-enabled", opts.showAlpha);
- container.toggleClass("sp-clear-enabled", allowEmpty);
- container.toggleClass("sp-buttons-disabled", !opts.showButtons);
- container.toggleClass("sp-palette-buttons-disabled", !opts.togglePaletteOnly);
- container.toggleClass("sp-palette-disabled", !opts.showPalette);
- container.toggleClass("sp-palette-only", opts.showPaletteOnly);
- container.toggleClass("sp-initial-disabled", !opts.showInitial);
- container.addClass(opts.className).addClass(opts.containerClassName);
-
- reflow();
- }
-
- function initialize() {
-
- if (IE) {
- container.find("*:not(input)").attr("unselectable", "on");
- }
-
- applyOptions();
-
- if (shouldReplace) {
- boundElement.after(replacer).hide();
- }
-
- if (!allowEmpty) {
- clearButton.hide();
- }
-
- if (flat) {
- boundElement.after(container).hide();
- }
- else {
-
- var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo);
- if (appendTo.length !== 1) {
- appendTo = $("body");
- }
-
- appendTo.append(container);
- }
-
- updateSelectionPaletteFromStorage();
-
- offsetElement.on("click.spectrum touchstart.spectrum", function (e) {
- if (!disabled) {
- toggle();
- }
-
- e.stopPropagation();
-
- if (!$(e.target).is("input")) {
- e.preventDefault();
- }
- });
-
- if(boundElement.is(":disabled") || (opts.disabled === true)) {
- disable();
- }
-
- // Prevent clicks from bubbling up to document. This would cause it to be hidden.
- container.click(stopPropagation);
-
- // Handle user typed input
- textInput.change(setFromTextInput);
- textInput.on("paste", function () {
- setTimeout(setFromTextInput, 1);
- });
- textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });
-
- cancelButton.text(opts.cancelText);
- cancelButton.on("click.spectrum", function (e) {
- e.stopPropagation();
- e.preventDefault();
- revert();
- hide();
- });
-
- clearButton.attr("title", opts.clearText);
- clearButton.on("click.spectrum", function (e) {
- e.stopPropagation();
- e.preventDefault();
- isEmpty = true;
- move();
-
- if(flat) {
- //for the flat style, this is a change event
- updateOriginalInput(true);
- }
- });
-
- chooseButton.text(opts.chooseText);
- chooseButton.on("click.spectrum", function (e) {
- e.stopPropagation();
- e.preventDefault();
-
- if (IE && textInput.is(":focus")) {
- textInput.trigger('change');
- }
-
- if (isValid()) {
- updateOriginalInput(true);
- hide();
- }
- });
-
- toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);
- toggleButton.on("click.spectrum", function (e) {
- e.stopPropagation();
- e.preventDefault();
-
- opts.showPaletteOnly = !opts.showPaletteOnly;
-
- // To make sure the Picker area is drawn on the right, next to the
- // Palette area (and not below the palette), first move the Palette
- // to the left to make space for the picker, plus 5px extra.
- // The 'applyOptions' function puts the whole container back into place
- // and takes care of the button-text and the sp-palette-only CSS class.
- if (!opts.showPaletteOnly && !flat) {
- container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5));
- }
- applyOptions();
- });
-
- draggable(alphaSlider, function (dragX, dragY, e) {
- currentAlpha = (dragX / alphaWidth);
- isEmpty = false;
- if (e.shiftKey) {
- currentAlpha = Math.round(currentAlpha * 10) / 10;
- }
-
- move();
- }, dragStart, dragStop);
-
- draggable(slider, function (dragX, dragY) {
- currentHue = parseFloat(dragY / slideHeight);
- isEmpty = false;
- if (!opts.showAlpha) {
- currentAlpha = 1;
- }
- move();
- }, dragStart, dragStop);
-
- draggable(dragger, function (dragX, dragY, e) {
-
- // shift+drag should snap the movement to either the x or y axis.
- if (!e.shiftKey) {
- shiftMovementDirection = null;
- }
- else if (!shiftMovementDirection) {
- var oldDragX = currentSaturation * dragWidth;
- var oldDragY = dragHeight - (currentValue * dragHeight);
- var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY);
-
- shiftMovementDirection = furtherFromX ? "x" : "y";
- }
-
- var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x";
- var setValue = !shiftMovementDirection || shiftMovementDirection === "y";
-
- if (setSaturation) {
- currentSaturation = parseFloat(dragX / dragWidth);
- }
- if (setValue) {
- currentValue = parseFloat((dragHeight - dragY) / dragHeight);
- }
-
- isEmpty = false;
- if (!opts.showAlpha) {
- currentAlpha = 1;
- }
-
- move();
-
- }, dragStart, dragStop);
-
- if (!!initialColor) {
- set(initialColor);
-
- // In case color was black - update the preview UI and set the format
- // since the set function will not run (default color is black).
- updateUI();
- currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format;
-
- addColorToSelectionPalette(initialColor);
- }
- else {
- updateUI();
- }
-
- if (flat) {
- show();
- }
-
- function paletteElementClick(e) {
- if (e.data && e.data.ignore) {
- set($(e.target).closest(".sp-thumb-el").data("color"));
- move();
- }
- else {
- set($(e.target).closest(".sp-thumb-el").data("color"));
- move();
-
- // If the picker is going to close immediately, a palette selection
- // is a change. Otherwise, it's a move only.
- if (opts.hideAfterPaletteSelect) {
- updateOriginalInput(true);
- hide();
- } else {
- updateOriginalInput();
- }
- }
-
- return false;
- }
-
- var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum";
- paletteContainer.on(paletteEvent, ".sp-thumb-el", paletteElementClick);
- initialColorContainer.on(paletteEvent, ".sp-thumb-el:nth-child(1)", { ignore: true }, paletteElementClick);
- }
-
- function updateSelectionPaletteFromStorage() {
-
- if (localStorageKey && window.localStorage) {
-
- // Migrate old palettes over to new format. May want to remove this eventually.
- try {
- var oldPalette = window.localStorage[localStorageKey].split(",#");
- if (oldPalette.length > 1) {
- delete window.localStorage[localStorageKey];
- $.each(oldPalette, function(i, c) {
- addColorToSelectionPalette(c);
- });
- }
- }
- catch(e) { }
-
- try {
- selectionPalette = window.localStorage[localStorageKey].split(";");
- }
- catch (e) { }
- }
- }
-
- function addColorToSelectionPalette(color) {
- if (showSelectionPalette) {
- var rgb = tinycolor(color).toRgbString();
- if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {
- selectionPalette.push(rgb);
- while(selectionPalette.length > maxSelectionSize) {
- selectionPalette.shift();
- }
- }
-
- if (localStorageKey && window.localStorage) {
- try {
- window.localStorage[localStorageKey] = selectionPalette.join(";");
- }
- catch(e) { }
- }
- }
- }
-
- function getUniqueSelectionPalette() {
- var unique = [];
- if (opts.showPalette) {
- for (var i = 0; i < selectionPalette.length; i++) {
- var rgb = tinycolor(selectionPalette[i]).toRgbString();
-
- if (!paletteLookup[rgb]) {
- unique.push(selectionPalette[i]);
- }
- }
- }
-
- return unique.reverse().slice(0, opts.maxSelectionSize);
- }
-
- function drawPalette() {
-
- var currentColor = get();
-
- var html = $.map(paletteArray, function (palette, i) {
- return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i, opts);
- });
-
- updateSelectionPaletteFromStorage();
-
- if (selectionPalette) {
- html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection", opts));
- }
-
- paletteContainer.html(html.join(""));
- }
-
- function drawInitial() {
- if (opts.showInitial) {
- var initial = colorOnShow;
- var current = get();
- initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial", opts));
- }
- }
-
- function dragStart() {
- if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {
- reflow();
- }
- isDragging = true;
- container.addClass(draggingClass);
- shiftMovementDirection = null;
- boundElement.trigger('dragstart.spectrum', [ get() ]);
- }
-
- function dragStop() {
- isDragging = false;
- container.removeClass(draggingClass);
- boundElement.trigger('dragstop.spectrum', [ get() ]);
- }
-
- function setFromTextInput() {
-
- var value = textInput.val();
-
- if ((value === null || value === "") && allowEmpty) {
- set(null);
- move();
- updateOriginalInput();
- }
- else {
- var tiny = tinycolor(value);
- if (tiny.isValid()) {
- set(tiny);
- move();
- updateOriginalInput();
- }
- else {
- textInput.addClass("sp-validation-error");
- }
- }
- }
-
- function toggle() {
- if (visible) {
- hide();
- }
- else {
- show();
- }
- }
-
- function show() {
- var event = $.Event('beforeShow.spectrum');
-
- if (visible) {
- reflow();
- return;
- }
-
- boundElement.trigger(event, [ get() ]);
-
- if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) {
- return;
- }
-
- hideAll();
- visible = true;
-
- $(doc).on("keydown.spectrum", onkeydown);
- $(doc).on("click.spectrum", clickout);
- $(window).on("resize.spectrum", resize);
- replacer.addClass("sp-active");
- container.removeClass("sp-hidden");
-
- reflow();
- updateUI();
-
- colorOnShow = get();
-
- drawInitial();
- callbacks.show(colorOnShow);
- boundElement.trigger('show.spectrum', [ colorOnShow ]);
- }
-
- function onkeydown(e) {
- // Close on ESC
- if (e.keyCode === 27) {
- hide();
- }
- }
-
- function clickout(e) {
- // Return on right click.
- if (e.button == 2) { return; }
-
- // If a drag event was happening during the mouseup, don't hide
- // on click.
- if (isDragging) { return; }
-
- if (clickoutFiresChange) {
- updateOriginalInput(true);
- }
- else {
- revert();
- }
- hide();
- }
-
- function hide() {
- // Return if hiding is unnecessary
- if (!visible || flat) { return; }
- visible = false;
-
- $(doc).off("keydown.spectrum", onkeydown);
- $(doc).off("click.spectrum", clickout);
- $(window).off("resize.spectrum", resize);
-
- replacer.removeClass("sp-active");
- container.addClass("sp-hidden");
-
- callbacks.hide(get());
- boundElement.trigger('hide.spectrum', [ get() ]);
- }
-
- function revert() {
- set(colorOnShow, true);
- updateOriginalInput(true);
- }
-
- function set(color, ignoreFormatChange) {
- if (tinycolor.equals(color, get())) {
- // Update UI just in case a validation error needs
- // to be cleared.
- updateUI();
- return;
- }
-
- var newColor, newHsv;
- if (!color && allowEmpty) {
- isEmpty = true;
- } else {
- isEmpty = false;
- newColor = tinycolor(color);
- newHsv = newColor.toHsv();
-
- currentHue = (newHsv.h % 360) / 360;
- currentSaturation = newHsv.s;
- currentValue = newHsv.v;
- currentAlpha = newHsv.a;
- }
- updateUI();
-
- if (newColor && newColor.isValid() && !ignoreFormatChange) {
- currentPreferredFormat = opts.preferredFormat || newColor.getFormat();
- }
- }
-
- function get(opts) {
- opts = opts || { };
-
- if (allowEmpty && isEmpty) {
- return null;
- }
-
- return tinycolor.fromRatio({
- h: currentHue,
- s: currentSaturation,
- v: currentValue,
- a: Math.round(currentAlpha * 1000) / 1000
- }, { format: opts.format || currentPreferredFormat });
- }
-
- function isValid() {
- return !textInput.hasClass("sp-validation-error");
- }
-
- function move() {
- updateUI();
-
- callbacks.move(get());
- boundElement.trigger('move.spectrum', [ get() ]);
- }
-
- function updateUI() {
-
- textInput.removeClass("sp-validation-error");
-
- updateHelperLocations();
-
- // Update dragger background color (gradients take care of saturation and value).
- var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 });
- dragger.css("background-color", flatColor.toHexString());
-
- // Get a format that alpha will be included in (hex and names ignore alpha)
- var format = currentPreferredFormat;
- if (currentAlpha < 1 && !(currentAlpha === 0 && format === "name")) {
- if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") {
- format = "rgb";
- }
- }
-
- var realColor = get({ format: format }),
- displayColor = '';
-
- //reset background info for preview element
- previewElement.removeClass("sp-clear-display");
- previewElement.css('background-color', 'transparent');
-
- if (!realColor && allowEmpty) {
- // Update the replaced elements background with icon indicating no color selection
- previewElement.addClass("sp-clear-display");
- }
- else {
- var realHex = realColor.toHexString(),
- realRgb = realColor.toRgbString();
-
- // Update the replaced elements background color (with actual selected color)
- if (rgbaSupport || realColor.alpha === 1) {
- previewElement.css("background-color", realRgb);
- }
- else {
- previewElement.css("background-color", "transparent");
- previewElement.css("filter", realColor.toFilter());
- }
-
- if (opts.showAlpha) {
- var rgb = realColor.toRgb();
- rgb.a = 0;
- var realAlpha = tinycolor(rgb).toRgbString();
- var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")";
-
- if (IE) {
- alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));
- }
- else {
- alphaSliderInner.css("background", "-webkit-" + gradient);
- alphaSliderInner.css("background", "-moz-" + gradient);
- alphaSliderInner.css("background", "-ms-" + gradient);
- // Use current syntax gradient on unprefixed property.
- alphaSliderInner.css("background",
- "linear-gradient(to right, " + realAlpha + ", " + realHex + ")");
- }
- }
-
- displayColor = realColor.toString(format);
- }
-
- // Update the text entry input as it changes happen
- if (opts.showInput) {
- textInput.val(displayColor);
- }
-
- if (opts.showPalette) {
- drawPalette();
- }
-
- drawInitial();
- }
-
- function updateHelperLocations() {
- var s = currentSaturation;
- var v = currentValue;
-
- if(allowEmpty && isEmpty) {
- //if selected color is empty, hide the helpers
- alphaSlideHelper.hide();
- slideHelper.hide();
- dragHelper.hide();
- }
- else {
- //make sure helpers are visible
- alphaSlideHelper.show();
- slideHelper.show();
- dragHelper.show();
-
- // Where to show the little circle in that displays your current selected color
- var dragX = s * dragWidth;
- var dragY = dragHeight - (v * dragHeight);
- dragX = Math.max(
- -dragHelperHeight,
- Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)
- );
- dragY = Math.max(
- -dragHelperHeight,
- Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)
- );
- dragHelper.css({
- "top": dragY + "px",
- "left": dragX + "px"
- });
-
- var alphaX = currentAlpha * alphaWidth;
- alphaSlideHelper.css({
- "left": (alphaX - (alphaSlideHelperWidth / 2)) + "px"
- });
-
- // Where to show the bar that displays your current selected hue
- var slideY = (currentHue) * slideHeight;
- slideHelper.css({
- "top": (slideY - slideHelperHeight) + "px"
- });
- }
- }
-
- function updateOriginalInput(fireCallback) {
- var color = get(),
- displayColor = '',
- hasChanged = !tinycolor.equals(color, colorOnShow);
-
- if (color) {
- displayColor = color.toString(currentPreferredFormat);
- // Update the selection palette with the current color
- addColorToSelectionPalette(color);
- }
-
- if (isInput) {
- boundElement.val(displayColor);
- }
-
- if (fireCallback && hasChanged) {
- callbacks.change(color);
- boundElement.trigger('change', [ color ]);
- }
- }
-
- function reflow() {
- if (!visible) {
- return; // Calculations would be useless and wouldn't be reliable anyways
- }
- dragWidth = dragger.width();
- dragHeight = dragger.height();
- dragHelperHeight = dragHelper.height();
- slideWidth = slider.width();
- slideHeight = slider.height();
- slideHelperHeight = slideHelper.height();
- alphaWidth = alphaSlider.width();
- alphaSlideHelperWidth = alphaSlideHelper.width();
-
- if (!flat) {
- container.css("position", "absolute");
- if (opts.offset) {
- container.offset(opts.offset);
- } else {
- container.offset(getOffset(container, offsetElement));
- }
- }
-
- updateHelperLocations();
-
- if (opts.showPalette) {
- drawPalette();
- }
-
- boundElement.trigger('reflow.spectrum');
- }
-
- function destroy() {
- boundElement.show();
- offsetElement.off("click.spectrum touchstart.spectrum");
- container.remove();
- replacer.remove();
- spectrums[spect.id] = null;
- }
-
- function option(optionName, optionValue) {
- if (optionName === undefined) {
- return $.extend({}, opts);
- }
- if (optionValue === undefined) {
- return opts[optionName];
- }
-
- opts[optionName] = optionValue;
-
- if (optionName === "preferredFormat") {
- currentPreferredFormat = opts.preferredFormat;
- }
- applyOptions();
- }
-
- function enable() {
- disabled = false;
- boundElement.attr("disabled", false);
- offsetElement.removeClass("sp-disabled");
- }
-
- function disable() {
- hide();
- disabled = true;
- boundElement.attr("disabled", true);
- offsetElement.addClass("sp-disabled");
- }
-
- function setOffset(coord) {
- opts.offset = coord;
- reflow();
- }
-
- initialize();
-
- var spect = {
- show: show,
- hide: hide,
- toggle: toggle,
- reflow: reflow,
- option: option,
- enable: enable,
- disable: disable,
- offset: setOffset,
- set: function (c) {
- set(c);
- updateOriginalInput();
- },
- get: get,
- destroy: destroy,
- container: container
- };
-
- spect.id = spectrums.push(spect) - 1;
-
- return spect;
- }
-
- /**
- * checkOffset - get the offset below/above and left/right element depending on screen position
- * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js
- */
- function getOffset(picker, input) {
- var extraY = 0;
- var dpWidth = picker.outerWidth();
- var dpHeight = picker.outerHeight();
- var inputHeight = input.outerHeight();
- var doc = picker[0].ownerDocument;
- var docElem = doc.documentElement;
- var viewWidth = docElem.clientWidth + $(doc).scrollLeft();
- var viewHeight = docElem.clientHeight + $(doc).scrollTop();
- var offset = input.offset();
- var offsetLeft = offset.left;
- var offsetTop = offset.top;
-
- offsetTop += inputHeight;
-
- 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 {
- top: offsetTop,
- bottom: offset.bottom,
- left: offsetLeft,
- right: offset.right,
- width: offset.width,
- height: offset.height
- };
- }
-
- /**
- * noop - do nothing
- */
- function noop() {
-
- }
-
- /**
- * stopPropagation - makes the code only doing this a little easier to read in line
- */
- function stopPropagation(e) {
- e.stopPropagation();
- }
-
- /**
- * Create a function bound to a given object
- * Thanks to underscore.js
- */
- function bind(func, obj) {
- var slice = Array.prototype.slice;
- var args = slice.call(arguments, 2);
- return function () {
- return func.apply(obj, args.concat(slice.call(arguments)));
- };
- }
-
- /**
- * Lightweight drag helper. Handles containment within the element, so that
- * when dragging, the x is within [0,element.width] and y is within [0,element.height]
- */
- function draggable(element, onmove, onstart, onstop) {
- onmove = onmove || function () { };
- onstart = onstart || function () { };
- onstop = onstop || function () { };
- var doc = document;
- var dragging = false;
- var offset = {};
- var maxHeight = 0;
- var maxWidth = 0;
- var hasTouch = ('ontouchstart' in window);
-
- var duringDragEvents = {};
- duringDragEvents["selectstart"] = prevent;
- duringDragEvents["dragstart"] = prevent;
- duringDragEvents["touchmove mousemove"] = move;
- duringDragEvents["touchend mouseup"] = stop;
-
- function prevent(e) {
- if (e.stopPropagation) {
- e.stopPropagation();
- }
- if (e.preventDefault) {
- e.preventDefault();
- }
- e.returnValue = false;
- }
-
- function move(e) {
- if (dragging) {
- // Mouseup happened outside of window
- if (IE && doc.documentMode < 9 && !e.button) {
- return stop();
- }
-
- var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0];
- var pageX = t0 && t0.pageX || e.pageX;
- var pageY = t0 && t0.pageY || e.pageY;
-
- var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
- var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
-
- if (hasTouch) {
- // Stop scrolling in iOS
- prevent(e);
- }
-
- onmove.apply(element, [dragX, dragY, e]);
- }
- }
-
- function start(e) {
- var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);
-
- if (!rightclick && !dragging) {
- if (onstart.apply(element, arguments) !== false) {
- dragging = true;
- maxHeight = $(element).height();
- maxWidth = $(element).width();
- offset = $(element).offset();
-
- $(doc).on(duringDragEvents);
- $(doc.body).addClass("sp-dragging");
-
- move(e);
-
- prevent(e);
- }
- }
- }
-
- function stop() {
- if (dragging) {
- $(doc).off(duringDragEvents);
- $(doc.body).removeClass("sp-dragging");
-
- // Wait a tick before notifying observers to allow the click event
- // to fire in Chrome.
- setTimeout(function() {
- onstop.apply(element, arguments);
- }, 0);
- }
- dragging = false;
- }
-
- $(element).on("touchstart mousedown", start);
- }
-
- function throttle(func, wait, debounce) {
- var timeout;
- return function () {
- var context = this, args = arguments;
- var throttler = function () {
- timeout = null;
- func.apply(context, args);
- };
- if (debounce) clearTimeout(timeout);
- if (debounce || !timeout) timeout = setTimeout(throttler, wait);
- };
- }
-
- function inputTypeColorSupport() {
- return $.fn.spectrum.inputTypeColorSupport();
- }
-
- /**
- * Define a jQuery plugin
- */
- var dataID = "spectrum.id";
- $.fn.spectrum = function (opts, extra) {
-
- if (typeof opts == "string") {
-
- var returnValue = this;
- var args = Array.prototype.slice.call( arguments, 1 );
-
- this.each(function () {
- var spect = spectrums[$(this).data(dataID)];
- if (spect) {
- var method = spect[opts];
- if (!method) {
- throw new Error( "Spectrum: no such method: '" + opts + "'" );
- }
-
- if (opts == "get") {
- returnValue = spect.get();
- }
- else if (opts == "container") {
- returnValue = spect.container;
- }
- else if (opts == "option") {
- returnValue = spect.option.apply(spect, args);
- }
- else if (opts == "destroy") {
- spect.destroy();
- $(this).removeData(dataID);
- }
- else {
- method.apply(spect, args);
- }
- }
- });
-
- return returnValue;
- }
-
- // Initializing a new instance of spectrum
- return this.spectrum("destroy").each(function () {
- var options = $.extend({}, $(this).data(), opts);
- var spect = spectrum(this, options);
- $(this).data(dataID, spect.id);
- });
- };
-
- $.fn.spectrum.load = true;
- $.fn.spectrum.loadOpts = {};
- $.fn.spectrum.draggable = draggable;
- $.fn.spectrum.defaults = defaultOpts;
- $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() {
- if (typeof inputTypeColorSupport._cachedResult === "undefined") {
- var colorInput = $("")[0]; // if color element is supported, value will default to not null
- inputTypeColorSupport._cachedResult = colorInput.type === "color" && colorInput.value !== "";
- }
- return inputTypeColorSupport._cachedResult;
- };
-
- $.spectrum = { };
- $.spectrum.localization = { };
- $.spectrum.palettes = { };
-
- $.fn.spectrum.processNativeColorInputs = function () {
- var colorInputs = $("input[type=color]");
- if (colorInputs.length && !inputTypeColorSupport()) {
- colorInputs.spectrum({
- preferredFormat: "hex6"
- });
- }
- };
-
- // TinyColor v1.1.2
- // https://github.com/bgrins/TinyColor
- // Brian Grinstead, MIT License
-
- (function() {
-
- var trimLeft = /^[\s,#]+/,
- trimRight = /\s+$/,
- tinyCounter = 0,
- math = Math,
- mathRound = math.round,
- mathMin = math.min,
- mathMax = math.max,
- mathRandom = math.random;
-
- var tinycolor = function(color, opts) {
-
- color = (color) ? color : '';
- opts = opts || { };
-
- // If input is already a tinycolor, return itself
- if (color instanceof tinycolor) {
- return color;
- }
- // If we are called as a function, call using new instead
- if (!(this instanceof tinycolor)) {
- return new tinycolor(color, opts);
- }
-
- var rgb = inputToRGB(color);
- this._originalInput = color,
- this._r = rgb.r,
- this._g = rgb.g,
- this._b = rgb.b,
- this._a = rgb.a,
- this._roundA = mathRound(1000 * this._a) / 1000,
- this._format = opts.format || rgb.format;
- this._gradientType = opts.gradientType;
-
- // Don't let the range of [0,255] come back in [0,1].
- // Potentially lose a little bit of precision here, but will fix issues where
- // .5 gets interpreted as half of the total, instead of half of 1
- // If it was supposed to be 128, this was already taken care of by `inputToRgb`
- if (this._r < 1) { this._r = mathRound(this._r); }
- if (this._g < 1) { this._g = mathRound(this._g); }
- if (this._b < 1) { this._b = mathRound(this._b); }
-
- this._ok = rgb.ok;
- this._tc_id = tinyCounter++;
- };
-
- tinycolor.prototype = {
- isDark: function() {
- return this.getBrightness() < 128;
- },
- isLight: function() {
- return !this.isDark();
- },
- isValid: function() {
- return this._ok;
- },
- getOriginalInput: function() {
- return this._originalInput;
- },
- getFormat: function() {
- return this._format;
- },
- getAlpha: function() {
- return this._a;
- },
- getBrightness: function() {
- var rgb = this.toRgb();
- return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
- },
- setAlpha: function(value) {
- this._a = boundAlpha(value);
- this._roundA = mathRound(1000 * this._a) / 1000;
- return this;
- },
- toHsv: function() {
- var hsv = rgbToHsv(this._r, this._g, this._b);
- return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
- },
- toHsvString: function() {
- var hsv = rgbToHsv(this._r, this._g, this._b);
- var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
- return (this._a == 1) ?
- "hsv(" + h + ", " + s + "%, " + v + "%)" :
- "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
- },
- toHsl: function() {
- var hsl = rgbToHsl(this._r, this._g, this._b);
- return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
- },
- toHslString: function() {
- var hsl = rgbToHsl(this._r, this._g, this._b);
- var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
- return (this._a == 1) ?
- "hsl(" + h + ", " + s + "%, " + l + "%)" :
- "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
- },
- toHex: function(allow3Char) {
- return rgbToHex(this._r, this._g, this._b, allow3Char);
- },
- toHexString: function(allow3Char) {
- return '#' + this.toHex(allow3Char);
- },
- toHex8: function() {
- return rgbaToHex(this._r, this._g, this._b, this._a);
- },
- toHex8String: function() {
- return '#' + this.toHex8();
- },
- toRgb: function() {
- return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
- },
- toRgbString: function() {
- return (this._a == 1) ?
- "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
- "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
- },
- toPercentageRgb: function() {
- return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
- },
- toPercentageRgbString: function() {
- return (this._a == 1) ?
- "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
- "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
- },
- toName: function() {
- if (this._a === 0) {
- return "transparent";
- }
-
- if (this._a < 1) {
- return false;
- }
-
- return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
- },
- toFilter: function(secondColor) {
- var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
- var secondHex8String = hex8String;
- var gradientType = this._gradientType ? "GradientType = 1, " : "";
-
- if (secondColor) {
- var s = tinycolor(secondColor);
- secondHex8String = s.toHex8String();
- }
-
- return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
- },
- toString: function(format) {
- var formatSet = !!format;
- format = format || this._format;
-
- var formattedString = false;
- var hasAlpha = this._a < 1 && this._a >= 0;
- var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
-
- if (needsAlphaFormat) {
- // Special case for "transparent", all other non-alpha formats
- // will return rgba when there is transparency.
- if (format === "name" && this._a === 0) {
- return this.toName();
- }
- return this.toRgbString();
- }
- if (format === "rgb") {
- formattedString = this.toRgbString();
- }
- if (format === "prgb") {
- formattedString = this.toPercentageRgbString();
- }
- if (format === "hex" || format === "hex6") {
- formattedString = this.toHexString();
- }
- if (format === "hex3") {
- formattedString = this.toHexString(true);
- }
- if (format === "hex8") {
- formattedString = this.toHex8String();
- }
- if (format === "name") {
- formattedString = this.toName();
- }
- if (format === "hsl") {
- formattedString = this.toHslString();
- }
- if (format === "hsv") {
- formattedString = this.toHsvString();
- }
-
- return formattedString || this.toHexString();
- },
-
- _applyModification: function(fn, args) {
- var color = fn.apply(null, [this].concat([].slice.call(args)));
- this._r = color._r;
- this._g = color._g;
- this._b = color._b;
- this.setAlpha(color._a);
- return this;
- },
- lighten: function() {
- return this._applyModification(lighten, arguments);
- },
- brighten: function() {
- return this._applyModification(brighten, arguments);
- },
- darken: function() {
- return this._applyModification(darken, arguments);
- },
- desaturate: function() {
- return this._applyModification(desaturate, arguments);
- },
- saturate: function() {
- return this._applyModification(saturate, arguments);
- },
- greyscale: function() {
- return this._applyModification(greyscale, arguments);
- },
- spin: function() {
- return this._applyModification(spin, arguments);
- },
-
- _applyCombination: function(fn, args) {
- return fn.apply(null, [this].concat([].slice.call(args)));
- },
- analogous: function() {
- return this._applyCombination(analogous, arguments);
- },
- complement: function() {
- return this._applyCombination(complement, arguments);
- },
- monochromatic: function() {
- return this._applyCombination(monochromatic, arguments);
- },
- splitcomplement: function() {
- return this._applyCombination(splitcomplement, arguments);
- },
- triad: function() {
- return this._applyCombination(triad, arguments);
- },
- tetrad: function() {
- return this._applyCombination(tetrad, arguments);
- }
- };
-
- // If input is an object, force 1 into "1.0" to handle ratios properly
- // String input requires "1.0" as input, so 1 will be treated as 1
- tinycolor.fromRatio = function(color, opts) {
- if (typeof color == "object") {
- var newColor = {};
- for (var i in color) {
- if (color.hasOwnProperty(i)) {
- if (i === "a") {
- newColor[i] = color[i];
- }
- else {
- newColor[i] = convertToPercentage(color[i]);
- }
- }
- }
- color = newColor;
- }
-
- return tinycolor(color, opts);
- };
-
- // Given a string or object, convert that input to RGB
- // Possible string inputs:
- //
- // "red"
- // "#f00" or "f00"
- // "#ff0000" or "ff0000"
- // "#ff000000" or "ff000000"
- // "rgb 255 0 0" or "rgb (255, 0, 0)"
- // "rgb 1.0 0 0" or "rgb (1, 0, 0)"
- // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
- // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
- // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
- // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
- // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
- //
- function inputToRGB(color) {
-
- var rgb = { r: 0, g: 0, b: 0 };
- var a = 1;
- var ok = false;
- var format = false;
-
- if (typeof color == "string") {
- color = stringInputToObject(color);
- }
-
- if (typeof color == "object") {
- if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
- rgb = rgbToRgb(color.r, color.g, color.b);
- ok = true;
- format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
- }
- else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
- color.s = convertToPercentage(color.s);
- color.v = convertToPercentage(color.v);
- rgb = hsvToRgb(color.h, color.s, color.v);
- ok = true;
- format = "hsv";
- }
- else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
- color.s = convertToPercentage(color.s);
- color.l = convertToPercentage(color.l);
- rgb = hslToRgb(color.h, color.s, color.l);
- ok = true;
- format = "hsl";
- }
-
- if (color.hasOwnProperty("a")) {
- a = color.a;
- }
- }
-
- a = boundAlpha(a);
-
- return {
- ok: ok,
- format: color.format || format,
- r: mathMin(255, mathMax(rgb.r, 0)),
- g: mathMin(255, mathMax(rgb.g, 0)),
- b: mathMin(255, mathMax(rgb.b, 0)),
- a: a
- };
- }
-
-
- // Conversion Functions
- // --------------------
-
- // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
- //
-
- // `rgbToRgb`
- // Handle bounds / percentage checking to conform to CSS color spec
- //
- // *Assumes:* r, g, b in [0, 255] or [0, 1]
- // *Returns:* { r, g, b } in [0, 255]
- function rgbToRgb(r, g, b){
- return {
- r: bound01(r, 255) * 255,
- g: bound01(g, 255) * 255,
- b: bound01(b, 255) * 255
- };
- }
-
- // `rgbToHsl`
- // Converts an RGB color value to HSL.
- // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
- // *Returns:* { h, s, l } in [0,1]
- function rgbToHsl(r, g, b) {
-
- r = bound01(r, 255);
- g = bound01(g, 255);
- b = bound01(b, 255);
-
- var max = mathMax(r, g, b), min = mathMin(r, g, b);
- var h, s, l = (max + min) / 2;
-
- if(max == min) {
- h = s = 0; // achromatic
- }
- else {
- var d = max - min;
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
- switch(max) {
- case r: h = (g - b) / d + (g < b ? 6 : 0); break;
- case g: h = (b - r) / d + 2; break;
- case b: h = (r - g) / d + 4; break;
- }
-
- h /= 6;
- }
-
- return { h: h, s: s, l: l };
- }
-
- // `hslToRgb`
- // Converts an HSL color value to RGB.
- // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
- // *Returns:* { r, g, b } in the set [0, 255]
- function hslToRgb(h, s, l) {
- var r, g, b;
-
- h = bound01(h, 360);
- s = bound01(s, 100);
- l = bound01(l, 100);
-
- function hue2rgb(p, q, t) {
- if(t < 0) t += 1;
- if(t > 1) t -= 1;
- if(t < 1/6) return p + (q - p) * 6 * t;
- if(t < 1/2) return q;
- if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- }
-
- if(s === 0) {
- r = g = b = l; // achromatic
- }
- else {
- var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- var p = 2 * l - q;
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1/3);
- }
-
- return { r: r * 255, g: g * 255, b: b * 255 };
- }
-
- // `rgbToHsv`
- // Converts an RGB color value to HSV
- // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
- // *Returns:* { h, s, v } in [0,1]
- function rgbToHsv(r, g, b) {
-
- r = bound01(r, 255);
- g = bound01(g, 255);
- b = bound01(b, 255);
-
- var max = mathMax(r, g, b), min = mathMin(r, g, b);
- var h, s, v = max;
-
- var d = max - min;
- s = max === 0 ? 0 : d / max;
-
- if(max == min) {
- h = 0; // achromatic
- }
- else {
- switch(max) {
- case r: h = (g - b) / d + (g < b ? 6 : 0); break;
- case g: h = (b - r) / d + 2; break;
- case b: h = (r - g) / d + 4; break;
- }
- h /= 6;
- }
- return { h: h, s: s, v: v };
- }
-
- // `hsvToRgb`
- // Converts an HSV color value to RGB.
- // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
- // *Returns:* { r, g, b } in the set [0, 255]
- function hsvToRgb(h, s, v) {
-
- h = bound01(h, 360) * 6;
- s = bound01(s, 100);
- v = bound01(v, 100);
-
- var i = math.floor(h),
- f = h - i,
- p = v * (1 - s),
- q = v * (1 - f * s),
- t = v * (1 - (1 - f) * s),
- mod = i % 6,
- r = [v, q, p, p, t, v][mod],
- g = [t, v, v, q, p, p][mod],
- b = [p, p, t, v, v, q][mod];
-
- return { r: r * 255, g: g * 255, b: b * 255 };
- }
-
- // `rgbToHex`
- // Converts an RGB color to hex
- // Assumes r, g, and b are contained in the set [0, 255]
- // Returns a 3 or 6 character hex
- function rgbToHex(r, g, b, allow3Char) {
-
- var hex = [
- pad2(mathRound(r).toString(16)),
- pad2(mathRound(g).toString(16)),
- pad2(mathRound(b).toString(16))
- ];
-
- // Return a 3 character hex if possible
- if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
- return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
- }
-
- return hex.join("");
- }
- // `rgbaToHex`
- // Converts an RGBA color plus alpha transparency to hex
- // Assumes r, g, b and a are contained in the set [0, 255]
- // Returns an 8 character hex
- function rgbaToHex(r, g, b, a) {
-
- var hex = [
- pad2(convertDecimalToHex(a)),
- pad2(mathRound(r).toString(16)),
- pad2(mathRound(g).toString(16)),
- pad2(mathRound(b).toString(16))
- ];
-
- return hex.join("");
- }
-
- // `equals`
- // Can be called with any tinycolor input
- tinycolor.equals = function (color1, color2) {
- if (!color1 || !color2) { return false; }
- return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
- };
- tinycolor.random = function() {
- return tinycolor.fromRatio({
- r: mathRandom(),
- g: mathRandom(),
- b: mathRandom()
- });
- };
-
-
- // Modification Functions
- // ----------------------
- // Thanks to less.js for some of the basics here
- //
-
- function desaturate(color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.s -= amount / 100;
- hsl.s = clamp01(hsl.s);
- return tinycolor(hsl);
- }
-
- function saturate(color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.s += amount / 100;
- hsl.s = clamp01(hsl.s);
- return tinycolor(hsl);
- }
-
- function greyscale(color) {
- return tinycolor(color).desaturate(100);
- }
-
- function lighten (color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.l += amount / 100;
- hsl.l = clamp01(hsl.l);
- return tinycolor(hsl);
- }
-
- function brighten(color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var rgb = tinycolor(color).toRgb();
- rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
- rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
- rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
- return tinycolor(rgb);
- }
-
- function darken (color, amount) {
- amount = (amount === 0) ? 0 : (amount || 10);
- var hsl = tinycolor(color).toHsl();
- hsl.l -= amount / 100;
- hsl.l = clamp01(hsl.l);
- return tinycolor(hsl);
- }
-
- // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
- // Values outside of this range will be wrapped into this range.
- function spin(color, amount) {
- var hsl = tinycolor(color).toHsl();
- var hue = (mathRound(hsl.h) + amount) % 360;
- hsl.h = hue < 0 ? 360 + hue : hue;
- return tinycolor(hsl);
- }
-
- // Combination Functions
- // ---------------------
- // Thanks to jQuery xColor for some of the ideas behind these
- //
-
- function complement(color) {
- var hsl = tinycolor(color).toHsl();
- hsl.h = (hsl.h + 180) % 360;
- return tinycolor(hsl);
- }
-
- function triad(color) {
- var hsl = tinycolor(color).toHsl();
- var h = hsl.h;
- return [
- tinycolor(color),
- tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
- tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
- ];
- }
-
- function tetrad(color) {
- var hsl = tinycolor(color).toHsl();
- var h = hsl.h;
- return [
- tinycolor(color),
- tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
- tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
- tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
- ];
- }
-
- function splitcomplement(color) {
- var hsl = tinycolor(color).toHsl();
- var h = hsl.h;
- return [
- tinycolor(color),
- tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
- tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
- ];
- }
-
- function analogous(color, results, slices) {
- results = results || 6;
- slices = slices || 30;
-
- var hsl = tinycolor(color).toHsl();
- var part = 360 / slices;
- var ret = [tinycolor(color)];
-
- for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
- hsl.h = (hsl.h + part) % 360;
- ret.push(tinycolor(hsl));
- }
- return ret;
- }
-
- function monochromatic(color, results) {
- results = results || 6;
- var hsv = tinycolor(color).toHsv();
- var h = hsv.h, s = hsv.s, v = hsv.v;
- var ret = [];
- var modification = 1 / results;
-
- while (results--) {
- ret.push(tinycolor({ h: h, s: s, v: v}));
- v = (v + modification) % 1;
- }
-
- return ret;
- }
-
- // Utility Functions
- // ---------------------
-
- tinycolor.mix = function(color1, color2, amount) {
- amount = (amount === 0) ? 0 : (amount || 50);
-
- var rgb1 = tinycolor(color1).toRgb();
- var rgb2 = tinycolor(color2).toRgb();
-
- var p = amount / 100;
- var w = p * 2 - 1;
- var a = rgb2.a - rgb1.a;
-
- var w1;
-
- if (w * a == -1) {
- w1 = w;
- } else {
- w1 = (w + a) / (1 + w * a);
- }
-
- w1 = (w1 + 1) / 2;
-
- var w2 = 1 - w1;
-
- var rgba = {
- r: rgb2.r * w1 + rgb1.r * w2,
- g: rgb2.g * w1 + rgb1.g * w2,
- b: rgb2.b * w1 + rgb1.b * w2,
- a: rgb2.a * p + rgb1.a * (1 - p)
- };
-
- return tinycolor(rgba);
- };
-
-
- // Readability Functions
- // ---------------------
- //
-
- // `readability`
- // Analyze the 2 colors and returns an object with the following properties:
- // `brightness`: difference in brightness between the two colors
- // `color`: difference in color/hue between the two colors
- tinycolor.readability = function(color1, color2) {
- var c1 = tinycolor(color1);
- var c2 = tinycolor(color2);
- var rgb1 = c1.toRgb();
- var rgb2 = c2.toRgb();
- var brightnessA = c1.getBrightness();
- var brightnessB = c2.getBrightness();
- var colorDiff = (
- Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +
- Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +
- Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)
- );
-
- return {
- brightness: Math.abs(brightnessA - brightnessB),
- color: colorDiff
- };
- };
-
- // `readable`
- // http://www.w3.org/TR/AERT#color-contrast
- // Ensure that foreground and background color combinations provide sufficient contrast.
- // *Example*
- // tinycolor.isReadable("#000", "#111") => false
- tinycolor.isReadable = function(color1, color2) {
- var readability = tinycolor.readability(color1, color2);
- return readability.brightness > 125 && readability.color > 500;
- };
-
- // `mostReadable`
- // Given a base color and a list of possible foreground or background
- // colors for that base, returns the most readable color.
- // *Example*
- // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
- tinycolor.mostReadable = function(baseColor, colorList) {
- var bestColor = null;
- var bestScore = 0;
- var bestIsReadable = false;
- for (var i=0; i < colorList.length; i++) {
-
- // We normalize both around the "acceptable" breaking point,
- // but rank brightness constrast higher than hue.
-
- var readability = tinycolor.readability(baseColor, colorList[i]);
- var readable = readability.brightness > 125 && readability.color > 500;
- var score = 3 * (readability.brightness / 125) + (readability.color / 500);
-
- if ((readable && ! bestIsReadable) ||
- (readable && bestIsReadable && score > bestScore) ||
- ((! readable) && (! bestIsReadable) && score > bestScore)) {
- bestIsReadable = readable;
- bestScore = score;
- bestColor = tinycolor(colorList[i]);
- }
- }
- return bestColor;
- };
-
-
- // Big List of Colors
- // ------------------
- //
- var names = tinycolor.names = {
- aliceblue: "f0f8ff",
- antiquewhite: "faebd7",
- aqua: "0ff",
- aquamarine: "7fffd4",
- azure: "f0ffff",
- beige: "f5f5dc",
- bisque: "ffe4c4",
- black: "000",
- blanchedalmond: "ffebcd",
- blue: "00f",
- blueviolet: "8a2be2",
- brown: "a52a2a",
- burlywood: "deb887",
- burntsienna: "ea7e5d",
- cadetblue: "5f9ea0",
- chartreuse: "7fff00",
- chocolate: "d2691e",
- coral: "ff7f50",
- cornflowerblue: "6495ed",
- cornsilk: "fff8dc",
- crimson: "dc143c",
- cyan: "0ff",
- darkblue: "00008b",
- darkcyan: "008b8b",
- darkgoldenrod: "b8860b",
- darkgray: "a9a9a9",
- darkgreen: "006400",
- darkgrey: "a9a9a9",
- darkkhaki: "bdb76b",
- darkmagenta: "8b008b",
- darkolivegreen: "556b2f",
- darkorange: "ff8c00",
- darkorchid: "9932cc",
- darkred: "8b0000",
- darksalmon: "e9967a",
- darkseagreen: "8fbc8f",
- darkslateblue: "483d8b",
- darkslategray: "2f4f4f",
- darkslategrey: "2f4f4f",
- darkturquoise: "00ced1",
- darkviolet: "9400d3",
- deeppink: "ff1493",
- deepskyblue: "00bfff",
- dimgray: "696969",
- dimgrey: "696969",
- dodgerblue: "1e90ff",
- firebrick: "b22222",
- floralwhite: "fffaf0",
- forestgreen: "228b22",
- fuchsia: "f0f",
- gainsboro: "dcdcdc",
- ghostwhite: "f8f8ff",
- gold: "ffd700",
- goldenrod: "daa520",
- gray: "808080",
- green: "008000",
- greenyellow: "adff2f",
- grey: "808080",
- honeydew: "f0fff0",
- hotpink: "ff69b4",
- indianred: "cd5c5c",
- indigo: "4b0082",
- ivory: "fffff0",
- khaki: "f0e68c",
- lavender: "e6e6fa",
- lavenderblush: "fff0f5",
- lawngreen: "7cfc00",
- lemonchiffon: "fffacd",
- lightblue: "add8e6",
- lightcoral: "f08080",
- lightcyan: "e0ffff",
- lightgoldenrodyellow: "fafad2",
- lightgray: "d3d3d3",
- lightgreen: "90ee90",
- lightgrey: "d3d3d3",
- lightpink: "ffb6c1",
- lightsalmon: "ffa07a",
- lightseagreen: "20b2aa",
- lightskyblue: "87cefa",
- lightslategray: "789",
- lightslategrey: "789",
- lightsteelblue: "b0c4de",
- lightyellow: "ffffe0",
- lime: "0f0",
- limegreen: "32cd32",
- linen: "faf0e6",
- magenta: "f0f",
- maroon: "800000",
- mediumaquamarine: "66cdaa",
- mediumblue: "0000cd",
- mediumorchid: "ba55d3",
- mediumpurple: "9370db",
- mediumseagreen: "3cb371",
- mediumslateblue: "7b68ee",
- mediumspringgreen: "00fa9a",
- mediumturquoise: "48d1cc",
- mediumvioletred: "c71585",
- midnightblue: "191970",
- mintcream: "f5fffa",
- mistyrose: "ffe4e1",
- moccasin: "ffe4b5",
- navajowhite: "ffdead",
- navy: "000080",
- oldlace: "fdf5e6",
- olive: "808000",
- olivedrab: "6b8e23",
- orange: "ffa500",
- orangered: "ff4500",
- orchid: "da70d6",
- palegoldenrod: "eee8aa",
- palegreen: "98fb98",
- paleturquoise: "afeeee",
- palevioletred: "db7093",
- papayawhip: "ffefd5",
- peachpuff: "ffdab9",
- peru: "cd853f",
- pink: "ffc0cb",
- plum: "dda0dd",
- powderblue: "b0e0e6",
- purple: "800080",
- rebeccapurple: "663399",
- red: "f00",
- rosybrown: "bc8f8f",
- royalblue: "4169e1",
- saddlebrown: "8b4513",
- salmon: "fa8072",
- sandybrown: "f4a460",
- seagreen: "2e8b57",
- seashell: "fff5ee",
- sienna: "a0522d",
- silver: "c0c0c0",
- skyblue: "87ceeb",
- slateblue: "6a5acd",
- slategray: "708090",
- slategrey: "708090",
- snow: "fffafa",
- springgreen: "00ff7f",
- steelblue: "4682b4",
- tan: "d2b48c",
- teal: "008080",
- thistle: "d8bfd8",
- tomato: "ff6347",
- turquoise: "40e0d0",
- violet: "ee82ee",
- wheat: "f5deb3",
- white: "fff",
- whitesmoke: "f5f5f5",
- yellow: "ff0",
- yellowgreen: "9acd32"
- };
-
- // Make it easy to access colors via `hexNames[hex]`
- var hexNames = tinycolor.hexNames = flip(names);
-
-
- // Utilities
- // ---------
-
- // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
- function flip(o) {
- var flipped = { };
- for (var i in o) {
- if (o.hasOwnProperty(i)) {
- flipped[o[i]] = i;
- }
- }
- return flipped;
- }
-
- // Return a valid alpha value [0,1] with all invalid values being set to 1
- function boundAlpha(a) {
- a = parseFloat(a);
-
- if (isNaN(a) || a < 0 || a > 1) {
- a = 1;
- }
-
- return a;
- }
-
- // Take input from [0, n] and return it as [0, 1]
- function bound01(n, max) {
- if (isOnePointZero(n)) { n = "100%"; }
-
- var processPercent = isPercentage(n);
- n = mathMin(max, mathMax(0, parseFloat(n)));
-
- // Automatically convert percentage into number
- if (processPercent) {
- n = parseInt(n * max, 10) / 100;
- }
-
- // Handle floating point rounding errors
- if ((math.abs(n - max) < 0.000001)) {
- return 1;
- }
-
- // Convert into [0, 1] range if it isn't already
- return (n % max) / parseFloat(max);
- }
-
- // Force a number between 0 and 1
- function clamp01(val) {
- return mathMin(1, mathMax(0, val));
- }
-
- // Parse a base-16 hex value into a base-10 integer
- function parseIntFromHex(val) {
- return parseInt(val, 16);
- }
-
- // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
- //
- function isOnePointZero(n) {
- return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
- }
-
- // Check to see if string passed in is a percentage
- function isPercentage(n) {
- return typeof n === "string" && n.indexOf('%') != -1;
- }
-
- // Force a hex value to have 2 characters
- function pad2(c) {
- return c.length == 1 ? '0' + c : '' + c;
- }
-
- // Replace a decimal with it's percentage value
- function convertToPercentage(n) {
- if (n <= 1) {
- n = (n * 100) + "%";
- }
-
- return n;
- }
-
- // Converts a decimal to a hex value
- function convertDecimalToHex(d) {
- return Math.round(parseFloat(d) * 255).toString(16);
- }
- // Converts a hex value to a decimal
- function convertHexToDecimal(h) {
- return (parseIntFromHex(h) / 255);
- }
-
- var matchers = (function() {
-
- //
- var CSS_INTEGER = "[-\\+]?\\d+%?";
-
- //
- var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
-
- // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
- var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
-
- // Actual matching.
- // Parentheses and commas are optional, but not required.
- // Whitespace can take the place of commas or opening paren
- var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
- var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
-
- return {
- rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
- rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
- hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
- hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
- hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
- hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
- hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
- hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
- hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
- };
- })();
-
- // `stringInputToObject`
- // Permissive string parsing. Take in a number of formats, and output an object
- // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
- function stringInputToObject(color) {
-
- color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
- var named = false;
- if (names[color]) {
- color = names[color];
- named = true;
- }
- else if (color == 'transparent') {
- return { r: 0, g: 0, b: 0, a: 0, format: "name" };
- }
-
- // Try to match string input using regular expressions.
- // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
- // Just return an object and let the conversion functions handle that.
- // This way the result will be the same whether the tinycolor is initialized with string or object.
- var match;
- if ((match = matchers.rgb.exec(color))) {
- return { r: match[1], g: match[2], b: match[3] };
- }
- if ((match = matchers.rgba.exec(color))) {
- return { r: match[1], g: match[2], b: match[3], a: match[4] };
- }
- if ((match = matchers.hsl.exec(color))) {
- return { h: match[1], s: match[2], l: match[3] };
- }
- if ((match = matchers.hsla.exec(color))) {
- return { h: match[1], s: match[2], l: match[3], a: match[4] };
- }
- if ((match = matchers.hsv.exec(color))) {
- return { h: match[1], s: match[2], v: match[3] };
- }
- if ((match = matchers.hsva.exec(color))) {
- return { h: match[1], s: match[2], v: match[3], a: match[4] };
- }
- if ((match = matchers.hex8.exec(color))) {
- return {
- a: convertHexToDecimal(match[1]),
- r: parseIntFromHex(match[2]),
- g: parseIntFromHex(match[3]),
- b: parseIntFromHex(match[4]),
- format: named ? "name" : "hex8"
- };
- }
- if ((match = matchers.hex6.exec(color))) {
- return {
- r: parseIntFromHex(match[1]),
- g: parseIntFromHex(match[2]),
- b: parseIntFromHex(match[3]),
- format: named ? "name" : "hex"
- };
- }
- if ((match = matchers.hex3.exec(color))) {
- return {
- r: parseIntFromHex(match[1] + '' + match[1]),
- g: parseIntFromHex(match[2] + '' + match[2]),
- b: parseIntFromHex(match[3] + '' + match[3]),
- format: named ? "name" : "hex"
- };
- }
-
- return false;
- }
-
- window.tinycolor = tinycolor;
- })();
-
- $(function () {
- if ($.fn.spectrum.load) {
- $.fn.spectrum.processNativeColorInputs();
- }
- });
-
-});
diff --git a/modules/backend/formwidgets/colorpicker/partials/_colorpicker.htm b/modules/backend/formwidgets/colorpicker/partials/_colorpicker.htm
index d48211471d..93ac09a15e 100644
--- a/modules/backend/formwidgets/colorpicker/partials/_colorpicker.htm
+++ b/modules/backend/formwidgets/colorpicker/partials/_colorpicker.htm
@@ -1,42 +1,34 @@
-previewMode): ?>
-