Compare commits

...

69 Commits

Author SHA1 Message Date
github-actions 5f1d896cb7 Merge branch '2.11' into 2 2023-10-11 12:06:12 +00:00
Sabina Talipova 3f69c0d9f9
Merge pull request #262 from josephlewisnz/fix/trigger-has-changes
FIX Added the state changed checks and bubble up event
2023-10-09 15:42:28 +13:00
josephlewisnz 9257956255 fixed reference issue now that we've changed ref from createRef() 2023-10-05 16:02:59 +13:00
josephlewisnz f7af10a837 build 2023-10-05 16:02:59 +13:00
josephlewisnz c931056689 FIX Added the state changed checks and bubble up event - pending build 2023-10-05 16:02:59 +13:00
Guy Sartorelli f32c37cdd9
Merge branch '2.11' into 2 2023-04-27 13:43:39 +12:00
Guy Sartorelli 635d13c6bb
Merge branch '2.10' into 2.11 2023-04-26 13:48:56 +12:00
Guy Sartorelli 1b9f5a9388
Merge pull request #233 from w-huber/2
add performReadonlyTransformation
2023-04-26 13:47:27 +12:00
Guy Sartorelli 783ab4ef2b
Merge branch '2.11' into 2 2023-04-26 12:45:41 +12:00
Guy Sartorelli 158bc006a5
FIX Format value to string for readonly transformation
Co-authored-by: Werner Huber <w.huber@byro13.de>
2023-04-17 13:51:10 +12:00
Guy Sartorelli ef178ce534
MNT Revert erroneous dependency changes (#236) 2023-03-28 17:00:38 +13:00
Maxime Rainville d53ba1a9f8
Merge pull request #235 from creative-commoners/pulls/2/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:19:27 +13:00
Steve Boyd 9db4a2e55b MNT Use gha-dispatch-ci 2023-03-21 13:42:10 +13:00
Guy Sartorelli 9013377996
MNT Update development dependencies 2023-03-10 16:32:16 +13:00
Guy Sartorelli 41c412970d
MNT Update release dependencies 2023-03-10 16:32:13 +13:00
Guy Sartorelli d8be307ced
MNT Update development dependencies 2023-03-10 12:21:28 +13:00
Guy Sartorelli 42cf651cef
Merge pull request #225 from purplespider/2
Remove unnecessary react-select scss import that breaks other CMS styles
2023-02-23 09:29:23 +13:00
James Cocker 777e2e9e59
Remove unnecessary react-select scss import that breaks other CMS styles 2022-12-22 10:13:39 +00:00
Sabina Talipova b5e7981aac
Merge pull request #222 from creative-commoners/pulls/2/stop-using-depr
API Stop using deprecated API
2022-12-05 16:41:12 +13:00
dependabot[bot] d2a3a74e76
Merge pull request #223 from silverstripe/dependabot/npm_and_yarn/decode-uri-component-0.2.2 2022-12-04 08:27:25 +00:00
dependabot[bot] e7c618992c
Bump decode-uri-component from 0.2.0 to 0.2.2
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-03 15:33:32 +00:00
Steve Boyd dc932bd72d API Stop using deprecated API 2022-11-24 17:39:57 +13:00
Guy Sartorelli 8822b0f0b5
Merge pull request #221 from creative-commoners/pulls/2/depr-messages
API Update deprecations
2022-11-21 09:57:33 +13:00
Steve Boyd 442d9d8f32 API Update deprecations 2022-11-16 11:55:46 +13:00
Michal Kleiner 90f02b5602
Merge pull request #220 from thewebmen/fix/untrack-hidden-inputs 2022-11-08 10:24:44 +13:00
Rens 39759209d7 FIX Ignore hidden inputs for tracking unsaved changes 2022-11-07 15:07:20 +01:00
Steve Boyd 3d6a77fff1 Merge branch '2.9' into 2 2022-08-02 19:00:56 +12:00
Steve Boyd 8a4d2220af Merge branch '2.8' into 2.9 2022-08-02 19:00:53 +12:00
Guy Sartorelli ab52014523
Merge pull request #217 from creative-commoners/pulls/2.8/standardise-modules
MNT Standardise modules
2022-08-02 16:10:33 +12:00
Steve Boyd d45abda0dc MNT Standardise modules 2022-08-01 16:23:29 +12:00
Guy Sartorelli 86f2d63205
Merge pull request #216 from creative-commoners/pulls/2/update-js
MNT Use up­date-js action
2022-07-29 17:06:01 +12:00
Steve Boyd 1beccb2d96 MNT Use update-js action 2022-07-29 13:08:19 +12:00
Guy Sartorelli ab1e5b2ec0
Merge pull request #214 from bummzack/fix/has-one
fix: Deal with DataObjects or Int values
2022-07-29 10:35:37 +12:00
Roman Schmid 3f1d65e88e docs: Update readme with an example for `has_one` relations. 2022-07-28 10:48:33 +02:00
Roman Schmid 8da9f83cde fix: Fall back to the `ID` if no title is given 2022-07-26 10:47:13 +02:00
Roman Schmid 560a1398ab fix: Do not use `getField` to allow for field fallback values 2022-07-25 16:49:23 +02:00
Guy Sartorelli eed4afb710 Merge branch '2.9' into 2 2022-07-11 11:39:16 +12:00
Guy Sartorelli 0590385268 Merge branch '2.8' into 2.9 2022-07-11 11:39:04 +12:00
Guy Sartorelli 152ce93c51
Merge pull request #213 from creative-commoners/pulls/2.8/module-standards
MNT Use GitHub Actions CI
2022-07-05 16:32:31 +12:00
Steve Boyd 8d29af6158 MNT Use GitHub Actions CI 2022-07-05 16:10:46 +12:00
Roman Schmid e34d6e4898 fix: Use the title-field for has_one DataObject relations 2022-07-04 16:10:02 +02:00
Roman Schmid bc7bf5265b fix: Deal with DataObjects or Int values, so that the correct value is shown with has_one 2022-06-28 09:29:32 +02:00
Will Rossiter 527c5b2224
FIX Selected tag is not shown when isMutliple is false (#201)
* doc: add note about `setTitleField` (Fixes #153)

* NEW Add support for saving tag value into has_one components

FIX Value not shown if isMultiple is false (#195)

* fix: don't default to ID column as pgsql throws an error if comparing string value
2022-06-07 13:00:12 +12:00
Steve Boyd ee7c422855
Merge pull request #207 from silverstripe/pulls/2/update-js-1648772584
DEP Update JS dependencies
2022-05-31 11:01:54 +12:00
dependabot[bot] 8172abe158
Merge pull request #210 from silverstripe/dependabot/npm_and_yarn/async-2.6.4 2022-05-03 09:27:40 +00:00
dependabot[bot] 7a19b709f2
Bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-28 21:08:54 +00:00
Guy Sartorelli d486842a69
Merge pull request #209 from creative-commoners/pulls/2/php81
ENH PHP 8.1 compatibility
2022-04-26 17:57:37 +12:00
Steve Boyd 1b7dba124a ENH PHP 8.1 compatibility 2022-04-13 13:50:19 +12:00
Daniel Kliemsch 0a04e35825
ENH Make the write call of StringTagField::saveInto method configurable (#208)
* Make the immediate write call of StringTagField::saveInto method configurable
* Set immediate_write_enabled option in the class instead of config; add deprecation notice
* Add tests for the immediate write option
2022-04-04 20:27:49 +12:00
github-actions 1a4dfc5904 DEP Update JS dependencies 2022-04-01 00:23:04 +00:00
Maxime Rainville 8477911890
Merge pull request #205 from creative-commoners/pulls/2/update-js-deps
MNT Add update JS deps workflow
2022-03-23 14:17:06 +13:00
Steve Boyd a71f57d0fa MNT Add update JS deps workflow 2022-03-22 12:10:24 +13:00
dependabot[bot] 162f629db8
Merge pull request #204 from silverstripe/dependabot/npm_and_yarn/url-parse-1.5.10 2022-02-28 11:51:09 +00:00
dependabot[bot] 6de73bad08
Bump url-parse from 1.5.7 to 1.5.10
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.7 to 1.5.10.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.7...1.5.10)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-28 04:44:34 +00:00
dependabot[bot] a74e00568f
Merge pull request #203 from silverstripe/dependabot/npm_and_yarn/url-parse-1.5.7 2022-02-21 23:22:17 +00:00
Maxime Rainville e00d4f7c59
Merge pull request #202 from creative-commoners/pulls/2/php74
DEP Set PHP 7.4 as the minimum version
2022-02-18 22:09:31 +13:00
dependabot[bot] d7539bebb9
Bump url-parse from 1.5.3 to 1.5.7
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-18 06:19:36 +00:00
Steve Boyd 206740a9aa DEP Set PHP 7.4 as the minimum version 2022-02-10 17:39:53 +13:00
Will Rossiter de93200d7d
Merge pull request #199 from micschk/patch-1
Adding `$schemaComponent` to make react render correct component
2021-12-17 06:45:31 +13:00
Michael van Schaik 07868abfb5
Adding `$schemaComponent` to make react render correct component
Fixes #198
2021-11-24 15:41:10 +01:00
Maxime Rainville 187e66401e
Merge pull request #197 from creative-commoners/pulls/2/js
DEP Update JS dependencies
2021-11-24 17:21:14 +13:00
Steve Boyd adfe0f994e DEP Update JS dependencies 2021-11-22 16:51:14 +13:00
Steve Boyd cf9be969e6 Merge branch '2.7' into 2 2021-11-18 17:48:52 +13:00
Maxime Rainville aad83ac30d
Merge pull request #196 from creative-commoners/pulls/2/remove-js-prefix
MNT Remove js prefix from require statements
2021-11-16 10:49:57 +13:00
Steve Boyd d11abb5638 MNT Remove js prefix from require statements 2021-11-02 21:56:45 +13:00
Maxime Rainville 3f595cf09a
Merge pull request #194 from creative-commoners/pulls/2/sapphire-test-nine
API phpunit 9 support
2021-11-01 22:33:09 +13:00
Steve Boyd 5eae80abb3 API phpunit 9 support 2021-10-27 18:15:33 +13:00
Joseph Madden b81546d555 FIX Use existing source datalist if available before creating a new one (closes #171)
Fix for #171 - Issue when utilizing multiple of same parent class

Co-authored-by: Joe Madden <joe.madden@moosylvania.com>
2021-10-15 13:57:33 +13:00
Joseph Madden 55e03731bf
FIX Use existing source datalist if available before creating a new one
Fix for #171 - Issue when utilizing multiple of same parent class

Co-authored-by: Joe Madden <joe.madden@moosylvania.com>
2021-10-15 13:38:53 +13:00
25 changed files with 3908 additions and 1416 deletions

View File

@ -13,6 +13,7 @@ trim_trailing_whitespace = true
[{*.yml,package.json,*.js,*.scss}]
indent_size = 2
indent_style = space
quote_type = single
# The indent size used in the package.json file cannot be changed:
# https://github.com/npm/npm/pull/3180#issuecomment-16336516

11
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
name: CI
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Dispatch CI
on:
# At 12:00 PM UTC, only on Saturday and Sunday
schedule:
- cron: '0 12 * * 6,0'
jobs:
dispatch-ci:
name: Dispatch CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Dispatch CI
uses: silverstripe/gha-dispatch-ci@v1

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
schedule:
- cron: '50 10 4 * *'
jobs:
keepalive:
name: Keepalive
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Keepalive
uses: silverstripe/gha-keepalive@v1

17
.github/workflows/update-js.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Update JS
on:
workflow_dispatch:
# Run on a schedule of once per quarter
schedule:
- cron: '0 0 1 */3 *'
jobs:
update-js:
name: Update JS
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Update JS
uses: silverstripe/gha-update-js@v1

View File

@ -1,12 +0,0 @@
checks:
php: true
build:
nodes:
analysis:
tests:
override: [php-scrutinizer-run]
filter:
paths: ["src/*", "tests/*"]

View File

@ -1,4 +0,0 @@
version: ~> 1.0
import:
- silverstripe/silverstripe-travis-shared:config/provision/standard-jobs-range-npm.yml

166
README.md Normal file
View File

@ -0,0 +1,166 @@
# Tag Field
[![CI](https://github.com/silverstripe/silverstripe-tagfield/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-tagfield/actions/workflows/ci.yml)
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
Custom tag input field, for SilverStripe.
## Overview
![Screenshot](docs/en/screenshot.png)
Allows storing tags as a relationship, or comma-delimited strings.
Supports autocompletion with lazy-loading.
Note: The field is optimised for usage in the Silverstripe CMS UI.
The form field class itself can be used outside of the CMS,
but you'll need to build your own frontend to interpret the raw field data (`data-schema` attribute).
## Requirements
* Silverstripe 4.0
## Installing
```sh
$ composer require silverstripe/tagfield
```
## Using
### Relational Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $many_many = [
'BlogTags' => BlogTag::class
];
}
```
```php
use SilverStripe\ORM\DataObject;
class BlogTag extends DataObject
{
private static $db = [
'Title' => 'Varchar(200)',
];
private static $belongs_many_many = [
'BlogPosts' => BlogPost::class
];
}
```
```php
$field = TagField::create(
'BlogTags',
'Blog Tags',
BlogTag::get(),
$this->BlogTags()
)
->setShouldLazyLoad(true) // tags should be lazy loaded
->setCanCreate(true); // new tag DataObjects can be created
```
**Note:** This assumes you have imported the namespaces class, e.g. use
SilverStripe\TagField\TagField;
#### Has-One Relations
You can also use the TagField to select values for `has_one` relations.
Let's assume, that a `BlogPost` *has one* `BlogCategory`.
```php
class BlogCategory extends DataObject
{
private static $db = [
'Title' => 'Varchar(200)',
];
}
```
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $has_one = [
'BlogCategory' => BlogCategory::class
];
}
```
```php
$field = TagField::create(
'BlogCategoryID',
$this->fieldLabel('BlogCategory'),
BlogCategory::get()
)
->setIsMultiple(false)
->setCanCreate(true);
```
**Note:** We're using the `ID` suffix for the field-name (eg. `BlogCategoryID` instead of `BlogCategory`) and
only allow one value by setting `->setIsMultiple(false)`
### String Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $db = [
'Tags' => 'Text',
];
}
```
```php
$field = StringTagField::create(
'Tags',
'Tags',
['one', 'two'],
explode(',', $this->Tags)
);
$field->setShouldLazyLoad(true); // tags should be lazy loaded
```
You can find more in-depth documentation in [docs/en](docs/en/introduction.md).
## Using TagField with silverstripe-taxonomy
TagField assumes a `Title` field on objects. For classes without a `Title` field
use `setTitleField` to modify accordingly.
```php
$field = TagField::create(
'Tags',
'Blog Tags',
TaxonomyTerm::get(),
)
->setTitleField('Name');
```
## Versioning
This library follows [Semver](http://semver.org). According to Semver, you will
be able to upgrade to any minor or patch version of this library without any
breaking changes to the public API. Semver also requires that we clearly define
the public API for this library.
All methods, with `public` visibility, are part of the public API. All other
methods are not part of the public API. Where possible, we'll try to keep
`protected` methods backwards-compatible in minor/patch versions, but if you're
overriding methods then please test your work before upgrading.
## Reporting Issues
Please [create an
issue](http://github.com/silverstripe/silverstripe-tagfield/issues) for any bugs
you've found, or features you're missing.

View File

@ -1 +1 @@
!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(e,t,n){"use strict";var o=n("./client/src/boot/registerComponents.js"),r=function(e){return e&&e.__esModule?e:{default:e}}(o);window.document.addEventListener("DOMContentLoaded",function(){(0,r.default)()})},"./client/src/boot/registerComponents.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),s=o(r),i=n("./client/src/components/TagField.js"),a=o(i);t.default=function(){s.default.component.registerMany({TagField:a.default})}},"./client/src/bundles/bundle.js":function(e,t,n){"use strict";n("./client/src/legacy/entwine/TagField.js"),n("./client/src/boot/index.js")},"./client/src/components/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n={};for(var o in e)t.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(n[o]=e[o]);return n}function s(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.Component=void 0;var l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},h=function(){function e(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,n,o){return n&&e(t.prototype,n),o&&e(t,o),t}}(),c=n(1),f=o(c),p=n(5),d=o(p),m=n(3),v=o(m),y=n(2),b=o(y),g=n("./node_modules/url/url.js"),j=o(g),O=n("./node_modules/debounce-promise/dist/index.js"),w=o(O),x=n("./node_modules/prop-types/index.js"),_=o(x),C=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.isControlled()||(n.state={value:e.value}),n.handleChange=n.handleChange.bind(n),n.handleOnBlur=n.handleOnBlur.bind(n),n.getOptions=n.getOptions.bind(n),n.fetchOptions=(0,w.default)(n.fetchOptions,500),n}return u(t,e),h(t,[{key:"getOptions",value:function(e){var t=this.props,n=t.lazyLoad,o=t.options;return n?e?this.fetchOptions(e):Promise.resolve({options:[]}):Promise.resolve({options:o})}},{key:"handleChange",value:function(e){if(this.isControlled())return void this.props.onChange(e);this.setState({value:e})}},{key:"isControlled",value:function(){return"function"==typeof this.props.onChange}},{key:"handleOnBlur",value:function(){}},{key:"fetchOptions",value:function(e){var t=this.props,n=t.optionUrl,o=t.labelKey,r=t.valueKey,i=j.default.parse(n,!0);return i.query.term=e,(0,v.default)(j.default.format(i),{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return{options:e.items.map(function(e){var t;return t={},s(t,o,e.Title),s(t,r,e.Value),t})}})}},{key:"render",value:function(){var e=this.props,t=e.lazyLoad,n=e.options,o=e.creatable,s=r(e,["lazyLoad","options","creatable"]),i=t?{loadOptions:this.getOptions}:{options:n},a=d.default;return t&&o?a=d.default.AsyncCreatable:t?a=d.default.Async:o&&(a=d.default.Creatable),this.isControlled()||(s.value=this.state.value),f.default.createElement(a,l({},s,{onChange:this.handleChange,onBlur:this.handleOnBlur,inputProps:{className:"no-change-track"}},i))}}]),t}(c.Component);C.propTypes={name:_.default.string.isRequired,labelKey:_.default.string.isRequired,valueKey:_.default.string.isRequired,lazyLoad:_.default.bool,creatable:_.default.bool,multi:_.default.bool,disabled:_.default.bool,options:_.default.arrayOf(_.default.object),optionUrl:_.default.string,value:_.default.any,onChange:_.default.func,onBlur:_.default.func},C.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1,lazyLoad:!1,creatable:!1,multi:!1},t.Component=C,t.default=(0,b.default)(C)},"./client/src/legacy/entwine/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s=n(1),i=o(s),a=n(4),u=o(a),l=n(0);window.jQuery.entwine("ss",function(e){e(".js-injector-boot .ss-tag-field.entwine").entwine({onmatch:function(){var e=this,t=this.closest(".cms-content").attr("id"),n=t?{context:t}:{},o=(0,l.loadComponent)("TagField",n),s=r({},this.data("schema"),{onBlur:function(){e.parents(".cms-edit-form:first").trigger("change")}});u.default.render(i.default.createElement(o,r({noHolder:!0},s)),this[0])},onunmatch:function(){u.default.unmountComponentAtNode(this[0])}})})},"./node_modules/debounce-promise/dist/index.js":function(e,t,n){"use strict";function o(e){return"function"==typeof e?e():e}function r(){var e={};return e.promise=new Promise(function(t,n){e.resolve=t,e.reject=n}),e}e.exports=function(e){function t(){var t=a;clearTimeout(u),Promise.resolve(s.accumulate?e.call(this,l):e.apply(this,l[l.length-1])).then(t.resolve,t.reject),l=[],a=null}var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=void 0,a=void 0,u=void 0,l=[];return function(){var h=o(n),c=(new Date).getTime(),f=!i||c-i>h;i=c;for(var p=arguments.length,d=Array(p),m=0;m<p;m++)d[m]=arguments[m];if(f&&s.leading)return s.accumulate?Promise.resolve(e.call(this,[d])).then(function(e){return e[0]}):Promise.resolve(e.call.apply(e,[this].concat(d)));if(a?clearTimeout(u):a=r(),l.push(d),u=setTimeout(t.bind(this),h),s.accumulate){var v=l.length-1;return a.promise.then(function(e){return e[v]})}return a.promise}}},"./node_modules/node-libs-browser/node_modules/punycode/punycode.js":function(e,t,n){(function(e,o){var r;!function(o){function s(e){throw new RangeError(I[e])}function i(e,t){for(var n=e.length,o=[];n--;)o[n]=t(e[n]);return o}function a(e,t){var n=e.split("@"),o="";return n.length>1&&(o=n[0]+"@",e=n[1]),e=e.replace(A,"."),o+i(e.split("."),t).join(".")}function u(e){for(var t,n,o=[],r=0,s=e.length;r<s;)t=e.charCodeAt(r++),t>=55296&&t<=56319&&r<s?(n=e.charCodeAt(r++),56320==(64512&n)?o.push(((1023&t)<<10)+(1023&n)+65536):(o.push(t),r--)):o.push(t);return o}function l(e){return i(e,function(e){var t="";return e>65535&&(e-=65536,t+=S(e>>>10&1023|55296),e=56320|1023&e),t+=S(e)}).join("")}function h(e){return e-48<10?e-22:e-65<26?e-65:e-97<26?e-97:g}function c(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function f(e,t,n){var o=0;for(e=n?k(e/x):e>>1,e+=k(e/t);e>R*O>>1;o+=g)e=k(e/R);return k(o+(R+1)*e/(e+w))}function p(e){var t,n,o,r,i,a,u,c,p,d,m=[],v=e.length,y=0,w=C,x=_;for(n=e.lastIndexOf(P),n<0&&(n=0),o=0;o<n;++o)e.charCodeAt(o)>=128&&s("not-basic"),m.push(e.charCodeAt(o));for(r=n>0?n+1:0;r<v;){for(i=y,a=1,u=g;r>=v&&s("invalid-input"),c=h(e.charCodeAt(r++)),(c>=g||c>k((b-y)/a))&&s("overflow"),y+=c*a,p=u<=x?j:u>=x+O?O:u-x,!(c<p);u+=g)d=g-p,a>k(b/d)&&s("overflow"),a*=d;t=m.length+1,x=f(y-i,t,0==i),k(y/t)>b-w&&s("overflow"),w+=k(y/t),y%=t,m.splice(y++,0,w)}return l(m)}function d(e){var t,n,o,r,i,a,l,h,p,d,m,v,y,w,x,T=[];for(e=u(e),v=e.length,t=C,n=0,i=_,a=0;a<v;++a)(m=e[a])<128&&T.push(S(m));for(o=r=T.length,r&&T.push(P);o<v;){for(l=b,a=0;a<v;++a)(m=e[a])>=t&&m<l&&(l=m);for(y=o+1,l-t>k((b-n)/y)&&s("overflow"),n+=(l-t)*y,t=l,a=0;a<v;++a)if(m=e[a],m<t&&++n>b&&s("overflow"),m==t){for(h=n,p=g;d=p<=i?j:p>=i+O?O:p-i,!(h<d);p+=g)x=h-d,w=g-d,T.push(S(c(d+x%w,0))),h=k(x/w);T.push(S(c(h,0))),i=f(n,y,o==r),n=0,++o}++n,++t}return T.join("")}function m(e){return a(e,function(e){return T.test(e)?p(e.slice(4).toLowerCase()):e})}function v(e){return a(e,function(e){return q.test(e)?"xn--"+d(e):e})}var y,b=("object"==typeof t&&t&&t.nodeType,"object"==typeof e&&e&&e.nodeType,2147483647),g=36,j=1,O=26,w=38,x=700,_=72,C=128,P="-",T=/^xn--/,q=/[^\x20-\x7E]/,A=/[\x2E\u3002\uFF0E\uFF61]/g,I={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},R=g-j,k=Math.floor,S=String.fromCharCode;y={version:"1.4.1",ucs2:{decode:u,encode:l},decode:p,encode:d,toASCII:v,toUnicode:m},void 0!==(r=function(){return y}.call(t,n,t,e))&&(e.exports=r)}()}).call(t,n("./node_modules/webpack/buildin/module.js")(e),n("./node_modules/webpack/buildin/global.js"))},"./node_modules/prop-types/factoryWithThrowingShims.js":function(e,t,n){"use strict";function o(){}function r(){}var s=n("./node_modules/prop-types/lib/ReactPropTypesSecret.js");r.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,r,i){if(i!==s){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:o};return n.PropTypes=n,n}},"./node_modules/prop-types/index.js":function(e,t,n){e.exports=n("./node_modules/prop-types/factoryWithThrowingShims.js")()},"./node_modules/prop-types/lib/ReactPropTypesSecret.js":function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},"./node_modules/querystring-es3/decode.js":function(e,t,n){"use strict";function o(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,s){t=t||"&",n=n||"=";var i={};if("string"!=typeof e||0===e.length)return i;var a=/\+/g;e=e.split(t);var u=1e3;s&&"number"==typeof s.maxKeys&&(u=s.maxKeys);var l=e.length;u>0&&l>u&&(l=u);for(var h=0;h<l;++h){var c,f,p,d,m=e[h].replace(a,"%20"),v=m.indexOf(n);v>=0?(c=m.substr(0,v),f=m.substr(v+1)):(c=m,f=""),p=decodeURIComponent(c),d=decodeURIComponent(f),o(i,p)?r(i[p])?i[p].push(d):i[p]=[i[p],d]:i[p]=d}return i};var r=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},"./node_modules/querystring-es3/encode.js":function(e,t,n){"use strict";function o(e,t){if(e.map)return e.map(t);for(var n=[],o=0;o<e.length;o++)n.push(t(e[o],o));return n}var r=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,a){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?o(i(e),function(i){var a=encodeURIComponent(r(i))+n;return s(e[i])?o(e[i],function(e){return a+encodeURIComponent(r(e))}).join(t):a+encodeURIComponent(r(e[i]))}).join(t):a?encodeURIComponent(r(a))+n+encodeURIComponent(r(e)):""};var s=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},"./node_modules/querystring-es3/index.js":function(e,t,n){"use strict";t.decode=t.parse=n("./node_modules/querystring-es3/decode.js"),t.encode=t.stringify=n("./node_modules/querystring-es3/encode.js")},"./node_modules/url/url.js":function(e,t,n){"use strict";function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function r(e,t,n){if(e&&l.isObject(e)&&e instanceof o)return e;var r=new o;return r.parse(e,t,n),r}function s(e){return l.isString(e)&&(e=r(e)),e instanceof o?e.format():o.prototype.format.call(e)}function i(e,t){return r(e,!1,!0).resolve(t)}function a(e,t){return e?r(e,!1,!0).resolveObject(t):t}var u=n("./node_modules/node-libs-browser/node_modules/punycode/punycode.js"),l=n("./node_modules/url/util.js");t.parse=r,t.resolve=i,t.resolveObject=a,t.format=s,t.Url=o;var h=/^([a-z0-9.+-]+:)/i,c=/:[0-9]*$/,f=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,p=["<",">",'"',"`"," ","\r","\n","\t"],d=["{","}","|","\\","^","`"].concat(p),m=["'"].concat(d),v=["%","/","?",";","#"].concat(m),y=["/","?","#"],b=/^[+a-z0-9A-Z_-]{0,63}$/,g=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,j={javascript:!0,"javascript:":!0},O={javascript:!0,"javascript:":!0},w={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},x=n("./node_modules/querystring-es3/index.js");o.prototype.parse=function(e,t,n){if(!l.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),r=-1!==o&&o<e.indexOf("#")?"?":"#",s=e.split(r),i=/\\/g;s[0]=s[0].replace(i,"/"),e=s.join(r);var a=e;if(a=a.trim(),!n&&1===e.split("#").length){var c=f.exec(a);if(c)return this.path=a,this.href=a,this.pathname=c[1],c[2]?(this.search=c[2],this.query=t?x.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var p=h.exec(a);if(p){p=p[0];var d=p.toLowerCase();this.protocol=d,a=a.substr(p.length)}if(n||p||a.match(/^\/\/[^@\/]+@[^@\/]+/)){var _="//"===a.substr(0,2);!_||p&&O[p]||(a=a.substr(2),this.slashes=!0)}if(!O[p]&&(_||p&&!w[p])){for(var C=-1,P=0;P<y.length;P++){var T=a.indexOf(y[P]);-1!==T&&(-1===C||T<C)&&(C=T)}var q,A;A=-1===C?a.lastIndexOf("@"):a.lastIndexOf("@",C),-1!==A&&(q=a.slice(0,A),a=a.slice(A+1),this.auth=decodeURIComponent(q)),C=-1;for(var P=0;P<v.length;P++){var T=a.indexOf(v[P]);-1!==T&&(-1===C||T<C)&&(C=T)}-1===C&&(C=a.length),this.host=a.slice(0,C),a=a.slice(C),this.parseHost(),this.hostname=this.hostname||"";var I="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!I)for(var R=this.hostname.split(/\./),P=0,k=R.length;P<k;P++){var S=R[P];if(S&&!S.match(b)){for(var U="",E=0,F=S.length;E<F;E++)S.charCodeAt(E)>127?U+="x":U+=S[E];if(!U.match(b)){var L=R.slice(0,P),M=R.slice(P+1),N=S.match(g);N&&(L.push(N[1]),M.unshift(N[2])),M.length&&(a="/"+M.join(".")+a),this.hostname=L.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),I||(this.hostname=u.toASCII(this.hostname));var z=this.port?":"+this.port:"",B=this.hostname||"";this.host=B+z,this.href+=this.host,I&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!j[d])for(var P=0,k=m.length;P<k;P++){var K=m[P];if(-1!==a.indexOf(K)){var D=encodeURIComponent(K);D===K&&(D=escape(K)),a=a.split(K).join(D)}}var H=a.indexOf("#");-1!==H&&(this.hash=a.substr(H),a=a.slice(0,H));var W=a.indexOf("?");if(-1!==W?(this.search=a.substr(W),this.query=a.substr(W+1),t&&(this.query=x.parse(this.query)),a=a.slice(0,W)):t&&(this.search="",this.query={}),a&&(this.pathname=a),w[d]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var z=this.pathname||"",$=this.search||"";this.path=z+$}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",o=this.hash||"",r=!1,s="";this.host?r=e+this.host:this.hostname&&(r=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(r+=":"+this.port)),this.query&&l.isObject(this.query)&&Object.keys(this.query).length&&(s=x.stringify(this.query));var i=this.search||s&&"?"+s||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||w[t])&&!1!==r?(r="//"+(r||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):r||(r=""),o&&"#"!==o.charAt(0)&&(o="#"+o),i&&"?"!==i.charAt(0)&&(i="?"+i),n=n.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),i=i.replace("#","%23"),t+r+n+i+o},o.prototype.resolve=function(e){return this.resolveObject(r(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if(l.isString(e)){var t=new o;t.parse(e,!1,!0),e=t}for(var n=new o,r=Object.keys(this),s=0;s<r.length;s++){var i=r[s];n[i]=this[i]}if(n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol){for(var a=Object.keys(e),u=0;u<a.length;u++){var h=a[u];"protocol"!==h&&(n[h]=e[h])}return w[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n}if(e.protocol&&e.protocol!==n.protocol){if(!w[e.protocol]){for(var c=Object.keys(e),f=0;f<c.length;f++){var p=c[f];n[p]=e[p]}return n.href=n.format(),n}if(n.protocol=e.protocol,e.host||O[e.protocol])n.pathname=e.pathname;else{for(var d=(e.pathname||"").split("/");d.length&&!(e.host=d.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==d[0]&&d.unshift(""),d.length<2&&d.unshift(""),n.pathname=d.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var m=n.pathname||"",v=n.search||"";n.path=m+v}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var y=n.pathname&&"/"===n.pathname.charAt(0),b=e.host||e.pathname&&"/"===e.pathname.charAt(0),g=b||y||n.host&&e.pathname,j=g,x=n.pathname&&n.pathname.split("/")||[],d=e.pathname&&e.pathname.split("/")||[],_=n.protocol&&!w[n.protocol];if(_&&(n.hostname="",n.port=null,n.host&&(""===x[0]?x[0]=n.host:x.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===d[0]?d[0]=e.host:d.unshift(e.host)),e.host=null),g=g&&(""===d[0]||""===x[0])),b)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,x=d;else if(d.length)x||(x=[]),x.pop(),x=x.concat(d),n.search=e.search,n.query=e.query;else if(!l.isNullOrUndefined(e.search)){if(_){n.hostname=n.host=x.shift();var C=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");C&&(n.auth=C.shift(),n.host=n.hostname=C.shift())}return n.search=e.search,n.query=e.query,l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!x.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var P=x.slice(-1)[0],T=(n.host||e.host||x.length>1)&&("."===P||".."===P)||""===P,q=0,A=x.length;A>=0;A--)P=x[A],"."===P?x.splice(A,1):".."===P?(x.splice(A,1),q++):q&&(x.splice(A,1),q--);if(!g&&!j)for(;q--;q)x.unshift("..");!g||""===x[0]||x[0]&&"/"===x[0].charAt(0)||x.unshift(""),T&&"/"!==x.join("/").substr(-1)&&x.push("");var I=""===x[0]||x[0]&&"/"===x[0].charAt(0);if(_){n.hostname=n.host=I?"":x.length?x.shift():"";var C=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");C&&(n.auth=C.shift(),n.host=n.hostname=C.shift())}return g=g||n.host&&x.length,g&&!I&&x.unshift(""),x.length?n.pathname=x.join("/"):(n.pathname=null,n.path=null),l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var e=this.host,t=c.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},"./node_modules/url/util.js":function(e,t,n){"use strict";e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},"./node_modules/webpack/buildin/global.js":function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},"./node_modules/webpack/buildin/module.js":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},0:function(e,t){e.exports=Injector},1:function(e,t){e.exports=React},2:function(e,t){e.exports=FieldHolder},3:function(e,t){e.exports=IsomorphicFetch},4:function(e,t){e.exports=ReactDom},5:function(e,t){e.exports=ReactSelect}});
!function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(e,t,n){"use strict";var o=n("./client/src/boot/registerComponents.js"),r=function(e){return e&&e.__esModule?e:{default:e}}(o);window.document.addEventListener("DOMContentLoaded",function(){(0,r.default)()})},"./client/src/boot/registerComponents.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),s=o(r),i=n("./client/src/components/TagField.js"),a=o(i);t.default=function(){s.default.component.registerMany({TagField:a.default})}},"./client/src/bundles/bundle.js":function(e,t,n){"use strict";n("./client/src/legacy/entwine/TagField.js"),n("./client/src/boot/index.js")},"./client/src/components/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n={};for(var o in e)t.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(n[o]=e[o]);return n}function s(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function u(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.Component=void 0;var l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},c=function(){function e(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,n,o){return n&&e(t.prototype,n),o&&e(t,o),t}}(),f=n(1),p=o(f),d=n(5),m=o(d),v=n(3),y=o(v),b=n(2),g=o(b),j=n("./node_modules/url/url.js"),O=o(j),w=n("./node_modules/debounce-promise/dist/index.js"),C=o(w),x=n("./node_modules/prop-types/index.js"),_=o(x),S=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.selectComponentRef=null,n.setSelectComponentRef=function(e){n.selectComponentRef=e},n.state={initalState:e.value?e.value:[],hasChanges:!1},n.isControlled()||(n.state=h({},n.state,{value:e.value})),n.handleChange=n.handleChange.bind(n),n.handleOnBlur=n.handleOnBlur.bind(n),n.getOptions=n.getOptions.bind(n),n.fetchOptions=(0,C.default)(n.fetchOptions,500),n}return u(t,e),c(t,[{key:"componentDidUpdate",value:function(e,t){if(t.hasChanges!==this.state.hasChanges){var n=this.selectComponentRef.select.wrapper,o=new Event("change",{bubbles:!0});n.dispatchEvent(o)}}},{key:"getOptions",value:function(e){var t=this.props,n=t.lazyLoad,o=t.options;return n?e?this.fetchOptions(e):Promise.resolve({options:[]}):Promise.resolve({options:o})}},{key:"handleChange",value:function(e){if(this.setState({hasChanges:!1}),JSON.stringify(this.state.initalState)!==JSON.stringify(e)&&this.setState({hasChanges:!0}),this.isControlled())return void this.props.onChange(e);this.setState({value:e})}},{key:"isControlled",value:function(){return"function"==typeof this.props.onChange}},{key:"handleOnBlur",value:function(){}},{key:"fetchOptions",value:function(e){var t=this.props,n=t.optionUrl,o=t.labelKey,r=t.valueKey,i=O.default.parse(n,!0);return i.query.term=e,(0,y.default)(O.default.format(i),{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return{options:e.items.map(function(e){var t;return t={},s(t,o,e.Title),s(t,r,e.Value),s(t,"Selected",e.Selected),t})}})}},{key:"render",value:function(){var e=this.props,t=e.lazyLoad,n=e.options,o=e.creatable,s=r(e,["lazyLoad","options","creatable"]),i=t?{loadOptions:this.getOptions}:{options:n},a=m.default;if(t&&o?a=m.default.AsyncCreatable:t?a=m.default.Async:o&&(a=m.default.Creatable),this.isControlled()||(s.value=this.state.value),!s.multi&&s.value&&Object.keys(s.value).length>0){var u=s.value[Object.keys(s.value)[0]];"object"===(void 0===u?"undefined":l(u))&&(s.value=u)}var c=this.state.hasChanges?"":"no-change-track";return p.default.createElement(a,h({},s,{onChange:this.handleChange,onBlur:this.handleOnBlur},i,{className:c,ref:this.setSelectComponentRef}))}}]),t}(f.Component);S.propTypes={name:_.default.string.isRequired,labelKey:_.default.string.isRequired,valueKey:_.default.string.isRequired,lazyLoad:_.default.bool,creatable:_.default.bool,multi:_.default.bool,disabled:_.default.bool,options:_.default.arrayOf(_.default.object),optionUrl:_.default.string,value:_.default.any,onChange:_.default.func,onBlur:_.default.func},S.defaultProps={labelKey:"Title",valueKey:"Value",disabled:!1,lazyLoad:!1,creatable:!1,multi:!1},t.Component=S,t.default=(0,g.default)(S)},"./client/src/legacy/entwine/TagField.js":function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s=n(1),i=o(s),a=n(4),u=o(a),l=n(0);window.jQuery.entwine("ss",function(e){e(".js-injector-boot .ss-tag-field.entwine").entwine({onmatch:function(){var e=this,t=this.closest(".cms-content").attr("id"),n=t?{context:t}:{},o=(0,l.loadComponent)("TagField",n),s=r({},this.data("schema"),{onBlur:function(){e.parents(".cms-edit-form:first").trigger("change")}});u.default.render(i.default.createElement(o,r({noHolder:!0},s)),this[0])},onunmatch:function(){u.default.unmountComponentAtNode(this[0])}}),e(".cms-edit-form").entwine({getChangeTrackerOptions:function(){var t=void 0===this.entwineData("ChangeTrackerOptions"),n=this._super();return t&&(n=e.extend({},n),n.ignoreFieldSelector+=", .ss-tag-field .no-change-track :input",this.setChangeTrackerOptions(n)),n}})})},"./node_modules/debounce-promise/dist/index.js":function(e,t,n){"use strict";function o(e){return"function"==typeof e?e():e}function r(){var e={};return e.promise=new Promise(function(t,n){e.resolve=t,e.reject=n}),e}e.exports=function(e){function t(){var t=a;clearTimeout(u),Promise.resolve(s.accumulate?e.call(this,l):e.apply(this,l[l.length-1])).then(t.resolve,t.reject),l=[],a=null}var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=void 0,a=void 0,u=void 0,l=[];return function(){var h=o(n),c=(new Date).getTime(),f=!i||c-i>h;i=c;for(var p=arguments.length,d=Array(p),m=0;m<p;m++)d[m]=arguments[m];if(f&&s.leading)return s.accumulate?Promise.resolve(e.call(this,[d])).then(function(e){return e[0]}):Promise.resolve(e.call.apply(e,[this].concat(d)));if(a?clearTimeout(u):a=r(),l.push(d),u=setTimeout(t.bind(this),h),s.accumulate){var v=l.length-1;return a.promise.then(function(e){return e[v]})}return a.promise}}},"./node_modules/node-libs-browser/node_modules/punycode/punycode.js":function(e,t,n){(function(e,o){var r;!function(o){function s(e){throw new RangeError(R[e])}function i(e,t){for(var n=e.length,o=[];n--;)o[n]=t(e[n]);return o}function a(e,t){var n=e.split("@"),o="";return n.length>1&&(o=n[0]+"@",e=n[1]),e=e.replace(k,"."),o+i(e.split("."),t).join(".")}function u(e){for(var t,n,o=[],r=0,s=e.length;r<s;)t=e.charCodeAt(r++),t>=55296&&t<=56319&&r<s?(n=e.charCodeAt(r++),56320==(64512&n)?o.push(((1023&t)<<10)+(1023&n)+65536):(o.push(t),r--)):o.push(t);return o}function l(e){return i(e,function(e){var t="";return e>65535&&(e-=65536,t+=I(e>>>10&1023|55296),e=56320|1023&e),t+=I(e)}).join("")}function h(e){return e-48<10?e-22:e-65<26?e-65:e-97<26?e-97:g}function c(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function f(e,t,n){var o=0;for(e=n?A(e/C):e>>1,e+=A(e/t);e>q*O>>1;o+=g)e=A(e/q);return A(o+(q+1)*e/(e+w))}function p(e){var t,n,o,r,i,a,u,c,p,d,m=[],v=e.length,y=0,w=_,C=x;for(n=e.lastIndexOf(S),n<0&&(n=0),o=0;o<n;++o)e.charCodeAt(o)>=128&&s("not-basic"),m.push(e.charCodeAt(o));for(r=n>0?n+1:0;r<v;){for(i=y,a=1,u=g;r>=v&&s("invalid-input"),c=h(e.charCodeAt(r++)),(c>=g||c>A((b-y)/a))&&s("overflow"),y+=c*a,p=u<=C?j:u>=C+O?O:u-C,!(c<p);u+=g)d=g-p,a>A(b/d)&&s("overflow"),a*=d;t=m.length+1,C=f(y-i,t,0==i),A(y/t)>b-w&&s("overflow"),w+=A(y/t),y%=t,m.splice(y++,0,w)}return l(m)}function d(e){var t,n,o,r,i,a,l,h,p,d,m,v,y,w,C,T=[];for(e=u(e),v=e.length,t=_,n=0,i=x,a=0;a<v;++a)(m=e[a])<128&&T.push(I(m));for(o=r=T.length,r&&T.push(S);o<v;){for(l=b,a=0;a<v;++a)(m=e[a])>=t&&m<l&&(l=m);for(y=o+1,l-t>A((b-n)/y)&&s("overflow"),n+=(l-t)*y,t=l,a=0;a<v;++a)if(m=e[a],m<t&&++n>b&&s("overflow"),m==t){for(h=n,p=g;d=p<=i?j:p>=i+O?O:p-i,!(h<d);p+=g)C=h-d,w=g-d,T.push(I(c(d+C%w,0))),h=A(C/w);T.push(I(c(h,0))),i=f(n,y,o==r),n=0,++o}++n,++t}return T.join("")}function m(e){return a(e,function(e){return T.test(e)?p(e.slice(4).toLowerCase()):e})}function v(e){return a(e,function(e){return P.test(e)?"xn--"+d(e):e})}var y,b=("object"==typeof t&&t&&t.nodeType,"object"==typeof e&&e&&e.nodeType,2147483647),g=36,j=1,O=26,w=38,C=700,x=72,_=128,S="-",T=/^xn--/,P=/[^\x20-\x7E]/,k=/[\x2E\u3002\uFF0E\uFF61]/g,R={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},q=g-j,A=Math.floor,I=String.fromCharCode;y={version:"1.4.1",ucs2:{decode:u,encode:l},decode:p,encode:d,toASCII:v,toUnicode:m},void 0!==(r=function(){return y}.call(t,n,t,e))&&(e.exports=r)}()}).call(t,n("./node_modules/webpack/buildin/module.js")(e),n("./node_modules/webpack/buildin/global.js"))},"./node_modules/prop-types/factoryWithThrowingShims.js":function(e,t,n){"use strict";function o(){}function r(){}var s=n("./node_modules/prop-types/lib/ReactPropTypesSecret.js");r.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,r,i){if(i!==s){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:o};return n.PropTypes=n,n}},"./node_modules/prop-types/index.js":function(e,t,n){e.exports=n("./node_modules/prop-types/factoryWithThrowingShims.js")()},"./node_modules/prop-types/lib/ReactPropTypesSecret.js":function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},"./node_modules/querystring-es3/decode.js":function(e,t,n){"use strict";function o(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,s){t=t||"&",n=n||"=";var i={};if("string"!=typeof e||0===e.length)return i;var a=/\+/g;e=e.split(t);var u=1e3;s&&"number"==typeof s.maxKeys&&(u=s.maxKeys);var l=e.length;u>0&&l>u&&(l=u);for(var h=0;h<l;++h){var c,f,p,d,m=e[h].replace(a,"%20"),v=m.indexOf(n);v>=0?(c=m.substr(0,v),f=m.substr(v+1)):(c=m,f=""),p=decodeURIComponent(c),d=decodeURIComponent(f),o(i,p)?r(i[p])?i[p].push(d):i[p]=[i[p],d]:i[p]=d}return i};var r=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},"./node_modules/querystring-es3/encode.js":function(e,t,n){"use strict";function o(e,t){if(e.map)return e.map(t);for(var n=[],o=0;o<e.length;o++)n.push(t(e[o],o));return n}var r=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,a){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?o(i(e),function(i){var a=encodeURIComponent(r(i))+n;return s(e[i])?o(e[i],function(e){return a+encodeURIComponent(r(e))}).join(t):a+encodeURIComponent(r(e[i]))}).join(t):a?encodeURIComponent(r(a))+n+encodeURIComponent(r(e)):""};var s=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},"./node_modules/querystring-es3/index.js":function(e,t,n){"use strict";t.decode=t.parse=n("./node_modules/querystring-es3/decode.js"),t.encode=t.stringify=n("./node_modules/querystring-es3/encode.js")},"./node_modules/url/url.js":function(e,t,n){"use strict";function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function r(e,t,n){if(e&&l.isObject(e)&&e instanceof o)return e;var r=new o;return r.parse(e,t,n),r}function s(e){return l.isString(e)&&(e=r(e)),e instanceof o?e.format():o.prototype.format.call(e)}function i(e,t){return r(e,!1,!0).resolve(t)}function a(e,t){return e?r(e,!1,!0).resolveObject(t):t}var u=n("./node_modules/node-libs-browser/node_modules/punycode/punycode.js"),l=n("./node_modules/url/util.js");t.parse=r,t.resolve=i,t.resolveObject=a,t.format=s,t.Url=o;var h=/^([a-z0-9.+-]+:)/i,c=/:[0-9]*$/,f=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,p=["<",">",'"',"`"," ","\r","\n","\t"],d=["{","}","|","\\","^","`"].concat(p),m=["'"].concat(d),v=["%","/","?",";","#"].concat(m),y=["/","?","#"],b=/^[+a-z0-9A-Z_-]{0,63}$/,g=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,j={javascript:!0,"javascript:":!0},O={javascript:!0,"javascript:":!0},w={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},C=n("./node_modules/querystring-es3/index.js");o.prototype.parse=function(e,t,n){if(!l.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),r=-1!==o&&o<e.indexOf("#")?"?":"#",s=e.split(r),i=/\\/g;s[0]=s[0].replace(i,"/"),e=s.join(r);var a=e;if(a=a.trim(),!n&&1===e.split("#").length){var c=f.exec(a);if(c)return this.path=a,this.href=a,this.pathname=c[1],c[2]?(this.search=c[2],this.query=t?C.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var p=h.exec(a);if(p){p=p[0];var d=p.toLowerCase();this.protocol=d,a=a.substr(p.length)}if(n||p||a.match(/^\/\/[^@\/]+@[^@\/]+/)){var x="//"===a.substr(0,2);!x||p&&O[p]||(a=a.substr(2),this.slashes=!0)}if(!O[p]&&(x||p&&!w[p])){for(var _=-1,S=0;S<y.length;S++){var T=a.indexOf(y[S]);-1!==T&&(-1===_||T<_)&&(_=T)}var P,k;k=-1===_?a.lastIndexOf("@"):a.lastIndexOf("@",_),-1!==k&&(P=a.slice(0,k),a=a.slice(k+1),this.auth=decodeURIComponent(P)),_=-1;for(var S=0;S<v.length;S++){var T=a.indexOf(v[S]);-1!==T&&(-1===_||T<_)&&(_=T)}-1===_&&(_=a.length),this.host=a.slice(0,_),a=a.slice(_),this.parseHost(),this.hostname=this.hostname||"";var R="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!R)for(var q=this.hostname.split(/\./),S=0,A=q.length;S<A;S++){var I=q[S];if(I&&!I.match(b)){for(var U="",E=0,F=I.length;E<F;E++)I.charCodeAt(E)>127?U+="x":U+=I[E];if(!U.match(b)){var L=q.slice(0,S),N=q.slice(S+1),M=I.match(g);M&&(L.push(M[1]),N.unshift(M[2])),N.length&&(a="/"+N.join(".")+a),this.hostname=L.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),R||(this.hostname=u.toASCII(this.hostname));var z=this.port?":"+this.port:"",B=this.hostname||"";this.host=B+z,this.href+=this.host,R&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!j[d])for(var S=0,A=m.length;S<A;S++){var K=m[S];if(-1!==a.indexOf(K)){var D=encodeURIComponent(K);D===K&&(D=escape(K)),a=a.split(K).join(D)}}var H=a.indexOf("#");-1!==H&&(this.hash=a.substr(H),a=a.slice(0,H));var W=a.indexOf("?");if(-1!==W?(this.search=a.substr(W),this.query=a.substr(W+1),t&&(this.query=C.parse(this.query)),a=a.slice(0,W)):t&&(this.search="",this.query={}),a&&(this.pathname=a),w[d]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var z=this.pathname||"",$=this.search||"";this.path=z+$}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",o=this.hash||"",r=!1,s="";this.host?r=e+this.host:this.hostname&&(r=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(r+=":"+this.port)),this.query&&l.isObject(this.query)&&Object.keys(this.query).length&&(s=C.stringify(this.query));var i=this.search||s&&"?"+s||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||w[t])&&!1!==r?(r="//"+(r||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):r||(r=""),o&&"#"!==o.charAt(0)&&(o="#"+o),i&&"?"!==i.charAt(0)&&(i="?"+i),n=n.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),i=i.replace("#","%23"),t+r+n+i+o},o.prototype.resolve=function(e){return this.resolveObject(r(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if(l.isString(e)){var t=new o;t.parse(e,!1,!0),e=t}for(var n=new o,r=Object.keys(this),s=0;s<r.length;s++){var i=r[s];n[i]=this[i]}if(n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol){for(var a=Object.keys(e),u=0;u<a.length;u++){var h=a[u];"protocol"!==h&&(n[h]=e[h])}return w[n.protocol]&&n.hostname&&!n.pathname&&(n.path=n.pathname="/"),n.href=n.format(),n}if(e.protocol&&e.protocol!==n.protocol){if(!w[e.protocol]){for(var c=Object.keys(e),f=0;f<c.length;f++){var p=c[f];n[p]=e[p]}return n.href=n.format(),n}if(n.protocol=e.protocol,e.host||O[e.protocol])n.pathname=e.pathname;else{for(var d=(e.pathname||"").split("/");d.length&&!(e.host=d.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==d[0]&&d.unshift(""),d.length<2&&d.unshift(""),n.pathname=d.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var m=n.pathname||"",v=n.search||"";n.path=m+v}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var y=n.pathname&&"/"===n.pathname.charAt(0),b=e.host||e.pathname&&"/"===e.pathname.charAt(0),g=b||y||n.host&&e.pathname,j=g,C=n.pathname&&n.pathname.split("/")||[],d=e.pathname&&e.pathname.split("/")||[],x=n.protocol&&!w[n.protocol];if(x&&(n.hostname="",n.port=null,n.host&&(""===C[0]?C[0]=n.host:C.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===d[0]?d[0]=e.host:d.unshift(e.host)),e.host=null),g=g&&(""===d[0]||""===C[0])),b)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,C=d;else if(d.length)C||(C=[]),C.pop(),C=C.concat(d),n.search=e.search,n.query=e.query;else if(!l.isNullOrUndefined(e.search)){if(x){n.hostname=n.host=C.shift();var _=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");_&&(n.auth=_.shift(),n.host=n.hostname=_.shift())}return n.search=e.search,n.query=e.query,l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n}if(!C.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var S=C.slice(-1)[0],T=(n.host||e.host||C.length>1)&&("."===S||".."===S)||""===S,P=0,k=C.length;k>=0;k--)S=C[k],"."===S?C.splice(k,1):".."===S?(C.splice(k,1),P++):P&&(C.splice(k,1),P--);if(!g&&!j)for(;P--;P)C.unshift("..");!g||""===C[0]||C[0]&&"/"===C[0].charAt(0)||C.unshift(""),T&&"/"!==C.join("/").substr(-1)&&C.push("");var R=""===C[0]||C[0]&&"/"===C[0].charAt(0);if(x){n.hostname=n.host=R?"":C.length?C.shift():"";var _=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@");_&&(n.auth=_.shift(),n.host=n.hostname=_.shift())}return g=g||n.host&&C.length,g&&!R&&C.unshift(""),C.length?n.pathname=C.join("/"):(n.pathname=null,n.path=null),l.isNull(n.pathname)&&l.isNull(n.search)||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var e=this.host,t=c.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},"./node_modules/url/util.js":function(e,t,n){"use strict";e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},"./node_modules/webpack/buildin/global.js":function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},"./node_modules/webpack/buildin/module.js":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},0:function(e,t){e.exports=Injector},1:function(e,t){e.exports=React},2:function(e,t){e.exports=FieldHolder},3:function(e,t){e.exports=IsomorphicFetch},4:function(e,t){e.exports=ReactDom},5:function(e,t){e.exports=ReactSelect}});

View File

@ -1 +1 @@
@-o-keyframes Select-animation-spin{to{-o-transform:rotate(1turn);transform:rotate(1turn)}}.Select{position:relative}.Select input::-webkit-contacts-auto-fill-button,.Select input::-webkit-credentials-auto-fill-button{display:none!important}.Select input::-ms-clear,.Select input::-ms-reveal{display:none!important}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{-webkit-box-shadow:none;box-shadow:none}.Select.is-searchable.is-focused:not(.is-open)>.Select-control,.Select.is-searchable.is-open>.Select-control{cursor:text}.Select.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#b3b3b3 #ccc #d9d9d9}.Select.is-open>.Select-control .Select-arrow{top:-2px;border-color:transparent transparent #999;border-width:0 5px 5px}.Select.is-focused>.Select-control{background:#fff}.Select.is-focused:not(.is-open)>.Select-control{border-color:#08c #0099e6 #0099e6;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1),0 0 5px -1px fade(#08c,50%);box-shadow:inset 0 1px 2px rgba(0,0,0,.1),0 0 5px -1px fade(#08c,50%)}.Select.has-value.is-clearable.Select--single>.Select-control .Select-value{padding-right:42px}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label,.Select.has-value.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover{color:#08c;outline:none;text-decoration:underline}.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus{background:#fff}.Select.has-value.is-pseudo-focused .Select-input{opacity:0}.Select.is-open .Select-arrow,.Select .Select-arrow-zone:hover>.Select-arrow{border-top-color:#666}.Select.Select--rtl{direction:rtl;text-align:right}.Select-control{background-color:#fff;border-color:#d9d9d9 #ccc #b3b3b3;border-radius:4px;border:1px solid #ccc;color:#333;cursor:default;display:table;border-spacing:0;border-collapse:separate;height:36px;outline:none;overflow:hidden;position:relative;width:100%}.Select-control:hover{-webkit-box-shadow:0 1px 0 rgba(0,0,0,.06);box-shadow:0 1px 0 rgba(0,0,0,.06)}.Select-control .Select-input:focus{outline:none;background:#fff}.Select--single>.Select-control .Select-value,.Select-placeholder{bottom:0;color:#aaa;left:0;line-height:34px;padding-left:10px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none transparent;border:0 none;-webkit-box-shadow:none;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:none;line-height:17px;padding:8px 0 12px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.Select-control:not(.is-searchable)>.Select-input{outline:none}.Select-loading-zone{cursor:pointer;display:table-cell;text-align:center}.Select-loading,.Select-loading-zone{position:relative;vertical-align:middle;width:16px}.Select-loading{-webkit-animation:Select-animation-spin .4s infinite linear;-o-animation:Select-animation-spin .4s infinite linear;animation:Select-animation-spin .4s infinite linear;height:16px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn .2s;-o-animation:Select-animation-fadeIn .2s;animation:Select-animation-fadeIn .2s;color:#999;cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:17px}.Select-clear-zone:hover{color:#d0021b}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select--multi .Select-multi-value-wrapper{display:inline-block}.Select .Select-aria-only{position:absolute;display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0,0,0,0);overflow:hidden;float:left}.Select-arrow-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:25px;padding-right:5px}.Select--rtl .Select-arrow-zone{padding-right:0;padding-left:5px}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0;position:relative}@-webkit-keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}@-o-keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{0%{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.06);box-shadow:0 1px 0 rgba(0,0,0,.06);-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;left:0;top:100%;width:100%;z-index:1000;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option{-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#fff;color:#666;cursor:pointer;display:block;padding:8px 10px}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;color:#333}.Select-option.is-focused{background-color:#f2f9fc;color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{-webkit-box-sizing:border-box;box-sizing:border-box;color:#999;cursor:default;display:block;padding:8px 10px}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.Select--rtl .Select-input{margin-left:0;margin-right:10px}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#f2f9fc;border-radius:2px;border:1px solid #c9e6f2;color:#08c;display:inline-block;font-size:.9em;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#08c;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c9e6f2;padding:1px 5px 3px}.Select--multi .Select-value-icon:focus,.Select--multi .Select-value-icon:hover{background-color:#ddeff7;color:#0077b3}.Select--multi .Select-value-icon:active{background-color:#c9e6f2}.Select--multi.Select--rtl .Select-value{margin-left:0;margin-right:5px}.Select--multi.Select--rtl .Select-value-icon{border-right:none;border-left:1px solid #c9e6f2}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:active,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:hover{background-color:#fcfcfc}@keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn);-o-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}.ss-tag-field .Select--multi .Select-value{margin-top:3px}
.ss-tag-field .Select--multi .Select-value{margin-top:3px}

View File

@ -1,2 +1,2 @@
require('legacy/entwine/TagField.js');
require('legacy/entwine/TagField');
require('boot');

View File

@ -10,8 +10,20 @@ class TagField extends Component {
constructor(props) {
super(props);
this.selectComponentRef = null;
this.setSelectComponentRef = ref => {
this.selectComponentRef = ref;
};
this.state = {
initalState: props.value ? props.value : [],
hasChanges: false,
};
if (!this.isControlled()) {
this.state = {
...this.state,
value: props.value,
};
}
@ -22,6 +34,14 @@ class TagField extends Component {
this.fetchOptions = debounce(this.fetchOptions, 500);
}
componentDidUpdate(previousProps, previousState) {
if (previousState.hasChanges !== this.state.hasChanges) {
const element = this.selectComponentRef.select.wrapper;
const event = new Event('change', { bubbles: true });
element.dispatchEvent(event);
}
}
/**
* Get the options that should be shown to the user for this tagfield, optionally filtering by the
* given string input
@ -50,13 +70,23 @@ class TagField extends Component {
* @param {string} value
*/
handleChange(value) {
this.setState({
hasChanges: false
});
if (JSON.stringify(this.state.initalState) !== JSON.stringify(value)) {
this.setState({
hasChanges: true
});
}
if (this.isControlled()) {
this.props.onChange(value);
return;
}
this.setState({
value
value,
});
}
@ -76,9 +106,7 @@ class TagField extends Component {
*
* @link https://github.com/JedWatson/react-select/issues/805
*/
handleOnBlur() {
}
handleOnBlur() {}
/**
* Initiate a request to fetch options, optionally using the given string as a filter.
@ -94,20 +122,17 @@ class TagField extends Component {
return fetch(url.format(fetchURL), { credentials: 'same-origin' })
.then((response) => response.json())
.then((json) => ({
options: json.items.map(item => ({
options: json.items.map((item) => ({
[labelKey]: item.Title,
[valueKey]: item.Value,
}))
Selected: item.Selected,
})),
}));
}
render() {
const {
lazyLoad,
options,
creatable,
...passThroughAttributes
} = this.props;
const { lazyLoad, options, creatable, ...passThroughAttributes } =
this.props;
const optionAttributes = lazyLoad
? { loadOptions: this.getOptions }
@ -128,13 +153,30 @@ class TagField extends Component {
passThroughAttributes.value = this.state.value;
}
// if this is a single select then we just need the first value
if (!passThroughAttributes.multi && passThroughAttributes.value) {
if (Object.keys(passThroughAttributes.value).length > 0) {
const value =
passThroughAttributes.value[
Object.keys(passThroughAttributes.value)[0]
];
if (typeof value === 'object') {
passThroughAttributes.value = value;
}
}
}
const changedClassName = this.state.hasChanges ? '' : 'no-change-track';
return (
<SelectComponent
{...passThroughAttributes}
onChange={this.handleChange}
onBlur={this.handleOnBlur}
inputProps={{ className: 'no-change-track' }}
{...optionAttributes}
className={changedClassName}
ref={this.setSelectComponentRef}
/>
);
}

View File

@ -1,5 +1,3 @@
@import "~react-select/scss/default";
.ss-tag-field .Select--multi .Select-value {
margin-top: 3px;
}

View File

@ -31,4 +31,26 @@ window.jQuery.entwine('ss', ($) => {
ReactDOM.unmountComponentAtNode(this[0]);
}
});
$('.cms-edit-form').entwine({
getChangeTrackerOptions() {
// Figure out if we're still returning the default value
const isDefault = (this.entwineData('ChangeTrackerOptions') === undefined);
// Get the current options
let opts = this._super();
if (isDefault) {
// If it is the default then...
// clone the object (so we don't modify the original),
opts = $.extend({}, opts);
// modify it,
opts.ignoreFieldSelector += ', .ss-tag-field .no-change-track :input';
// then set the clone as the value on this element
// (so next call to this method gets this same clone)
this.setChangeTrackerOptions(opts);
}
return opts;
}
});
});

View File

@ -19,11 +19,12 @@
"issues": "http://github.com/silverstripe/silverstripe-tagfield/issues"
},
"require": {
"silverstripe/framework": "^4.3",
"php": "^7.4 || ^8.0",
"silverstripe/framework": "^4.10",
"silverstripe/versioned": "^1.3"
},
"require-dev": {
"sminnee/phpunit": "^5.7",
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.0"
},
"autoload": {

View File

@ -68,3 +68,17 @@ class BlogPost extends DataObject
In the above code example, the options available (whether lazy loaded or not) would be "one" and "two", and the
user would be able to create new options. Whichever tags are chosen would be stored in the BlogPost's `Tags` field
as a comma-delimited string.
## Using TagField with silverstripe-taxonomy
TagField assumes a `Title` field on objects. For classes without a `Title` field
use `setTitleField` to modify accordingly.
```php
$field = TagField::create(
'Tags',
'Blog Tags',
TaxonomyTerm::get(),
)
->setTitleField('Name');
```

View File

@ -57,7 +57,7 @@
]
},
"devDependencies": {
"@silverstripe/eslint-config": "^0.0.5",
"@silverstripe/eslint-config": "^0.0.6",
"@silverstripe/webpack-config": "^0.10.0",
"@storybook/react": "^3.2.19",
"babel-jest": "^23.6.0",

View File

@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
<testsuite name="Default">
<directory>tests/</directory>
</testsuite>
<testsuites>
<testsuite name="Default">
<directory>tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src/</directory>

109
readme.md
View File

@ -1,109 +0,0 @@
# Tag Field
Custom tag input field, for SilverStripe.
[![Build Status](https://api.travis-ci.com/silverstripe/silverstripe-tagfield.svg?branch=2)](https://travis-ci.com/silverstripe/silverstripe-tagfield)
[![Code Quality](http://img.shields.io/scrutinizer/g/silverstripe/silverstripe-tagfield.svg?style=flat)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-tagfield)
[![Code coverage](https://codecov.io/gh/silverstripe/silverstripe-tagfield/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-tagfield)
[![Version](http://img.shields.io/packagist/v/silverstripe/tagfield.svg?style=flat)](https://packagist.org/packages/silverstripe/tagfield)
[![License](http://img.shields.io/packagist/l/silverstripe/tagfield.svg?style=flat)](license.md)
[![SilverStripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
## Overview
![Screenshot](docs/en/screenshot.png)
Allows storing tags as a relationship, or comma-delimited strings.
Supports autocompletion with lazy-loading.
Note: The field is optimised for usage in the SilverStripe CMS UI.
The form field class itself can be used outside of the CMS,
but you'll need to build your own frontend to interpret the raw field data (`data-schema` attribute).
## Requirements
* SilverStripe 4.0
## Installing
```sh
$ composer require silverstripe/tagfield
```
## Using
### Relational Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $many_many = [
'BlogTags' => BlogTag::class
];
}
```
```php
use SilverStripe\ORM\DataObject;
class BlogTag extends DataObject
{
private static $db = [
'Title' => 'Varchar(200)',
];
private static $belongs_many_many = [
'BlogPosts' => BlogPost::class
];
}
```
```php
$field = TagField::create(
'BlogTags',
'Blog Tags',
BlogTag::get(),
$this->BlogTags()
)
->setShouldLazyLoad(true) // tags should be lazy loaded
->setCanCreate(true); // new tag DataObjects can be created
```
**Note:** This assumes you have imported the namespaces class, e.g. use SilverStripe\TagField\TagField;
### String Tags
```php
use SilverStripe\ORM\DataObject;
class BlogPost extends DataObject
{
private static $db = [
'Tags' => 'Text',
];
}
```
```php
$field = StringTagField::create(
'Tags',
'Tags',
['one', 'two'],
explode(',', $this->Tags)
);
$field->setShouldLazyLoad(true); // tags should be lazy loaded
```
You can find more in-depth documentation in [docs/en](docs/en/introduction.md).
## Versioning
This library follows [Semver](http://semver.org). According to Semver, you will be able to upgrade to any minor or patch version of this library without any breaking changes to the public API. Semver also requires that we clearly define the public API for this library.
All methods, with `public` visibility, are part of the public API. All other methods are not part of the public API. Where possible, we'll try to keep `protected` methods backwards-compatible in minor/patch versions, but if you're overriding methods then please test your work before upgrading.
## Reporting Issues
Please [create an issue](http://github.com/silverstripe/silverstripe-tagfield/issues) for any bugs you've found, or features you're missing.

View File

@ -6,6 +6,7 @@ use Iterator;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\Validator;
use SilverStripe\ORM\ArrayList;
@ -33,6 +34,13 @@ class StringTagField extends DropdownField
'suggest',
];
/**
* @var bool Triggers a write call within the saveInto function if enabled
*
* @deprecated 3.0.0 Will be removed without equivalent functionality to replace it
*/
private static $immediate_write_enabled = true;
/**
* @var bool
*/
@ -58,6 +66,9 @@ class StringTagField extends DropdownField
*/
protected $isMultiple = true;
/** @skipUpgrade */
protected $schemaComponent = 'TagField';
/**
* @return bool
*/
@ -244,12 +255,12 @@ class StringTagField extends DropdownField
public function setValue($value, $source = null)
{
if (is_string($value)) {
$value = explode(',', $value);
$value = explode(',', $value ?? '');
}
if ($source instanceof DataObject) {
$name = $this->getName();
$value = explode(',', $source->$name);
$value = explode(',', $source->$name ?? '');
}
if ($source instanceof SS_List) {
@ -260,7 +271,7 @@ class StringTagField extends DropdownField
$value = [];
}
return parent::setValue(array_filter($value));
return parent::setValue(array_filter($value ?? []));
}
public function saveInto(DataObjectInterface $record)
@ -270,7 +281,13 @@ class StringTagField extends DropdownField
$name = $this->getName();
$record->$name = $this->dataValue();
$record->write();
$immediate_write_enabled = Deprecation::withNoReplacement(function () {
return self::config()->get('immediate_write_enabled');
});
if ($immediate_write_enabled) {
$record->write();
}
}
/**
@ -302,6 +319,49 @@ class StringTagField extends DropdownField
return $response;
}
/**
* Get or create tag with the given value
*
* @param string $term
* @return DataObject|bool
*/
protected function getOrCreateTag($term)
{
// Check if existing record can be found
$source = $this->getSourceList();
if (!$source) {
return false;
}
$titleField = $this->getTitleField();
$record = $source
->filter($titleField, $term)
->first();
if ($record) {
return $record;
}
// Create new instance if not yet saved
if ($this->getCanCreate()) {
$dataClass = $source->dataClass();
$record = Injector::inst()->create($dataClass);
if (is_array($term)) {
$term = $term['Value'];
}
$record->{$titleField} = $term;
$record->write();
if ($source instanceof SS_List) {
$source->add($record);
}
return $record;
}
return false;
}
/**
* Returns array of arrays representing tags that partially match the given search term
*
@ -315,7 +375,7 @@ class StringTagField extends DropdownField
/** @var ArrayData $tag */
$tagValue = $tag->Value;
// Map into a distinct list (prevent duplicates)
if (stripos($tagValue, $term) !== false && !array_key_exists($tagValue, $items)) {
if (stripos($tagValue ?? '', $term ?? '') !== false && !array_key_exists($tagValue, $items ?? [])) {
$items[$tagValue] = [
'id' => $tag->Title,
'text' => $tag->Value,
@ -323,7 +383,7 @@ class StringTagField extends DropdownField
}
}
// @todo do we actually need lazy loading limits for StringTagField?
return array_slice(array_values($items), 0, $this->getLazyLoadItemLimit());
return array_slice(array_values($items ?? []), 0, $this->getLazyLoadItemLimit());
}
/**
@ -356,4 +416,11 @@ class StringTagField extends DropdownField
return $this;
}
public function performReadonlyTransformation()
{
$field = parent::performReadonlyTransformation();
$field->setValue(implode(', ', $this->Value()));
return $field;
}
}

View File

@ -13,6 +13,7 @@ use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\FieldType\DBMultiEnum;
use SilverStripe\ORM\Relation;
use SilverStripe\ORM\SS_List;
use SilverStripe\View\ArrayData;
@ -221,10 +222,16 @@ class TagField extends MultiSelectField
public function getSchemaDataDefaults()
{
$options = $this->getOptions(true);
$name = $this->getName();
if ($this->getIsMultiple() && strpos($name, '[') === false) {
$name .= '[]';
}
$schema = array_merge(
parent::getSchemaDataDefaults(),
[
'name' => $this->getName() . '[]',
'name' => $name,
'lazyLoad' => $this->getShouldLazyLoad(),
'creatable' => $this->getCanCreate(),
'multi' => $this->getIsMultiple(),
@ -234,7 +241,7 @@ class TagField extends MultiSelectField
);
if (!$this->getShouldLazyLoad()) {
$schema['options'] = array_values($this->getOptions()->toNestedArray());
$schema['options'] = array_values($this->getOptions()->toNestedArray() ?? []);
} else {
$schema['optionUrl'] = $this->getSuggestURL();
}
@ -265,31 +272,49 @@ class TagField extends MultiSelectField
$dataClass = $source->dataClass();
$values = $this->Value();
$values = $this->getValueArray();
// If we have no values and we only want selected options we can bail here
if (empty($values) && $onlySelected) {
return ArrayList::create();
}
$titleField = $this->getTitleField();
// Convert an array of values into a datalist of options
if (!$values instanceof SS_List) {
if (is_array($values) && !empty($values)) {
$values = DataList::create($dataClass)
->filter($this->getTitleField(), $values);
// if values is an array of Ids then we should look up via
// ID.
if (array_filter($values, 'is_int')) {
$queryField = 'ID';
} else {
$queryField = $titleField;
}
if (is_a($source, DataList::class)) {
$values = $source->filterAny([
$queryField => $values
]);
} else {
$values = DataList::create($dataClass)
->filterAny([
$queryField => $values
]);
}
} else {
$values = ArrayList::create();
}
}
// Prep a function to parse a dataobject into an option
$addOption = function (DataObject $item) use ($options, $values) {
$titleField = $this->getTitleField();
$addOption = function (DataObject $item) use ($options, $values, $titleField) {
$option = $item->$titleField;
$options->push(ArrayData::create([
'Title' => $option,
'Value' => $option,
'Selected' => (bool) $values->find('ID', $item->ID)
'Selected' => (bool) $values->find($titleField, $option)
]));
};
@ -300,36 +325,10 @@ class TagField extends MultiSelectField
}
$source->each($addOption);
return $options;
}
/**
* {@inheritdoc}
*/
public function setValue($value, $source = null)
{
if ($source instanceof DataObject) {
$name = $this->getName();
if ($source->hasMethod($name)) {
$value = $source->$name()->column($this->getTitleField());
}
}
if (!is_array($value)) {
return parent::setValue($value);
}
// Safely map php / react-select values to flat list
$values = [];
foreach ($value as $item) {
if ($item) {
$values[] = isset($item['Value']) ? $item['Value'] : $item;
}
}
return parent::setValue($values);
}
/**
* Gets the source array if required
@ -346,6 +345,7 @@ class TagField extends MultiSelectField
return $this->source;
}
/**
* Intercept DataList source
*
@ -364,30 +364,63 @@ class TagField extends MultiSelectField
return $this;
}
/**
* @param DataObject|DataObjectInterface $record DataObject to save data into
* @throws Exception
*/
public function getAttributes()
{
$name = $this->getName();
if ($this->getIsMultiple() && strpos($name, '[') === false) {
$name .= '[]';
}
return array_merge(
parent::getAttributes(),
[
'name' => $this->getName() . '[]',
'name' => $name,
'style' => 'width: 100%',
'data-schema' => json_encode($this->getSchemaData()),
]
);
}
protected function getListValues($values): array
{
if (empty($values)) {
return [];
}
if (is_array($values)) {
return $values;
}
if ($values instanceof SS_List) {
return $values->column($this->getTitleField());
}
if ($values instanceof DataObject && $values->exists()) {
return [$values->{$this->getTitleField()} ?? $values->ID];
}
if (is_int($values)) {
return [$values];
}
return [trim((string) $values)];
}
/**
* {@inheritdoc}
*/
public function saveInto(DataObjectInterface $record)
{
$name = $this->getName();
$titleField = $this->getTitleField();
$values = $this->Value();
$values = $this->getValueArray();
$ids = [];
@ -395,49 +428,70 @@ class TagField extends MultiSelectField
$values = [];
}
if (empty($record) || empty($titleField)) {
if (empty($record)) {
return;
}
if (!$record->hasMethod($name)) {
throw new Exception(
sprintf("%s does not have a %s method", get_class($record), $name)
);
}
/** @var Relation $relation */
$relation = $record->$name();
$relation = $record->hasMethod($name) ? $record->$name() : null;
foreach ($values as $key => $value) {
// Get or create record
$record = $this->getOrCreateTag($value);
if ($record) {
$ids[] = $record->ID;
$values[$key] = $record->Title;
$tag = $this->getOrCreateTag($value);
if ($tag) {
$ids[] = $tag->ID;
$values[$key] = $tag->Title;
}
}
$relation->setByIDList(array_filter($ids));
if ($relation instanceof Relation) {
// Save ids into relation
$relation->setByIDList(array_filter($ids ?? []));
} elseif ($record->hasField($name)) {
if ($this->getIsMultiple()) {
if ($record->obj($name) instanceof DBMultiEnum) {
// Save dataValue into field... a CSV for DBMultiEnum
$record->$name = $this->csvEncode(array_filter(array_values($values)));
} else {
// ... JSON-encoded string for other fields
$record->$name = $this->stringEncode(array_filter(array_values($values)));
}
} else {
if (isset($tag) && $tag->ID) {
$record->$name = $tag->ID;
} else {
$record->$name = null;
}
}
}
}
/**
* Get or create tag with the given value
*
* @param string $term
* @param string $value
*
* @return DataObject|bool
*/
protected function getOrCreateTag($term)
protected function getOrCreateTag($value)
{
if (is_array($value)) {
$value = $value['Value'] ?? '';
}
// Check if existing record can be found
$source = $this->getSourceList();
$titleField = $this->getTitleField();
if (!$source) {
return false;
}
$titleField = $this->getTitleField();
$record = $source
->filter($titleField, $term)
->filter($titleField, $value)
->first();
if ($record) {
return $record;
}
@ -446,16 +500,13 @@ class TagField extends MultiSelectField
if ($this->getCanCreate()) {
$dataClass = $source->dataClass();
$record = Injector::inst()->create($dataClass);
if (is_array($term)) {
$term = $term['Value'];
}
$record->{$titleField} = $term;
$record->{$titleField} = $value;
$record->write();
if ($source instanceof SS_List) {
$source->add($record);
}
return $record;
}
@ -502,14 +553,15 @@ class TagField extends MultiSelectField
// Map into a distinct list
$items = [];
$titleField = $this->getTitleField();
foreach ($query->map('ID', $titleField) as $id => $title) {
foreach ($query->map('ID', $titleField)->values() as $title) {
$items[$title] = [
'Title' => $title,
'Value' => $title,
];
}
return array_values($items);
return array_values($items ?? []);
}
/**
@ -563,4 +615,14 @@ class TagField extends MultiSelectField
return $data;
}
public function getSchemaDataType(): string
{
if ($this->getIsMultiple()) {
return self::SCHEMA_DATA_TYPE_MULTISELECT;
}
return self::SCHEMA_DATA_TYPE_SINGLESELECT;
}
}

View File

@ -2,17 +2,14 @@
namespace SilverStripe\TagField\Tests;
use PHPUnit_Framework_TestCase;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\TagField\StringTagField;
use SilverStripe\TagField\Tests\Stub\StringTagFieldTestBlogPost;
use SilverStripe\Dev\Deprecation;
/**
* @mixin PHPUnit_Framework_TestCase
*/
class StringTagFieldTest extends SapphireTest
{
/**
@ -65,6 +62,44 @@ class StringTagFieldTest extends SapphireTest
$this->assertEquals('Tag1,Tag2', $record->Tags);
}
public function testImmediateWriteEnabled()
{
if (Deprecation::isEnabled()) {
$this->markTestSkipped('Test calls deprecated code');
}
$record = $this->getNewStringTagFieldTestBlogPost('BlogPost1');
$record->write();
StringTagField::config()->set('immediate_write_enabled', true);
$field = new StringTagField('Tags');
$field->setValue(['Tag1', 'Tag2']);
$field->saveInto($record);
$this->assertEquals('Tag1,Tag2', StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
}
public function testImmediateWriteDisabled()
{
if (Deprecation::isEnabled()) {
$this->markTestSkipped('Test calls deprecated code');
}
$record = $this->getNewStringTagFieldTestBlogPost('BlogPost1');
$record->write();
StringTagField::config()->set('immediate_write_enabled', false);
$field = new StringTagField('Tags');
$field->setValue(['Tag1', 'Tag2']);
$field->saveInto($record);
$this->assertNull(StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
$record->write();
$this->assertEquals('Tag1,Tag2', StringTagFieldTestBlogPost::get()->byID($record->ID)->Tags);
}
public function testItSuggestsTags()
{
$field = new StringTagField('SomeField', 'Some field', ['Tag1', 'Tag2'], []);
@ -133,7 +168,7 @@ class StringTagFieldTest extends SapphireTest
$schema = $field->getSchemaDataDefaults();
$this->assertTrue($schema['lazyLoad']);
$this->assertTrue($schema['creatable']);
$this->assertContains('suggest', $schema['optionUrl']);
$this->assertStringContainsString('suggest', $schema['optionUrl']);
}
public function testSchemaIsAddedToAttributes()
@ -143,6 +178,28 @@ class StringTagFieldTest extends SapphireTest
$this->assertNotEmpty($attributes['data-schema']);
}
public function testPerformReadonlyTransformation()
{
$field = new StringTagField('Tags');
$field->setSource(['Test1', 'Test2', 'Test3']);
// Ensure a single value can be rendered
$field->setValue(['Test2']);
$field_readonly = $field->performReadonlyTransformation();
$this->assertEquals('Test2', $field_readonly->Value());
// Ensure multiple valid values are rendered
$field->setValue(['Test1', 'Test2']);
$field_readonly = $field->performReadonlyTransformation();
$this->assertEquals('Test1, Test2', $field_readonly->Value());
// Ensure an value not in the source array is still rendered
// (because e.g. in history view it must have been a valid value when it was set)
$field->setValue(['Test', 'Test1']);
$field_readonly = $field->performReadonlyTransformation();
$this->assertEquals('Test, Test1', $field_readonly->Value());
}
/**
* @param array $parameters
* @return HTTPRequest

View File

@ -18,4 +18,8 @@ class TagFieldTestBlogPost extends DataObject implements TestOnly
private static $many_many = [
'Tags' => TagFieldTestBlogTag::class
];
private static $has_one = [
'PrimaryTag' => TagFieldTestBlogTag::class
];
}

View File

@ -2,7 +2,6 @@
namespace SilverStripe\TagField\Tests;
use PHPUnit_Framework_TestCase;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
@ -15,9 +14,6 @@ use SilverStripe\TagField\Tests\Stub\TagFieldTestBlogPost;
use SilverStripe\TagField\Tests\Stub\TagFieldTestBlogTag;
use SilverStripe\TagField\Tests\Stub\TagFieldTestController;
/**
* @mixin PHPUnit_Framework_TestCase
*/
class TagFieldTest extends SapphireTest
{
/**
@ -40,12 +36,40 @@ class TagFieldTest extends SapphireTest
$field->setValue(['Tag3', 'Tag4']);
$field->saveInto($record);
$record->write();
$this->compareExpectedAndActualTags(
['Tag3', 'Tag4'],
$record
);
}
public function testItSavesToHasOne()
{
$record = $this->getNewTagFieldTestBlogPost('BlogPost1');
$tag = new TagFieldTestBlogTag();
$tag->Title = 'Foobar';
$tag->write();
$field = new TagField('PrimaryTagID', '', new DataList(TagFieldTestBlogTag::class));
$field->setIsMultiple(false);
$field->setValue('Foobar');
$field->saveInto($record);
$record->write();
$this->assertEquals($tag->ID, $record->PrimaryTagID, 'The tag is saved to a has_one');
$tag = new TagFieldTestBlogTag();
$tag->Title = 'Foobarbaz';
$tag->write();
$field->setValue(['Foobarbaz']);
$field->saveInto($record);
$record->write();
$this->assertEquals($tag->ID, $record->PrimaryTagID, 'The tag is saved to a has_one');
}
/**
* @param string $name
*
@ -76,7 +100,7 @@ class TagFieldTest extends SapphireTest
*/
protected function compareTagLists(array $expected, DataList $actualSource)
{
$actual = array_values($actualSource->map('ID', 'Title')->toArray());
$actual = array_values($actualSource->map('ID', 'Title')->toArray() ?? []);
sort($expected);
sort($actual);
@ -131,7 +155,7 @@ class TagFieldTest extends SapphireTest
$record
);
}
public function testSavesReactTags()
{
$record = $this->getNewTagFieldTestBlogPost('BlogPost1');
@ -311,7 +335,7 @@ class TagFieldTest extends SapphireTest
$this->objFromFixture(TagFieldTestBlogPost::class, 'BlogPost2')
);
$ids = TagFieldTestBlogTag::get()->column('Title');
$ids = TagFieldTestBlogTag::get()->column('ID');
$this->assertEquals($field->Value(), $ids);
}
@ -361,7 +385,9 @@ class TagFieldTest extends SapphireTest
$tag = TagFieldTestBlogTag::get()->first();
$this->assertNotEmpty($tag);
$this->assertEquals('New Tag', $tag->Title);
$record = TagFieldTestBlogPost::get()->byID($record->ID);
$this->assertEquals(
$tag->ID,
$record->Tags()->first()->ID
@ -390,6 +416,7 @@ class TagFieldTest extends SapphireTest
$selectedTag = $source->First();
$unselectedTag = $source->Last();
$value = $source->filter('ID', $selectedTag->ID); // arbitrary subset
$field = new TagField('TestField', null, $source, $value);
// Not the cleanest way to assert this, but getOptions() is protected
@ -436,7 +463,7 @@ class TagFieldTest extends SapphireTest
$schema = $field->getSchemaDataDefaults();
$this->assertTrue($schema['lazyLoad']);
$this->assertTrue($schema['creatable']);
$this->assertContains('suggest', $schema['optionUrl']);
$this->assertStringContainsString('suggest', $schema['optionUrl']);
}
public function testSchemaIsAddedToAttributes()

4460
yarn.lock

File diff suppressed because it is too large Load Diff