diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..b8cb438 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,3 @@ +service_name: travis-ci +coverage_clover: tests/_output/coverage.xml +json_path: tests/_output/coveralls-upload.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..436d305 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,35 @@ +# The following files should be ignored: +# - unencrypted sensitive data +# - files that are not checked into the code repository +# - files that are not relevant to the Docker build + +.git +.idea +bin +docker-output +docs +img + +tests/_output +!tests/_output/.gitignore +tests/_support/_generated +!tests/_support/_generated/.gitignore + +vendor +!vendor/autoload.php +!vendor/ivome/graphql-relay-php/src +!vendor/webonyx/graphql-php/src +!vendor/composer + +.dockerignore +.gitignore +.travis.yml +CODE_OF_CONDUCT.md +CONTRIBUTING.md +Dockerfile* +ISSUE_TEMPLATE.md +LICENSE +PULL_REQUEST_TEMPLATE.md +README.md +readme.txt +run-docker*.sh diff --git a/.env b/.env new file mode 100644 index 0000000..ec17662 --- /dev/null +++ b/.env @@ -0,0 +1,31 @@ +DB_NAME=wordpress +DB_HOST=app_db +DB_USER=wordpress +DB_PASSWORD=wordpress +WP_TABLE_PREFIX=wp_ +WP_URL=http://localhost +WP_DOMAIN=localhost +ADMIN_EMAIL=admin@example.com +ADMIN_USERNAME=admin +ADMIN_PASSWORD=password +ADMIN_PATH=/wp-admin + +TEST_DB_NAME=wpgraphql_acf_tests +TEST_DB_HOST=127.0.0.1 +TEST_DB_USER=wordpress +TEST_DB_PASSWORD=wordpress +TEST_WP_TABLE_PREFIX=wp_ + +SKIP_DB_CREATE=false +TEST_WP_ROOT_FOLDER=/tmp/wordpress +TEST_ADMIN_EMAIL=admin@wp.test + +TESTS_DIR=tests +TESTS_OUTPUT=tests/_output +TESTS_DATA=tests/_data +TESTS_SUPPORT=tests/_support +TESTS_ENVS=tests/_envs + +#WPGRAPHQL_VERSION=v1.3.3 +SKIP_TESTS_CLEANUP=1 +SUITES=wpunit diff --git a/.env.dist b/.env.dist deleted file mode 100644 index 12c16af..0000000 --- a/.env.dist +++ /dev/null @@ -1,23 +0,0 @@ -# Shared -TEST_DB_NAME="wptests" -TEST_DB_HOST="127.0.0.1" -TEST_DB_USER="root" -TEST_DB_PASSWORD="" - -# Install script -WP_VERSION=latest -SKIP_DB_CREATE=false - -# Codeception -WP_ROOT_FOLDER="/tmp/wp-graphql-acf/wordpress" -WP_ADMIN_PATH="/wp-admin" -DB_NAME="wptests" -DB_HOST="127.0.0.1" -DB_USER="root" -DB_PASSWORD="" -WP_TABLE_PREFIX="wp_" -WP_URL="http://wp.test" -WP_DOMAIN="wp.test" -ADMIN_EMAIL="admin@wp.test" -ADMIN_USERNAME="admin" -ADMIN_PASSWORD="password" diff --git a/.github/workflows/testing-integration.yml b/.github/workflows/testing-integration.yml new file mode 100644 index 0000000..38ca063 --- /dev/null +++ b/.github/workflows/testing-integration.yml @@ -0,0 +1,112 @@ +name: Testing Integration + +on: + push: + branches: + - develop + - master + pull_request: + branches: + - develop + - master + paths: + - '**.php' + - '!docs/**' + +jobs: + continuous_integration: + runs-on: ubuntu-latest + strategy: + matrix: + php: [ '7.3', '7.4' ] + wordpress: [ '5.6', '5.5.3', '5.4.2' ] + include: + - php: '7.4' + wordpress: '5.6' + coverage: 1 + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v1.3.5' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v1.2.6' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v1.1.8' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v1.0.5' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v0.15.5' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v0.14.0' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v0.13.3' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v0.12.3' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v0.11.0' + - php: '7.4' + wordpress: '5.6' + WPGRAPHQL_VERSION: 'v0.11.0' + - php: '7.4' + wordpress: '5.5.3' + - php: '7.4' + wordpress: '5.4.2' + - php: '7.3' + wordpress: '5.6' + - php: '7.3' + wordpress: '5.5.3' + - php: '7.3' + wordpress: '5.4.2' + fail-fast: false + name: WordPress ${{ matrix.wordpress }} on PHP ${{ matrix.php }} + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json, mbstring, zip, unzip + + - name: Get Composer Cache Directory + id: composercache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install + + - name: Build "testing" Docker Image + env: + PHP_VERSION: ${{ matrix.php }} + WP_VERSION: ${{ matrix.wordpress }} + run: composer build-test + + - name: Run Tests w/ Docker. + env: + COVERAGE: ${{ matrix.coverage }} + USING_XDEBUG: ${{ matrix.coverage }} + DEBUG: ${{ matrix.debug }} + SKIP_TESTS_CLEANUP: ${{ matrix.coverage }} + run: composer run-test + + - name: Push Codecoverage to Coveralls.io + if: ${{ matrix.coverage == 1 }} + env: + COVERALLS_RUN_LOCALLY: 1 + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: vendor/bin/php-coveralls -v diff --git a/.gitignore b/.gitignore index 75c6b57..e6c29ad 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ vendor/* vendor/composer/installed.json vendor/composer/*/ composer.lock +.log diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 985814d..0000000 --- a/.travis.yml +++ /dev/null @@ -1,67 +0,0 @@ -sudo: false -dist: trusty - -language: php - -notifications: - email: - on_success: never - on_failure: change - -branches: - only: - - master - -cache: - directories: - - vendor - - $HOME/.composer/cache - -matrix: - include: - - php: 7.3 - env: WP_VERSION=latest - - php: 7.2 - env: WP_VERSION=latest - - php: 7.1 - env: WP_VERSION=latest - - php: 7.0 - env: WP_VERSION=latest - - php: 7.0 - env: WP_VERSION=trunk - -install: - - | - cd $TRAVIS_BUILD_DIR - curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar - chmod +x wp-cli.phar - sudo mv wp-cli.phar /usr/local/bin/wp - -before_script: - - export PATH="$HOME/.composer/vendor/bin:$PATH" - - | - if [[ ! -z "$WP_VERSION" ]]; then - cp .env.dist .env - composer install-wp-tests - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer install --prefer-source --no-interaction - fi - - | - if [[ "$WP_TRAVISCI" == "phpcs" ]]; then - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer require \ - squizlabs/php_codesniffer \ - phpcompatibility/phpcompatibility-wp wp-coding-standards/wpcs \ - dealerdirect/phpcodesniffer-composer-installer - COMPOSER_MEMORY_LIMIT=-1 travis_retry composer install --no-dev - fi - -script: - - | - if [[ ! -z "$WP_VERSION" ]]; then - vendor/bin/codecept run wpunit - fi - - | - if [[ "$WP_TRAVISCI" == "phpcs" ]]; then - vendor/bin/phpcs \ - wp-graphql-acf.php \ - src/*.php --standard=WordPress - fi diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..720720a --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,75 @@ +# Using Docker For Local Development + +## WordPress Site + +The `app` docker image starts a running WordPress site with the local wp-graphql-acf directory installed and activated. Local changes to the source code is immediately reflects in the app. + +1. Build the plugin. +1.1 `composer install` or `docker run -v $PWD:/app composer --ignore-platform-reqs install` +1. Run `composer build-app` to build the `app` docker image. +1. Run `composer build-test` to build the `testing` docker image. +1. Run `composer run-app` to start the WordPress site. +1. Browse to http://localhost:8091/ to access the running WordPress app. +1. Browse to http://localhost:8091/wp-admin/ to access the admin dashboard. Username is 'admin'. Password is 'password'. + +## Testing Suite + +The `testing` docker image starts a running WordPress and runs the codeception test suites. + +1. Run `composer build-test` to build the `testing` docker image. +1. Run `composer run-test` to start the `testing` image and run the codeception tests. + +# Using XDebug + +## Local WordPress Site With XDebug +Use an environment variable USING_XDEBUG to start the docker image and WordPress with xdebug configured to use port 9003 to communicated with your IDE. + +``` +export USING_XDEBUG=1 +composer run-app +``` + +Start the debugger in your IDE. Set breakpoints. + +Load the app in http://localhost:8091/. + +## Using XDebug With Tests + +Use the environment variable USING_XDEBUG to run tests with xdebug configured to use port 9003 to communicated with your IDE. + +``` +export USING_XDEBUG=1 +composer run-test +``` + +Start the debugger in your IDE. Set breakpoints. + +## Configure VSCode IDE Launch File + +Create or add the following configuration to your .vscode/launch.json in the root directory. Restart VSCode. Start the debug listener before running the app or testing images. + +If you have WordPress core files in a directory for local development, you can add the location to the `pathMappings` for debug step through. + +``` +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9003, + "xdebugSettings": { + "max_children": 128, + "max_data": 1024, + "max_depth": 3, + "show_hidden": 1 + }, + "pathMappings": { + "/var/www/html/wp-content/plugins/wp-graphql-acf": "${workspaceFolder}", + "/var/www/html/wp-content/plugins/wp-graphql": "${workspaceFolder}../wp-graphql", + "/var/www/html": "${workspaceFolder}/wordpress", + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index da5a21f..bf26740 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,11 @@ WPGraphQL for Advanced Custom Fields automatically exposes your ACF fields to th - [Location Rules](#location-rules) ## Install and Activate + WPGraphQL for Advanced Custom Fields is not currently available on the WordPress.org repository, so you must download it from Github, or Composer. ### Installing From Github + To install the plugin from Github, you can [download the latest release zip file](https://github.com/wp-graphql/wp-graphql-acf/archive/master.zip), upload the Zip file to your WordPress install, and activate the plugin. [Click here](https://wordpress.org/support/article/managing-plugins/) to learn more about installing WordPress plugins from a Zip file. @@ -59,6 +61,7 @@ To install the plugin from Github, you can [download the latest release zip file `composer require wp-graphql/wp-graphql-acf` ## Dependencies + In order to use WPGraphQL for Advanced Custom Fields, you must have [WPGraphQL](https://github.com/wp-graphql/wp-graphql) and [Advanced Custom Fields](https://advancedcustomfields.com) (free or pro) installed and activated. ## Adding Fields to the WPGraphQL Schema @@ -92,9 +95,10 @@ Setting the value of this field to "Yes" will show the field group in the WPGrap When registering ACF Fields in PHP, `@todo` ## Supported Fields + In order to document interacting with the fields in GraphQL, an example field group has been created with one field of each type. -To replicate the same field group documented here you can [download the exported field group](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/download/field-group-export.zip) and [import it](https://support.advancedcustomfields.com/forums/topic/importing-exporting-acf-settings/) into your environment. +To replicate the same field group documented here you can download the [example field group](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/field-group-examples-export.json) and [import it](https://support.advancedcustomfields.com/forums/topic/importing-exporting-acf-settings/) into your environment. For the sake of documentation, this example field group has the [location rule](#location-rules) set to "Post Type is equal to Post", which will allow for the fields to be entered when creating and editing Posts in the WordPress dashboard, and will expose the fields to the `Post` type in the WPGraphQL Schema. @@ -116,7 +120,7 @@ This field can be Queried in GraphQL like so: { post( id: "acf-example-test" idType: URI ) { acfDocs { - textArea + text } } } @@ -129,15 +133,13 @@ and the results of the query would be: "data": { "post": { "acfDocs": { - "textArea": "Text Area Value" + "text": "Text Value" } } } } ``` -![Text field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/text-field-query.png?raw=true) - ### Text Area Field Text Area fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -174,14 +176,11 @@ and the results of the query would be: } ``` -![Text field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/text-field-query.png?raw=true) - - ### Number Field -Number fields are added to the WPGraphQL Schema as a field with the Type `Integer`. +Number fields are added to the WPGraphQL Schema as a field with the Type `Float`. -Number fields can be queried and an Integer will be returned. +Number fields can be queried and a Float will be returned. Here, we have a Number field named `number` on the Post Edit screen within the "ACF Docs" Field Group. @@ -213,13 +212,11 @@ and the results of the query would be: } ``` -![Number field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/number-field-query.png?raw=true) - ### Range Field -Range fields are added to the WPGraphQL Schema as a field with the Type `Integer`. +Range fields are added to the WPGraphQL Schema as a field with the Type `Float`. -Range fields can be queried and an Integer will be returned. +Range fields can be queried and a Float will be returned. Here, we have a Range field named `range` on the Post Edit screen within the "ACF Docs" Field Group. @@ -251,8 +248,6 @@ and the results of the query would be: } ``` -![Range field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/range-field-query.png?raw=true) - ### Email Field Email fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -289,8 +284,6 @@ and the results of the query would be: } ``` -![Email field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/email-field-query.png?raw=true) - ### URL Field Url fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -327,8 +320,6 @@ and the results of the query would be: } ``` -![URL field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/url-field-query.png?raw=true) - ### Password Field Password fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -365,8 +356,6 @@ and the results of the query would be: } ``` -![Password field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/password-field-query.png?raw=true) - ### Image Field Image fields are added to the WPGraphQL Schema as a field with the Type `MediaItem`. @@ -384,9 +373,11 @@ This field can be Queried in GraphQL like so: ```graphql { post( id: "acf-example-test" idType: URI ) { - image { - id - sourceUrl(size: MEDIUM) + acfDocs { + image { + id + sourceUrl(size: MEDIUM) + } } } } @@ -409,8 +400,6 @@ And the results of the query would be: } ``` -![Image field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/image-field-query.png?raw=true) - ### File Field File fields are added to the WPGraphQL Schema as a field with the Type `MediaItem`. @@ -455,8 +444,6 @@ And the results of the query would be: } ``` -![File field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/file-field-query.png?raw=true) - ### WYSIWYG Editor Field WYSIWYG fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -493,9 +480,6 @@ and the results of the query would be: } ``` -![WYSIWYG field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/wysiwyg-field-query.png?raw=true) - - ### oEmbed Field oEmbed fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -504,7 +488,7 @@ oEmbed fields can be queried and a String will be returned. Here, we have a oEmbed field named `oembed` on the Post Edit screen within the "ACF Docs" Field Group. -![oEmbed field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/oembed-field-input.png?raw=true) +![oEmbed field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/oEmbed-field-input.png?raw=true) This field can be Queried in GraphQL like so: @@ -516,7 +500,6 @@ This field can be Queried in GraphQL like so: } } } - ``` and the results of the query would be: @@ -533,9 +516,6 @@ and the results of the query would be: } ``` -![oEmbed field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/oembed-field-query.png?raw=true) - - ### Gallery Field Gallery fields are added to the WPGraphQL Schema as a field with the Type of `['list_of' => 'MediaItem']`. @@ -586,8 +566,6 @@ and the results of the query would be: } ``` -![Gallery field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/gallery-field-query.png?raw=true) - ### Select Field Select fields (when configured to _not_ allow mutliple selections) are added to the WPGraphQL Schema as a field with the Type `String`. @@ -624,8 +602,6 @@ and the results of the query would be: } ``` -![Select field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/select-field-query.png?raw=true) - ### Checkbox Field Checkbox fields are added to the WPGraphQL Schema as a field with the Type `[ 'list_of' => 'String' ]`. @@ -664,8 +640,6 @@ and the results of the query would be: } ``` -![Checkbox field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/checkbox-field-query.png?raw=true) - ### Radio Button Field Radio Button fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -702,8 +676,6 @@ and the results of the query would be: } ``` -![Radio Button field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/radio-button-field-query.png?raw=true) - ### Button Group Field Button Group fields are added to the WPGraphQL Schema as a field with the Type `String`. @@ -740,17 +712,15 @@ and the results of the query would be: } ``` -![Radio Button field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/radio-button-field-query.png?raw=true) - ### True/False Field True/False fields are added to the WPGraphQL Schema as a field with the Type `Boolean`. -True/False fields can be queried and a String will be returned. +True/False fields can be queried and a Boolean will be returned. Here, we have a True/False field named `true_false` on the Post Edit screen within the "ACF Docs" Field Group, and "true" is selected. -![True/False field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/true-false-group-field-input.png?raw=true) +![True/False field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/true-false-field-input.png?raw=true) This field can be Queried in GraphQL like so: @@ -778,8 +748,6 @@ and the results of the query would be: } ``` -![True/False field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/true-false-field-query.png?raw=true) - ### Link Field Link fields are added to the WPGraphQL Schema as a field with the Type `ACF_Link`. @@ -794,7 +762,7 @@ The available fields on the `ACF_Link` Type are: Here, we have a Link field named `link` on the Post Edit screen within the "ACF Docs" Field Group. -![Link field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/link-group-field-input.png?raw=true) +![Link field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/link-field-input.png?raw=true) This field can be Queried in GraphQL like so: @@ -830,8 +798,6 @@ and the results of the query would be: } ``` -![Link field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/link-field-query.png?raw=true) - ### Post Object Field Post Object fields are added to the WPGraphQL Schema as a field with a [Union](https://graphql.org/learn/schema/#union-types) of Possible Types the field is configured to allow. @@ -848,7 +814,7 @@ Then the Union type for the field will allow `Post` and `Page` types to be retur ![Post Object field Union Possible Types](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/post-object-field-possible-types.png?raw=true) -Here, we have a Post Object field named `post_object` on the Post Edit screen within the "ACF Docs" Field Group, configured with the Post "Hello World". +Here, we have a Post Object field named `post_object` on the Post Edit screen within the "ACF Docs" Field Group, configured with the Post "Hello World!". ![Post Object field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/post-object-field-input-post.png?raw=true) @@ -898,8 +864,6 @@ and the results of the query would be: } ``` -![Post Object field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/post-object-field-query-post.png?raw=true) - If the input of the field was saved as a Page, instead of a Post, like so: ![Post Object field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/post-object-field-input-page.png?raw=true) @@ -922,8 +886,6 @@ Then the same query above, would return the following results: } ``` -![Post Object field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/post-object-field-query-page.png?raw=true) - Now, if the field were configured to allow multiple values, the field would be added to the Schema as a `listOf`, returning an Array of the Union. If the field were set with a value of one Page, and one Post, like so: @@ -956,8 +918,6 @@ Then the results of the same query as above would be: } ``` -![Post Object field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/post-object-field-query-multi.png?raw=true) - ### Page Link Field Page Link fields are added to the WPGraphQL Schema as a field with a [Union](https://graphql.org/learn/schema/#union-types) of Possible Types the field is configured to allow. @@ -974,7 +934,7 @@ Then the Union type for the field will allow `Post` and `Page` types to be retur Here, we have a Page Link field named `page_link` on the Post Edit screen within the "ACF Docs" Field Group, and the value is set to the "Sample Page" page. -![Page Link field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/page-link-field-input-page.png?raw=true) +![Page Link field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/page-link-field-input.png?raw=true) This field can be Queried in GraphQL like so: @@ -1017,11 +977,9 @@ and the results of the query would be: } ``` -![Page Link field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/link-field-query-page.png?raw=true) - Here, we set the value to the "Hello World" Post: -![Page Link field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/page-link-field-input-post.png?raw=true) +![Page Link field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/page-link-field-input-2.png?raw=true) And the results of the same query are now: @@ -1042,8 +1000,6 @@ And the results of the same query are now: } ``` -![Page Link field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/link-field-query-post.png?raw=true) - ### Relationship Field Relationship fields are added to the WPGraphQL Schema as a field with a [Union](https://graphql.org/learn/schema/#union-types) of Possible Types the field is configured to allow. @@ -1111,8 +1067,6 @@ and the results of the query would be: } ``` -![Relationship field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/relationship-field-query.png?raw=true) - ### Taxonomy Field The Taxonomy field is added to the GraphQL Schema as a List Of the Taxonomy Type. @@ -1161,8 +1115,6 @@ and the results of the query would be: } ``` -![Taxonomy field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/taxonomy-field-query.png?raw=true) - ### User Field User fields are added to the WPGraphQL Schema as a field with a User type. @@ -1207,13 +1159,11 @@ and the response would look like: } ``` -![User field Query with one selection](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/user-field-query.png?raw=true) - If the field is configured to allow multiple selections, it's added to the Schema as a List Of the User type. -Here, we have a User field named `user` on the Post Edit screen within the "ACF Docs" Field Group, set with the User "jasonbahl" as the value. +Here, we have a User field named `user` on the Post Edit screen within the "ACF Docs" Field Group, set with the User "jasonbahl" and "WPGraphQL" as the value. -![User field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/user-field-input-multi.png?raw=true) +![User field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/user-field-input-multiple.png?raw=true) and the response to the same query would look like: @@ -1241,11 +1191,10 @@ and the response to the same query would look like: } } ``` -![User field Query with multiple selections](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/user-field-query-multiple.png?raw=true) ### Google Map Field -Google Map fields are added the WPGraphQL Schema as the `ACF_GoogleMap` Type. +Google Map fields are added to the WPGraphQL Schema as the `ACF_GoogleMap` Type. The `ACF_GoogleMap` Type has fields that expose location data. The available fields are: @@ -1265,7 +1214,7 @@ The `ACF_GoogleMap` Type has fields that expose location data. The available fie Here, we have a Google Map field named `google_map` on the Post Edit screen within the "ACF Docs" Field Group, set with the Address "1 Infinite Loop, Cupertino, CA 95014, USA" as the value. -![Google Map field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/google-map-field-input.png?raw=true) +![Google Map field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/map-field-input.png?raw=true) This field can be queried in GraphQL like so: @@ -1285,7 +1234,6 @@ This field can be queried in GraphQL like so: } } } - ``` and the response would look like: @@ -1311,8 +1259,6 @@ and the response would look like: } ``` -![Google Map field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/google-map-field-query.png?raw=true) - ### Date Picker Field The Date Picker field is added to the WPGraphQL Schema as field with the Type `String`. @@ -1349,8 +1295,6 @@ and the result of the query would be: } ``` -![Date Picker field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/date-picker-field-query.png?raw=true) - ### Date/Time Picker Field The Date/Time Picker field is added to the WPGraphQL Schema as field with the Type `String`. @@ -1387,8 +1331,6 @@ and the result of the query would be: } ``` -![Date/Time Picker field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/date-time-picker-field-query.png?raw=true) - ### Time Picker Field The Time Picker field is added to the WPGraphQL Schema as field with the Type `String`. @@ -1425,8 +1367,6 @@ and the result of the query would be: } ``` -![Time Picker field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/time-picker-field-query.png?raw=true) - ### Color Picker Field The Color Picker field is added to the WPGraphQL Schema as field with the Type `String`. @@ -1463,8 +1403,6 @@ and the result of the query would be: } ``` -![Color Picker field in the Edit Post screen](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/color-picker-field-query.png?raw=true) - ### Message Field Message fields are not currently supported. @@ -1517,8 +1455,6 @@ And the results of the query would be: } ``` -![Group field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/group-field-query.png?raw=true) - ### Repeater Field Repeater Fields are added to the Schema as a List Of the Type of group that makes up the fields. @@ -1583,8 +1519,6 @@ and the results of the query would be: } ``` -![Repeater field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/repeater-field-query.png?raw=true) - ### Flexible Content Field The Flexible Content is a powerful ACF field that allows for groups of fields to be organized into "Layouts". @@ -1595,7 +1529,7 @@ Flexible Content Fields are added to the WPGraphQL Schema as a List Of [Unions]( The Union for a Flex Field is made up of each Layout in the Flex Field as the possible Types. -In our example, we've created a Flex Field with 3 layouts named "Layout 1", "Layout 2" and "Layout 3". In the Schema, we can see the Flex Field Union's Possible Types are these 3 layouts. +In our example, we've created a Flex Field with 3 layouts named "Layout One", "Layout Two" and "Layout Three". In the Schema, we can see the Flex Field Union's Possible Types are these 3 layouts. ![Flex Fields Schema Union Possible Types](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/flex-field-union-possible-types.png?raw=true) @@ -1603,12 +1537,12 @@ Each of these Layout types will contain the fields defined for the layout and ca Here's an example of a Flex Field named `flexible_content`, with 3 layouts: -- Layout 1 +- Layout One - Text field named "text" - Text field named "another_text_field" -- Layout 2 +- Layout Two - Image field named "image" -- Layout 3 +- Layout Three - Gallery field named "gallery" Above are the possible layouts and their fields. These layouts can be added and arranged in any order. While we, as a GraphQL consumer, don't know ahead of time what order they will be in, we _do_ know what the possibilities are. @@ -1620,7 +1554,6 @@ Here's an example of a Flex Field named `flexible_content` with the values saved We can query this field like so: ```graphql - { post(id: "acf-example-test", idType: URI) { acfDocs { @@ -1688,8 +1621,6 @@ and the results of the query would be: } ``` -![Flex field Query](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/flex-field-query.png?raw=true) - If we were to re-arrange the layouts, so that the order was "Layout Three", "Layout One", "Layout Two", the results of the query would be: ```json @@ -1728,8 +1659,6 @@ If we were to re-arrange the layouts, so that the order was "Layout Three", "Lay } ``` -![Flex field Query 2](https://github.com/wp-graphql/wp-graphql-acf/blob/master/docs/img/flex-field-query2.png?raw=true) - ### Clone Field The clone field is not fully supported (yet). We plan to support it in the future. @@ -1775,7 +1704,7 @@ Alternatively, it's you can check the Fields API Reference to learn about exposi ## Location Rules -Advanced Custom Fields field groups are added to the WordPress dashboard by being assinged "Location Rules". +Advanced Custom Fields field groups are added to the WordPress dashboard by being assigned "Location Rules". WPGraphQL for Advanced Custom Fields uses the Location Rules to determine where in the GraphQL Schema the field groups/fields should be added to the Schema. @@ -1786,6 +1715,7 @@ For example, if a Field Group were assigned to "Post Type is equal to Post", the @todo: Document supported location rules and how they map from ACF to the WPGraphQL Schema ### Why aren't all location rules supported? + You might notice that some location rules don't add fields to the Schema. This is because some location rules are based on context that doesn't exist when the GraphQL Schema is generated. For example, if you have a location rule to show a field group only on a specific page, how would that be exposed the the Schema? There's no Type in the Schema for just one specific page. diff --git a/bin/install-test-env.sh b/bin/install-test-env.sh new file mode 100644 index 0000000..66890d1 --- /dev/null +++ b/bin/install-test-env.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash + +source .env + +print_usage_instruction() { + echo "Ensure that .env file exist in project root directory exists." + echo "And run the following 'composer install-wp-tests' in the project root directory" + exit 1 +} + +if [[ -z "$TEST_DB_NAME" ]]; then + echo "TEST_DB_NAME not found" + print_usage_instruction +else + DB_NAME=$TEST_DB_NAME +fi +if [[ -z "$TEST_DB_USER" ]]; then + echo "TEST_DB_USER not found" + print_usage_instruction +else + DB_USER=$TEST_DB_USER +fi + +DB_HOST=${TEST_DB_HOST-localhost} +DB_PASS=${TEST_DB_PASSWORD-""} +WP_VERSION=${WP_VERSION-latest} +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${TEST_WP_ROOT_FOLDER-$TMPDIR/wordpress/} +PLUGIN_DIR=$(pwd) +DB_SERVE_NAME=${DB_SERVE_NAME-wpgatsby_serve} +SKIP_DB_CREATE=${SKIP_DB_CREATE-false} + +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then + WP_BRANCH=${WP_VERSION%\-*} + WP_TESTS_TAG="branches/$WP_BRANCH" + +elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then + WP_TESTS_TAG="branches/$WP_VERSION" +elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + WP_TESTS_TAG="tags/${WP_VERSION%??}" + else + WP_TESTS_TAG="tags/$WP_VERSION" + fi +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi +set -ex + +install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-nightly + download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip + unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ + mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + fi + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + RESULT=`mysql -u $DB_USER --password="$DB_PASS" --skip-column-names -e "SHOW DATABASES LIKE '$DB_NAME'"$EXTRA` + if [ "$RESULT" != $DB_NAME ]; then + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA + fi +} + +configure_wordpress() { + cd $WP_CORE_DIR + wp config create --dbname="$DB_NAME" --dbuser="$DB_USER" --dbpass="$DB_PASS" --dbhost="$DB_HOST" --skip-check --force=true + wp core install --url=wp.test --title="WPGraphQL ACF Tests" --admin_user=admin --admin_password=password --admin_email=admin@wp.test + wp rewrite structure '/%year%/%monthnum%/%postname%/' +} + +install_acf_pro() { + if [ ! -d $WP_CORE_DIR/wp-content/plugins/advanced-custom-fields-pro ]; then + echo "Cloning ACF PRO" + git clone https://github.com/wp-premium/advanced-custom-fields-pro.git $WP_CORE_DIR/wp-content/plugins/advanced-custom-fields-pro + fi + echo "Cloning ACF PRO" + wp plugin activate advanced-custom-fields-pro +} + +setup_plugin() { + # Add this repo as a plugin to the repo + if [ ! -d $WP_CORE_DIR/wp-content/plugins/wp-graphql-acf ]; then + ln -s $PLUGIN_DIR $WP_CORE_DIR/wp-content/plugins/wp-graphql-acf + cd $WP_CORE_DIR/wp-content/plugins + pwd + ls + fi + + cd $WP_CORE_DIR + + wp plugin list + + # Install WPGraphQL + wp plugin install wp-graphql + + # Activate WPGraphQL + wp plugin activate wp-graphql + + # activate the plugin + wp plugin activate wp-graphql-acf + + # List the active plugins + wp plugin list + + # Flush the permalinks + wp rewrite flush + + # Export the db for codeception to use + wp db export $PLUGIN_DIR/tests/_data/dump.sql +} + +install_wp +install_db +configure_wordpress +install_acf_pro +setup_plugin diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh deleted file mode 100755 index dbc7a55..0000000 --- a/bin/install-wp-tests.sh +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env bash - -source .env - -print_usage_instruction() { - echo "Ensure that .env file exist in project root directory exists." - echo "And run the following 'composer install-wp-tests' in the project root directory" - exit 1 -} - -if [[ -z "$TEST_DB_NAME" ]]; then - echo "TEST_DB_NAME not found" - print_usage_instruction -else - DB_NAME=$TEST_DB_NAME -fi -if [[ -z "$TEST_DB_USER" ]]; then - echo "TEST_DB_USER not found" - print_usage_instruction -else - DB_USER=$TEST_DB_USER -fi -if [[ -z "$TEST_DB_PASSWORD" ]]; then - DB_PASS="" -else - DB_PASS=$TEST_DB_PASSWORD -fi -if [[ -z "$TEST_DB_HOST" ]]; then - DB_HOST=localhost -else - DB_HOST=$TEST_DB_HOST -fi -if [ -z "$SKIP_DB_CREATE" ]; then - SKIP_DB_CREATE=false -fi - -PLUGIN_DIR=$(pwd) -WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wp-graphql-acf/wordpress-tests-lib} -WP_CORE_DIR=${WP_CORE_DIR-/tmp/wp-graphql-acf/wordpress/} - -download() { - if [ `which curl` ]; then - curl -s "$1" > "$2"; - elif [ `which wget` ]; then - wget -nv -O "$2" "$1" - fi -} - -if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then - WP_TESTS_TAG="tags/$WP_VERSION" -elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then - WP_TESTS_TAG="trunk" -else - # http serves a single offer, whereas https serves multiple. we only want one - download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json - grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json - LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') - if [[ -z "$LATEST_VERSION" ]]; then - echo "Latest WordPress version could not be found" - exit 1 - fi - WP_TESTS_TAG="tags/$LATEST_VERSION" -fi - -set -ex - -install_wp() { - - if [ -d $WP_CORE_DIR ]; then - return; - fi - - mkdir -p $WP_CORE_DIR - - if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then - mkdir -p /tmp/wordpress-nightly - download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip - unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/ - mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR - else - if [ $WP_VERSION == 'latest' ]; then - local ARCHIVE_NAME='latest' - else - local ARCHIVE_NAME="wordpress-$WP_VERSION" - fi - download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz - tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR - fi - - download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php -} - -install_test_suite() { - # portable in-place argument for both GNU sed and Mac OSX sed - if [[ $(uname -s) == 'Darwin' ]]; then - local ioption='-i .bak' - else - local ioption='-i' - fi - - # set up testing suite if it doesn't yet exist - if [ ! -d $WP_TESTS_DIR ]; then - # set up testing suite - mkdir -p $WP_TESTS_DIR - svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes - svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data - fi - - if [ ! -f wp-tests-config.php ]; then - download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php - # remove all forward slashes in the end - WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") - sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php - sed $ioption "s|localhost|$DB_HOST|" "$WP_TESTS_DIR"/wp-tests-config.php - fi - -} - -install_db() { - - if [ ${SKIP_DB_CREATE} = "true" ]; then - return 0 - fi - - # parse DB_HOST for port or socket references - local PARTS=(${DB_HOST//\:/ }) - local DB_HOSTNAME=${PARTS[0]}; - local DB_SOCK_OR_PORT=${PARTS[1]}; - local EXTRA="" - - if ! [ -z $DB_HOSTNAME ] ; then - if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then - EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" - elif ! [ -z $DB_SOCK_OR_PORT ] ; then - EXTRA=" --socket=$DB_SOCK_OR_PORT" - elif ! [ -z $DB_HOSTNAME ] ; then - EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" - fi - fi - - # create database - mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA -} - -configure_wordpress() { - - cd $WP_CORE_DIR - wp config create --dbname="$DB_NAME" --dbuser="$DB_USER" --dbpass="$DB_PASS" --dbhost="$DB_HOST" --skip-check --force=true - wp core install --url=wpgraphql.test --title="WPGraphQL ACF Tests" --admin_user=admin --admin_password=password --admin_email=admin@wpgraphql.test - wp rewrite structure '/%year%/%monthnum%/%postname%/' -} - -install_wpgraphql() { - if [ ! -d $WP_CORE_DIR/wp-content/plugins/wp-graphql ]; then - echo "Cloning WPGraphQL" - git clone https://github.com/wp-graphql/wp-graphql.git $WP_CORE_DIR/wp-content/plugins/wp-graphql - fi - echo "Activating WPGraphQL" - wp plugin activate wp-graphql -} - -install_acf_pro() { - if [ ! -d $WP_CORE_DIR/wp-content/plugins/advanced-custom-fields-pro ]; then - echo "Cloning ACF PRO" - git clone https://github.com/wp-premium/advanced-custom-fields-pro.git $WP_CORE_DIR/wp-content/plugins/advanced-custom-fields-pro - fi - echo "Cloning ACF PRO" - wp plugin activate advanced-custom-fields-pro -} - -activate_plugins() { - - # Add this repo as a plugin to the repo - if [ ! -d $WP_CORE_DIR/wp-content/plugins/wp-graphql-acf ]; then - ln -s $PLUGIN_DIR $WP_CORE_DIR/wp-content/plugins/wp-graphql-acf - fi - - cd $WP_CORE_DIR - - # Flush the permalinks - wp rewrite flush - - # Export the db for codeception to use - wp db export $PLUGIN_DIR/tests/_data/dump.sql -} - -install_wp -install_test_suite -install_db -configure_wordpress -install_wpgraphql -install_acf_pro -activate_plugins diff --git a/bin/run-docker.sh b/bin/run-docker.sh new file mode 100644 index 0000000..d21e8d1 --- /dev/null +++ b/bin/run-docker.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +set -eu + +## +# Use this script through Composer scripts in the package.json. +# To quickly build and run the docker-compose scripts for an app or automated testing +# run the command below after run `composer install --no-dev` with the respectively +# flag for what you need. +## +print_usage_instructions() { + echo "Usage: $0 [build|run] [-c|-a|-t]"; + echo " Build or run app or testing images." + echo " -c Specify as first option with [build] command to build images without cache." + echo " -a Spin up a WordPress installation."; + echo " -t Run the automated tests."; + exit 1 +} + +if [ -z "$1" ]; then + print_usage_instructions +fi + +BUILD_NO_CACHE= + +subcommand=$1; shift +case "$subcommand" in + "build" ) + while getopts ":cat" opt; do + case ${opt} in + c ) + echo "Build without cache" + BUILD_NO_CACHE=--no-cache + ;; + a ) + echo "Build app" + docker build $BUILD_NO_CACHE -f docker/app.Dockerfile \ + -t wpgraphql-acf-app:latest \ + --build-arg WP_VERSION=${WP_VERSION-5.4} \ + --build-arg PHP_VERSION=${PHP_VERSION-7.4} \ + . + ;; + t ) + echo "Build app" + docker build $BUILD_NO_CACHE -f docker/app.Dockerfile \ + -t wpgraphql-acf-app:latest \ + --build-arg WP_VERSION=${WP_VERSION-5.4} \ + --build-arg PHP_VERSION=${PHP_VERSION-7.4} \ + . + echo "Build testing" + docker build $BUILD_NO_CACHE -f docker/testing.Dockerfile \ + -t wpgraphql-acf-testing:latest \ + . + ;; + \? ) print_usage_instructions;; + * ) print_usage_instructions;; + esac + done + shift $((OPTIND -1)) + ;; + "run" ) + while getopts ":at" opt; do + case ${opt} in + a ) + docker-compose up --scale testing=0 + ;; + t ) + docker-compose run --rm \ + -e COVERAGE=${COVERAGE-} \ + -e USING_XDEBUG=${USING_XDEBUG-} \ + -e DEBUG=${DEBUG-} \ + -e WPGRAPHQL_VERSION=${WPGRAPHQL_VERSION-} \ + testing --scale app=0 + ;; + \? ) print_usage_instructions;; + * ) print_usage_instructions;; + esac + done + shift $((OPTIND -1)) + ;; + + \? ) print_usage_instructions;; + * ) print_usage_instructions;; +esac diff --git a/codeception.dist.yml b/codeception.dist.yml index e3d4224..23756bd 100644 --- a/codeception.dist.yml +++ b/codeception.dist.yml @@ -1,73 +1,85 @@ paths: - tests: tests - output: tests/_output - data: tests/_data - support: tests/_support - envs: tests/_envs + tests: '%TESTS_DIR%' + output: '%TESTS_OUTPUT%' + data: '%TESTS_DATA%' + support: '%TESTS_SUPPORT%' + envs: '%TESTS_ENVS%' +params: + - env + - .env actor_suffix: Tester settings: - colors: true - memory_limit: 1024M + colors: true + memory_limit: 1024M coverage: - enabled: true - whitelist: - include: - - wp-graphql-acf.php - - access-functions.php - - src/*.php + enabled: true + remote: false + c3_url: '%WP_URL%/wp-content/plugins/wp-graphql-acf/wp-graphql-acf.php' + include: + - src/* + exclude: + - wp-graphql-acf.php + - vendor/* + show_only_summary: false extensions: - enabled: - - Codeception\Extension\RunFailed - commands: - - Codeception\Command\GenerateWPUnit - - Codeception\Command\GenerateWPRestApi - - Codeception\Command\GenerateWPRestController - - Codeception\Command\GenerateWPRestPostTypeController - - Codeception\Command\GenerateWPAjax - - Codeception\Command\GenerateWPCanonical - - Codeception\Command\GenerateWPXMLRPC -params: - - .env + enabled: + - Codeception\Extension\RunFailed + commands: + - Codeception\Command\GenerateWPUnit + - Codeception\Command\GenerateWPRestApi + - Codeception\Command\GenerateWPRestController + - Codeception\Command\GenerateWPRestPostTypeController + - Codeception\Command\GenerateWPAjax + - Codeception\Command\GenerateWPCanonical + - Codeception\Command\GenerateWPXMLRPC modules: - config: - WPDb: - dsn: 'mysql:host=%DB_HOST%;dbname=%DB_NAME%' - user: '%DB_USER%' - password: '%DB_PASSWORD%' - populator: 'mysql -u $user -p$password -h $host $dbname < $dump' - dump: 'tests/_data/dump.sql' - populate: true - cleanup: true - waitlock: 0 - url: '%WP_URL%' - urlReplacement: true - tablePrefix: '%WP_TABLE_PREFIX%' - WPBrowser: - url: '%WP_URL%' - wpRootFolder: '%WP_ROOT_FOLDER%' - adminUsername: '%ADMIN_USERNAME%' - adminPassword: '%ADMIN_PASSWORD%' - adminPath: '/wp-admin' - REST: - depends: WPBrowser - url: '%WP_URL%' - WPFilesystem: - wpRootFolder: '%WP_ROOT_FOLDER%' - plugins: '/wp-content/plugins' - mu-plugins: '/wp-content/mu-plugins' - themes: '/wp-content/themes' - uploads: '/wp-content/uploads' - WPLoader: - wpRootFolder: '%WP_ROOT_FOLDER%' - dbName: '%DB_NAME%' - dbHost: '%DB_HOST%' - dbUser: '%DB_USER%' - dbPassword: '%DB_PASSWORD%' - tablePrefix: '%WP_TABLE_PREFIX%' - domain: '%WP_DOMAIN%' - adminEmail: '%ADMIN_EMAIL%' - title: 'Test' - plugins: ['wp-graphql/wp-graphql.php', 'wp-graphql-acf/wp-graphql-acf.php', 'advanced-custom-fields-pro/acf.php'] - activatePlugins: ['wp-graphql/wp-graphql.php', 'wp-graphql-acf/wp-graphql-acf.php', 'advanced-custom-fields-pro/acf.php'] - configFile: 'tests/_data/config.php' - + config: + WPDb: + dsn: 'mysql:host=%DB_HOST%;dbname=%DB_NAME%' + user: '%DB_USER%' + password: '%DB_PASSWORD%' + populator: 'mysql -u $user -p$password -h $host $dbname < $dump' + dump: 'tests/_data/dump.sql' + populate: false + cleanup: true + waitlock: 0 + url: '%WP_URL%' + urlReplacement: true + tablePrefix: '%WP_TABLE_PREFIX%' + WPBrowser: + url: '%WP_URL%' + wpRootFolder: '%WP_ROOT_FOLDER%' + adminUsername: '%ADMIN_USERNAME%' + adminPassword: '%ADMIN_PASSWORD%' + adminPath: '/wp-admin' + cookies: false + REST: + depends: WPBrowser + url: '%WP_URL%' + WPFilesystem: + wpRootFolder: '%WP_ROOT_FOLDER%' + plugins: '/wp-content/plugins' + mu-plugins: '/wp-content/mu-plugins' + themes: '/wp-content/themes' + uploads: '/wp-content/uploads' + WPLoader: + wpRootFolder: '%WP_ROOT_FOLDER%' + dbName: '%DB_NAME%' + dbHost: '%DB_HOST%' + dbUser: '%DB_USER%' + dbPassword: '%DB_PASSWORD%' + tablePrefix: '%WP_TABLE_PREFIX%' + domain: '%WP_DOMAIN%' + adminEmail: '%ADMIN_EMAIL%' + title: 'Test' + plugins: + - wp-graphql-acf/tests/_bootstrap/bootstrap.php + - advanced-custom-fields-pro/acf.php + - wp-graphql/wp-graphql.php + - wp-graphql-acf/wp-graphql-acf.php + activatePlugins: + - wp-graphql-acf/tests/_bootstrap/bootstrap.php + - advanced-custom-fields-pro/acf.php + - wp-graphql/wp-graphql.php + - wp-graphql-acf/wp-graphql-acf.php + configFile: 'tests/_data/config.php' diff --git a/composer.json b/composer.json index fb620d4..a5fcb29 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "email": "jasonbahl@mac.com" }], "config": { - "optimize-autoloader": true + "optimize-autoloader": true, + "process-timeout": 0 }, "autoload": { "psr-4": { @@ -19,16 +20,52 @@ ] }, "scripts": { - "install-wp-tests": "bash bin/install-wp-tests.sh", - "test": "vendor/bin/codecept run", - "functional-test": "vendor/bin/codecept run functional", - "acceptance-test": "vendor/bin/codecept run acceptance", - "wpunit-test": "vendor/bin/codecept run wpunit" + "install-test-env": "bash bin/install-test-env.sh", + "docker-build": "bash bin/run-docker.sh build", + "docker-run": "bash bin/run-docker.sh run", + "docker-destroy": "docker-compose down", + "build-and-run": [ + "@docker-build", + "@docker-run" + ], + "build-app": "@docker-build -a", + "build-test": "@docker-build -t", + "run-app": "@docker-run -a", + "run-test": "@docker-run -t", + "lint": "vendor/bin/phpcs", + "phpcs-i": [ + "php ./vendor/bin/phpcs -i" + ], + "check-cs": [ + "php ./vendor/bin/phpcs src" + ], + "fix-cs": [ + "php ./vendor/bin/phpcbf src" + ], + "phpstan": ["phpstan analyze --ansi --memory-limit=1G"] + }, + "require": { + "php": "^7" }, "require-dev": { - "lucatume/wp-browser": "^2.2.", - "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", - "wp-coding-standards/wpcs": "^2.1", - "phpcompatibility/phpcompatibility-wp": "^2.0" + "lucatume/wp-browser": "^2.4", + "codeception/module-asserts": "^1.0", + "codeception/module-phpbrowser": "^1.0", + "codeception/module-webdriver": "^1.0", + "codeception/module-db": "^1.0", + "codeception/module-filesystem": "^1.0", + "codeception/module-cli": "^1.0", + "codeception/util-universalframework": "^1.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", + "wp-coding-standards/wpcs": "2.1.1", + "phpcompatibility/phpcompatibility-wp": "2.1.0", + "squizlabs/php_codesniffer": "3.5.4", + "phpstan/phpstan": "^0.12.64", + "szepeviktor/phpstan-wordpress": "^0.7.1", + "codeception/module-rest": "^1.2", + "wp-graphql/wp-graphql-testcase": "^1.0", + "phpunit/phpunit": "9.4.1", + "simpod/php-coveralls-mirror": "^3.0", + "phpstan/extension-installer": "^1.1" } } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..03ea775 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,64 @@ +version: '3.3' + +services: + app: + depends_on: + - app_db + image: wpgraphql-acf-app:latest + volumes: + - '.:/var/www/html/wp-content/plugins/wp-graphql-acf' + - './.log/app:/var/log/apache2' + env_file: + - .env + environment: + WP_URL: 'http://localhost:8091' + WP_DOMAIN: 'localhost:8091' + DB_HOST: app_db + DB_NAME: wordpress + DB_USER: wordpress + DB_PASSWORD: wordpress + WP_DOMAIN: localhost + ADMIN_EMAIL: admin@example.com + ADMIN_USERNAME: admin + ADMIN_PASSWORD: password + USING_XDEBUG: ${USING_XDEBUG:-} + WPGRAPHQL_VERSION: ${WPGRAPHQL_VERSION:-} + ports: + - '8091:80' + networks: + local: + + app_db: + image: mysql:5.7 + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: wordpress + MYSQL_USER: wordpress + MYSQL_PASSWORD: wordpress + ports: + - '3306' + networks: + testing: + local: + + testing: + depends_on: + - app_db + image: wpgraphql-acf-testing:latest + volumes: + - '.:/var/www/html/wp-content/plugins/wp-graphql-acf' + - './.log/testing:/var/log/apache2' + - './codeception.dist.yml:/var/www/html/wp-content/plugins/wp-graphql-acf/codeception.yml' + env_file: + - .env + environment: + DB_HOST: app_db + WP_URL: 'http://localhost' + WP_DOMAIN: 'localhost' + SUITES: ${SUITES:-} + networks: + testing: + +networks: + local: + testing: diff --git a/docker/app.Dockerfile b/docker/app.Dockerfile new file mode 100644 index 0000000..4e0872e --- /dev/null +++ b/docker/app.Dockerfile @@ -0,0 +1,79 @@ +############################################################################### +# Pre-configured WordPress Installation w/ WPGraphQL, WPGraphQL for ACF, ACF Pro # +# For testing only, use in production not recommended. # +############################################################################### +ARG WP_VERSION +ARG PHP_VERSION + +FROM wordpress:${WP_VERSION}-php${PHP_VERSION}-apache + +ENV WP_VERSION=${WP_VERSION} +ENV PHP_VERSION=${PHP_VERSION} + +LABEL author=jasonbahl +LABEL author_uri=https://github.com/jasonbahl + +SHELL [ "/bin/bash", "-c" ] + +# Install system packages +RUN apt-get update && \ + apt-get -y install \ + # CircleCI depedencies + git \ + ssh \ + tar \ + gzip \ + wget \ + mariadb-client + +# Install Dockerize +ENV DOCKERIZE_VERSION v0.6.1 +RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + +# Install WP-CLI +RUN curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ + && chmod +x wp-cli.phar \ + && mv wp-cli.phar /usr/local/bin/wp + +# Set project environmental variables +ENV WP_ROOT_FOLDER="/var/www/html" +ENV WORDPRESS_DB_HOST=${DB_HOST} +ENV WORDPRESS_DB_USER=${DB_USER} +ENV WORDPRESS_DB_PASSWORD=${DB_PASSWORD} +ENV WORDPRESS_DB_NAME=${DB_NAME} +ENV PLUGINS_DIR="${WP_ROOT_FOLDER}/wp-content/plugins" +ENV PROJECT_DIR="${PLUGINS_DIR}/wp-graphql-acf" +ENV WPGRAPHQL_VERSION="${WPGRAPHQL_VERSION}" + +# Remove exec statement from base entrypoint script. +RUN sed -i '$d' /usr/local/bin/docker-entrypoint.sh + +# Set up Apache +RUN echo 'ServerName localhost' >> /etc/apache2/apache2.conf + +# Custom PHP settings +RUN echo "upload_max_filesize = 50M" >> /usr/local/etc/php/conf.d/custom.ini \ + ; + +# Install XDebug 3 +RUN echo "Installing XDebug 3 (in disabled state)" \ + && pecl install xdebug \ + && mkdir -p /usr/local/etc/php/conf.d/disabled \ + && echo "zend_extension=xdebug" > /usr/local/etc/php/conf.d/disabled/docker-php-ext-xdebug.ini \ + && echo "xdebug.mode=develop,debug,coverage" >> /usr/local/etc/php/conf.d/disabled/docker-php-ext-xdebug.ini \ + && echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/disabled/docker-php-ext-xdebug.ini \ + && echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/disabled/docker-php-ext-xdebug.ini \ + && echo "xdebug.client_port=9003" >> /usr/local/etc/php/conf.d/disabled/docker-php-ext-xdebug.ini \ + ; + +# Set xdebug configuration off by default. See the entrypoint.sh. +ENV USING_XDEBUG=${USING_XDEBUG} + +# Set up entrypoint +WORKDIR /var/www/html +COPY docker/app.entrypoint.sh /usr/local/bin/app-entrypoint.sh +RUN chmod 755 /usr/local/bin/app-entrypoint.sh +ENTRYPOINT ["app-entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/docker/app.entrypoint.sh b/docker/app.entrypoint.sh new file mode 100644 index 0000000..1a06d4c --- /dev/null +++ b/docker/app.entrypoint.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +if [ "$USING_XDEBUG" == "1" ]; then + echo "Enabling XDebug 3" + mv /usr/local/etc/php/conf.d/disabled/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/ +fi + +# Run WordPress docker entrypoint. +. docker-entrypoint.sh 'apache2' + +set +u + +# Ensure mysql is loaded +dockerize -wait tcp://${DB_HOST}:${DB_HOST_PORT:-3306} -timeout 1m + +# Config WordPress +if [ ! -f "${WP_ROOT_FOLDER}/wp-config.php" ]; then + wp config create \ + --path="${WP_ROOT_FOLDER}" \ + --dbname="${DB_NAME}" \ + --dbuser="${DB_USER}" \ + --dbpass="${DB_PASSWORD}" \ + --dbhost="${DB_HOST}" \ + --dbprefix="${WP_TABLE_PREFIX}" \ + --skip-check \ + --quiet \ + --allow-root +fi + +# Install WP if not yet installed +if ! $( wp core is-installed --allow-root ); then + wp core install \ + --path="${WP_ROOT_FOLDER}" \ + --url="${WP_URL}" \ + --title='Test' \ + --admin_user="${ADMIN_USERNAME}" \ + --admin_password="${ADMIN_PASSWORD}" \ + --admin_email="${ADMIN_EMAIL}" \ + --allow-root +fi + +# Install and activate WPGraphQL + +echo "wpgraphql version... ${WPGRAPHQL_VERSION}" +echo "${PLUGINS_DIR}" + +if [ ! -f "${PLUGINS_DIR}/wp-graphql/wp-graphql.php" ]; then + # WPGRAPHQL_VERSION in format like v1.2.3 + echo ${WPGRAPHQL_VERSION} + if [[ -z ${WPGRAPHQL_VERSION} ]]; then + echo "installing latest WPGraphQL from WordPress.org" + wp plugin install wp-graphql --activate --allow-root + else + echo "Installing WPGraphQL from Github" + git clone https://github.com/wp-graphql/wp-graphql.git "${PLUGINS_DIR}/wp-graphql" + cd "${PLUGINS_DIR}/wp-graphql" + echo "checking out WPGraphQL tag/${WPGRAPHQL_VERSION}" + git checkout tags/${WPGRAPHQL_VERSION} -b master + composer install --no-dev + cd ${WP_ROOT_FOLDER} + echo "activating WPGraphQL" + wp plugin activate wp-graphql --allow-root + wp plugin list --allow-root + fi +else + wp plugin activate wp-graphql --allow-root +fi + +# Install and activate ACF Pro +if [ ! -f "${PLUGINS_DIR}/advanced-custom-fields-pro/acf.php" ]; then + wp plugin install \ + https://github.com/wp-premium/advanced-custom-fields-pro/archive/master.zip \ + --activate --allow-root +else + wp plugin activate advanced-custom-fields-pro --allow-root +fi + +# Install and activate WPGatsby +wp plugin activate wp-graphql-acf --allow-root + +# Set pretty permalinks. +wp rewrite structure '/%year%/%monthnum%/%postname%/' --allow-root + +wp db export "${PROJECT_DIR}/tests/_data/dump.sql" --allow-root + +exec "$@" diff --git a/docker/testing.Dockerfile b/docker/testing.Dockerfile new file mode 100644 index 0000000..cdec274 --- /dev/null +++ b/docker/testing.Dockerfile @@ -0,0 +1,44 @@ +############################################################################ +# Container for running Codeception tests on a WPGraphQL Docker instance. # +############################################################################ + +# Using the 'DESIRED_' prefix to avoid confusion with environment variables of the same name. +FROM wpgraphql-acf-app:latest + +LABEL author=jasonbahl +LABEL author_uri=https://github.com/jasonbahl + +SHELL [ "/bin/bash", "-c" ] + +# Install php extensions +RUN docker-php-ext-install pdo_mysql + +# Install PCOV +# This is needed for Codeception / PHPUnit to track code coverage +RUN apt-get install zip unzip -y \ + && pecl install pcov + +ENV COVERAGE=0 +ENV SUITES=${SUITES:-zz} + +# Install composer +ENV COMPOSER_ALLOW_SUPERUSER=1 + +RUN curl -sS https://getcomposer.org/installer | php -- \ + --filename=composer \ + --install-dir=/usr/local/bin + +# Add composer global binaries to PATH +ENV PATH "$PATH:~/.composer/vendor/bin" + +# Configure php +RUN echo "date.timezone = UTC" >> /usr/local/etc/php/php.ini + +# Remove exec statement from base entrypoint script. +RUN sed -i '$d' /usr/local/bin/app-entrypoint.sh + +# Set up entrypoint +WORKDIR /var/www/html/wp-content/plugins/wp-graphql-acf +COPY docker/testing.entrypoint.sh /usr/local/bin/testing-entrypoint.sh +RUN chmod 755 /usr/local/bin/testing-entrypoint.sh +ENTRYPOINT ["testing-entrypoint.sh"] diff --git a/docker/testing.entrypoint.sh b/docker/testing.entrypoint.sh new file mode 100644 index 0000000..d812b6b --- /dev/null +++ b/docker/testing.entrypoint.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +# Processes parameters and runs Codeception. +run_tests() { + if [[ -n "$COVERAGE" ]]; then + local coverage="--coverage --coverage-xml" + fi + if [[ -n "$DEBUG" ]]; then + local debug="--debug" + fi + + local suites=$1 + if [[ -z "$suites" ]]; then + echo "No test suites specified. Must specify variable SUITES." + exit 1 + fi + + for suite in $suites ; do + echo "Running Test Suite $suite" + vendor/bin/codecept run -c codeception.dist.yml ${suite} ${coverage:-} ${debug:-} --no-exit + done +} + +# Exits with a status of 0 (true) if provided version number is higher than proceeding numbers. +version_gt() { + test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; +} + +write_htaccess() { + echo " +RewriteEngine On +RewriteBase / +SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=\$1 +RewriteRule ^index\.php$ - [L] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . /index.php [L] +" >> ${WP_ROOT_FOLDER}/.htaccess +} + +# Move to WordPress root folder +workdir="$PWD" +echo "Moving to WordPress root directory." +cd ${WP_ROOT_FOLDER} + +# Run app entrypoint script. +. app-entrypoint.sh + +write_htaccess + +# Return to PWD. +echo "Moving back to project working directory." +cd ${workdir} + +# Ensure Apache is running +service apache2 start + +# Ensure everything is loaded +dockerize \ + -wait tcp://${DB_HOST}:${DB_HOST_PORT:-3306} \ + -wait ${WP_URL} \ + -timeout 1m + +# Download c3 for testing. +if [ ! -f "$PROJECT_DIR/c3.php" ]; then + echo "Downloading Codeception's c3.php" + curl -L 'https://raw.github.com/Codeception/c3/2.0/c3.php' > "$PROJECT_DIR/c3.php" +fi + +# Install dependencies +echo "Running composer update" +COMPOSER_MEMORY_LIMIT=-1 composer update +echo "Running composer install" +COMPOSER_MEMORY_LIMIT=-1 composer install --no-interaction + +# Install pcov/clobber if PHP7.1+ +if version_gt $PHP_VERSION 7.0 && [[ -n "$COVERAGE" ]] && [[ -z "$USING_XDEBUG" ]]; then + echo "Using pcov/clobber for codecoverage" + docker-php-ext-enable pcov + echo "pcov.enabled=1" >> /usr/local/etc/php/conf.d/docker-php-ext-pcov.ini + echo "pcov.directory = /var/www/html/wp-content/plugins/wp-graphql" >> /usr/local/etc/php/conf.d/docker-php-ext-pcov.ini + COMPOSER_MEMORY_LIMIT=-1 composer require --dev pcov/clobber + vendor/bin/pcov clobber +elif [[ -n "$COVERAGE" ]] && [[ -n "$USING_XDEBUG" ]]; then + echo "Using XDebug for codecoverage" +fi + +# Set output permission +echo "Setting Codeception output directory permissions" +chmod 777 ${TESTS_OUTPUT} + +# Run tests +run_tests "${SUITES}" + +# Remove c3.php +if [ -f "$PROJECT_DIR/c3.php" ] && [ "$SKIP_TESTS_CLEANUP" != "1" ]; then + echo "Removing Codeception's c3.php" + rm -rf "$PROJECT_DIR/c3.php" +fi + +# Clean coverage.xml and clean up PCOV configurations. +if [ -f "${TESTS_OUTPUT}/coverage.xml" ] && [[ -n "$COVERAGE" ]]; then + echo 'Cleaning coverage.xml for deployment'. + pattern="$PROJECT_DIR/" + sed -i "s~$pattern~~g" "$TESTS_OUTPUT"/coverage.xml + + # Remove pcov/clobber + if version_gt $PHP_VERSION 7.0 && [[ -z "$SKIP_TESTS_CLEANUP" ]] && [[ -z "$USING_XDEBUG" ]]; then + echo 'Removing pcov/clobber.' + vendor/bin/pcov unclobber + COMPOSER_MEMORY_LIMIT=-1 composer remove --dev pcov/clobber + rm /usr/local/etc/php/conf.d/docker-php-ext-pcov.ini + fi + +fi + +if [[ -z "$SKIP_TESTS_CLEANUP" ]]; then + echo 'Changing composer configuration in container.' + composer config --global discard-changes true + + echo 'Removing devDependencies.' + composer install --no-dev -n + + echo 'Removing composer.lock' + rm composer.lock +fi + +# Set public test result files permissions. +if [ -n "$(ls "$TESTS_OUTPUT")" ]; then + echo 'Setting result files permissions'. + chmod 777 -R "$TESTS_OUTPUT"/* +fi + + +# Check results and exit accordingly. +if [ -f "${TESTS_OUTPUT}/failed" ]; then + echo "Uh oh, something went wrong." + exit 1 +else + echo "Woohoo! It's working!" +fi diff --git a/docs/field-group-examples-export.json b/docs/field-group-examples-export.json new file mode 100644 index 0000000..7a0f024 --- /dev/null +++ b/docs/field-group-examples-export.json @@ -0,0 +1,883 @@ +[ + { + "key": "group_60468a2b40d13", + "title": "ACF Docs", + "fields": [ + { + "key": "field_60468a428ad20", + "label": "Text", + "name": "text", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "prepend": "", + "append": "", + "maxlength": "" + }, + { + "key": "field_60468c101f5bc", + "label": "Text Area", + "name": "text_area", + "type": "textarea", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "maxlength": "", + "rows": "", + "new_lines": "" + }, + { + "key": "field_60468c261f5bd", + "label": "Number", + "name": "number", + "type": "number", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "prepend": "", + "append": "", + "min": "", + "max": "", + "step": "" + }, + { + "key": "field_60468c7d1f5be", + "label": "Range", + "name": "range", + "type": "range", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "min": "", + "max": "", + "step": "", + "prepend": "", + "append": "" + }, + { + "key": "field_60468d7ed5271", + "label": "Email", + "name": "email", + "type": "email", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "prepend": "", + "append": "" + }, + { + "key": "field_60468db4a3624", + "label": "Url", + "name": "url", + "type": "url", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "" + }, + { + "key": "field_60468dd9a7390", + "label": "Password", + "name": "password", + "type": "password", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "placeholder": "", + "prepend": "", + "append": "" + }, + { + "key": "field_60468e38c3039", + "label": "Image", + "name": "image", + "type": "image", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "return_format": "array", + "preview_size": "medium", + "library": "all", + "min_width": "", + "min_height": "", + "min_size": "", + "max_width": "", + "max_height": "", + "max_size": "", + "mime_types": "" + }, + { + "key": "field_6046909b38734", + "label": "File", + "name": "file", + "type": "file", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "return_format": "array", + "library": "all", + "min_size": "", + "max_size": "", + "mime_types": "" + }, + { + "key": "field_604690bb38735", + "label": "Wysiwyg", + "name": "wysiwyg", + "type": "wysiwyg", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "tabs": "all", + "toolbar": "full", + "media_upload": 1, + "delay": 0 + }, + { + "key": "field_604690cd38736", + "label": "Oembed", + "name": "oembed", + "type": "oembed", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "width": "", + "height": "" + }, + { + "key": "field_6047cac3147ba", + "label": "Gallery", + "name": "gallery", + "type": "gallery", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "return_format": "array", + "preview_size": "medium", + "insert": "append", + "library": "all", + "min": "", + "max": "", + "min_width": "", + "min_height": "", + "min_size": "", + "max_width": "", + "max_height": "", + "max_size": "", + "mime_types": "" + }, + { + "key": "field_604690da38737", + "label": "Select", + "name": "select", + "type": "select", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "choices": { + "choice_1": "Choice 1", + "choice_2": "Choice 2" + }, + "default_value": false, + "allow_null": 0, + "multiple": 0, + "ui": 0, + "return_format": "value", + "ajax": 0, + "placeholder": "" + }, + { + "key": "field_60469107346a9", + "label": "Checkbox", + "name": "checkbox", + "type": "checkbox", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "choices": { + "choice_1": "Choice 1", + "choice_2": "Choice 2" + }, + "allow_custom": 0, + "default_value": [], + "layout": "vertical", + "toggle": 0, + "return_format": "value", + "save_custom": 0 + }, + { + "key": "field_6046914753efc", + "label": "Radio Button", + "name": "radio_button", + "type": "radio", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "choices": { + "choice_1": "Choice 1", + "choice_2": "Choice 2" + }, + "allow_null": 0, + "other_choice": 0, + "default_value": "", + "layout": "vertical", + "return_format": "value", + "save_other_choice": 0 + }, + { + "key": "field_6046917b53efd", + "label": "Button Group", + "name": "button_group", + "type": "button_group", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "choices": { + "choice_1": "Choice 1", + "choice_2": "Choice 2" + }, + "allow_null": 0, + "default_value": "", + "layout": "horizontal", + "return_format": "value" + }, + { + "key": "field_604691d753ce6", + "label": "True False", + "name": "true_false", + "type": "true_false", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "message": "", + "default_value": 0, + "ui": 0, + "ui_on_text": "", + "ui_off_text": "" + }, + { + "key": "field_6046928a53ce7", + "label": "Link", + "name": "link", + "type": "link", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "return_format": "array" + }, + { + "key": "field_604692a202533", + "label": "Post Object", + "name": "post_object", + "type": "post_object", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "post_type": [ + "post", + "page" + ], + "taxonomy": "", + "allow_null": 0, + "multiple": 1, + "return_format": "object", + "ui": 1 + }, + { + "key": "field_60469560e9da6", + "label": "Page Link", + "name": "page_link", + "type": "page_link", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "post_type": [ + "post", + "page" + ], + "taxonomy": "", + "allow_null": 0, + "allow_archives": 1, + "multiple": 0 + }, + { + "key": "field_60469ad3e9da7", + "label": "Relationship", + "name": "relationship", + "type": "relationship", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "post_type": [ + "post", + "page" + ], + "taxonomy": "", + "filters": [ + "search", + "post_type", + "taxonomy" + ], + "elements": "", + "min": "", + "max": "", + "return_format": "object" + }, + { + "key": "field_60469bf265bd6", + "label": "Taxonomy", + "name": "taxonomy", + "type": "taxonomy", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "taxonomy": "category", + "field_type": "checkbox", + "add_term": 1, + "save_terms": 0, + "load_terms": 0, + "return_format": "id", + "multiple": 0, + "allow_null": 0 + }, + { + "key": "field_60469c1665bd7", + "label": "User", + "name": "user", + "type": "user", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "role": "", + "allow_null": 0, + "multiple": 1, + "return_format": "array" + }, + { + "key": "field_60469c2065bd8", + "label": "Google Map", + "name": "google_map", + "type": "google_map", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "center_lat": "", + "center_lng": "", + "zoom": "", + "height": "" + }, + { + "key": "field_60469d0dc197a", + "label": "Date Picker", + "name": "date_picker", + "type": "date_picker", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "display_format": "d\/m\/Y", + "return_format": "d\/m\/Y", + "first_day": 1 + }, + { + "key": "field_60469d19c197b", + "label": "Date Time Picker", + "name": "date_time_picker", + "type": "date_time_picker", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "display_format": "d\/m\/Y g:i a", + "return_format": "d\/m\/Y g:i a", + "first_day": 1 + }, + { + "key": "field_60469d27c197c", + "label": "Time Picker", + "name": "time_picker", + "type": "time_picker", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "display_format": "g:i a", + "return_format": "g:i a" + }, + { + "key": "field_60469d34c197d", + "label": "Color Picker", + "name": "color_picker", + "type": "color_picker", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "" + }, + { + "key": "field_60469d5933bce", + "label": "Group", + "name": "group", + "type": "group", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "layout": "block", + "sub_fields": [ + { + "key": "field_6047ecb8e5cbc", + "label": "Text Field In Group", + "name": "text_field_in_group", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "prepend": "", + "append": "", + "maxlength": "" + }, + { + "key": "field_6047eccce5cbd", + "label": "Text Area Field In Group", + "name": "text_area_field_in_group", + "type": "textarea", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "maxlength": "", + "rows": "", + "new_lines": "" + } + ] + }, + { + "key": "field_6047cb430101c", + "label": "Repeater", + "name": "repeater", + "type": "repeater", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "collapsed": "", + "min": 0, + "max": 0, + "layout": "table", + "button_label": "", + "sub_fields": [ + { + "key": "field_6047cb620101d", + "label": "Text Field In Repeater", + "name": "text_field_in_repeater", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "prepend": "", + "append": "", + "maxlength": "" + }, + { + "key": "field_6047cb740101e", + "label": "Image Field In Repeater", + "name": "image_field_in_repeater", + "type": "image", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "return_format": "array", + "preview_size": "medium", + "library": "all", + "min_width": "", + "min_height": "", + "min_size": "", + "max_width": "", + "max_height": "", + "max_size": "", + "mime_types": "" + } + ] + }, + { + "key": "field_6047cb92951ce", + "label": "Flexible Content", + "name": "flexible_content", + "type": "flexible_content", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "layouts": { + "layout_6047cb980608a": { + "key": "layout_6047cb980608a", + "name": "layout_one", + "label": "Layout One", + "display": "block", + "sub_fields": [ + { + "key": "field_6047cc58951cf", + "label": "Text", + "name": "text", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "prepend": "", + "append": "", + "maxlength": "" + }, + { + "key": "field_6047cc9b951d0", + "label": "Another Text Field", + "name": "another_text_field", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "default_value": "", + "placeholder": "", + "prepend": "", + "append": "", + "maxlength": "" + } + ], + "min": "", + "max": "" + }, + "layout_6047eee191715": { + "key": "layout_6047eee191715", + "name": "layout_two", + "label": "Layout Two", + "display": "block", + "sub_fields": [ + { + "key": "field_6047eeeb91716", + "label": "Image", + "name": "image", + "type": "image", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "return_format": "array", + "preview_size": "medium", + "library": "all", + "min_width": "", + "min_height": "", + "min_size": "", + "max_width": "", + "max_height": "", + "max_size": "", + "mime_types": "" + } + ], + "min": "", + "max": "" + }, + "layout_6047eefa91717": { + "key": "layout_6047eefa91717", + "name": "layout_three", + "label": "Layout Three", + "display": "block", + "sub_fields": [ + { + "key": "field_6047ef0291718", + "label": "Gallery", + "name": "gallery", + "type": "gallery", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "show_in_graphql": 1, + "return_format": "array", + "preview_size": "medium", + "insert": "append", + "library": "all", + "min": "", + "max": "", + "min_width": "", + "min_height": "", + "min_size": "", + "max_width": "", + "max_height": "", + "max_size": "", + "mime_types": "" + } + ], + "min": "", + "max": "" + } + }, + "button_label": "Add Row", + "min": "", + "max": "" + } + ], + "location": [ + [ + { + "param": "post_type", + "operator": "==", + "value": "post" + } + ] + ], + "menu_order": 0, + "position": "normal", + "style": "default", + "label_placement": "top", + "instruction_placement": "label", + "hide_on_screen": "", + "active": true, + "description": "ACF Documentation Examples", + "show_in_graphql": 1, + "graphql_field_name": "acfDocs" + } +] \ No newline at end of file diff --git a/readme.txt b/readme.txt index 7e7458a..0b68998 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://wpgraphql.com/acf Tags: WPGraphQL, GraphQL, API, Advanced Custom Fields, ACF Requires at least: 5.0 Tested up to: 5.1.1 -Stable tag: 0.4.1 +Stable tag: 0.5.0 License: GPL-3 License URI: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/src/class-acf.php b/src/class-acf.php index c925a25..e7f8e21 100644 --- a/src/class-acf.php +++ b/src/class-acf.php @@ -7,6 +7,8 @@ namespace WPGraphQL\ACF; +use GraphQL\Type\Definition\ResolveInfo; + /** * Final class ACF */ @@ -85,11 +87,6 @@ public function __wakeup() { */ private function setup_constants() { - // Plugin version. - if ( ! defined( 'WPGRAPHQL_ACF_VERSION' ) ) { - define( 'WPGRAPHQL_ACF_VERSION', '0.3.0' ); - } - // Plugin Folder Path. if ( ! defined( 'WPGRAPHQL_ACF_PLUGIN_DIR' ) ) { define( 'WPGRAPHQL_ACF_PLUGIN_DIR', plugin_dir_path( __FILE__ . '/..' ) ); @@ -132,6 +129,19 @@ private function actions() { */ private function filters() { + /** + * This filters any field that returns the `ContentTemplate` type + * to pass the source node down to the template for added context + */ + add_filter( 'graphql_resolve_field', function( $result, $source, $args, $context, ResolveInfo $info, $type_name, $field_key, $field, $field_resolver ) { + if ( isset( $info->returnType ) && strtolower( 'ContentTemplate' ) === strtolower( $info->returnType ) ) { + if ( is_array( $result ) && ! isset( $result['node'] ) && ! empty( $source ) ) { + $result['node'] = $source; + } + } + return $result; + }, 10, 9 ); + } /** diff --git a/src/class-acfsettings.php b/src/class-acfsettings.php index ae19d78..ff8292e 100644 --- a/src/class-acfsettings.php +++ b/src/class-acfsettings.php @@ -19,64 +19,84 @@ class ACF_Settings { */ public function init() { - /** - * Creates a field group setting to allow a field group to be - * shown in the GraphQL Schema. - */ - add_action( 'acf/render_field_group_settings', [ $this, 'add_field_group_settings' ], 10, 1 ); - /** * Add settings to individual fields to allow each field granular control * over how it's shown in the GraphQL Schema */ add_action( 'acf/render_field_settings', [ $this, 'add_field_settings' ], 10, 1 ); + /** + * Enqueue scripts to enhance the UI of the ACF Field Group Settings + */ + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_graphql_acf_scripts' ], 10, 1 ); + + /** + * Register meta boxes for the ACF Field Group Settings + */ + add_action( 'add_meta_boxes', [ $this, 'register_meta_boxes' ] ); + + /** + * Register an AJAX action and callback for converting ACF Location rules to GraphQL Types + */ + add_action( 'wp_ajax_get_acf_field_group_graphql_types', [ $this, 'ajax_callback' ] ); + } /** - * Add settings to each field to show in GraphQL + * Handle the AJAX callback for converting ACF Location settings to GraphQL Types * - * @param array $field The field to add the setting to. + * @return void */ - public function add_field_settings( $field ) { + public function ajax_callback() { - $supported_fields = Config::get_supported_fields(); + if ( isset( $_POST['data'] ) ) { - /** - * If there are no supported fields, or the field is not supported, don't add a setting field. - */ - if ( empty( $supported_fields ) || ! is_array( $supported_fields ) || ! in_array( $field['type'], $supported_fields, true ) ) { - return; - } + $form_data = []; - /** - * Render the "show_in_graphql" setting for the field. - */ - acf_render_field_setting( - $field, - [ - 'label' => __( 'Show in GraphQL', 'wp-graphql-acf' ), - 'instructions' => __( 'Whether the field should be queryable via GraphQL', 'wp-graphql-acf' ), - 'name' => 'show_in_graphql', - 'type' => 'true_false', - 'ui' => 1, - 'default_value' => 1, - 'value' => isset( $field['show_in_graphql'] ) ? (bool) $field['show_in_graphql'] : true, - ], - true - ); + parse_str( $_POST['data'], $form_data ); + + if ( empty( $form_data ) || ! isset( $form_data['acf_field_group'] ) ) { + wp_send_json( __( 'No form data.', 'wp-graphql-acf' ) ); + } + + $field_group = isset( $form_data['acf_field_group'] ) ? $form_data['acf_field_group'] : []; + $rules = new LocationRules( [ $field_group ] ); + $rules->determine_location_rules(); + + $group_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : $field_group['title']; + $group_name = $rules->format_field_name( $group_name ); + $all_rules = $rules->get_rules(); + if ( isset( $all_rules[ $group_name ] ) ) { + wp_send_json( [ 'graphql_types' => array_values( $all_rules[ $group_name ] ) ] ); + } + wp_send_json( [ 'graphql_types' => null ] ); + } + + echo __( 'No location rules were found', 'wp-graphql-acf' ); + wp_die(); } /** - * This adds a setting to the ACF Field groups to activate a field group in GraphQL. + * Register the GraphQL Settings metabox for the ACF Field Group post type * - * If a field group is set to active and is set to "show_in_graphql", the fields in the field - * group will be exposed to the GraphQL Schema based on the matching location rules. + * @return void + */ + public function register_meta_boxes() { + add_meta_box( 'wpgraphql-acf-meta-box', __( 'GraphQL', 'wp-graphql-acf' ), [ + $this, + 'display_metabox' + ], [ 'acf-field-group' ] ); + } + + /** + * Display the GraphQL Settings Metabox on the Field Group admin page * - * @param array $field_group The field group to add settings to. + * @param $field_group_post_object */ - public function add_field_group_settings( $field_group ) { + public function display_metabox( $field_group_post_object ) { + + global $field_group; /** * Render a field in the Field Group settings to allow for a Field Group to be shown in GraphQL. @@ -94,21 +114,118 @@ public function add_field_group_settings( $field_group ) { ); /** - * Render a field in the Field Group settings to allow for a Field Group to be shown in GraphQL. + * Render a field in the Field Group settings to set the GraphQL field name for the field group. */ acf_render_field_wrap( [ 'label' => __( 'GraphQL Field Name', 'acf' ), - 'instructions' => __( 'The name of the field group in the GraphQL Schema.', 'wp-graphql-acf' ), + 'instructions' => __( 'The name of the field group in the GraphQL Schema. Names should not include spaces or special characters. Best practice is to use "camelCase".', 'wp-graphql-acf' ), 'type' => 'text', 'prefix' => 'acf_field_group', 'name' => 'graphql_field_name', - 'required' => true, + 'required' => isset( $field_group['show_in_graphql'] ) ? (bool) $field_group['show_in_graphql'] : false, 'placeholder' => ! empty( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : null, 'value' => ! empty( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : null, ] ); + acf_render_field_wrap( + [ + 'label' => __( 'Manually Set GraphQL Types for Field Group', 'acf' ), + 'instructions' => __( 'By default, ACF Field groups are added to the GraphQL Schema based on the field group\'s location rules. Checking this box will let you manually control the GraphQL Types the field group should be shown on in the GraphQL Schema using the checkboxes below, and the Location Rules will no longer effect the GraphQL Types.', 'wp-graphql-acf' ), + 'type' => 'true_false', + 'name' => 'map_graphql_types_from_location_rules', + 'prefix' => 'acf_field_group', + 'value' => isset( $field_group['map_graphql_types_from_location_rules'] ) ? (bool) $field_group['map_graphql_types_from_location_rules'] : false, + 'ui' => 1, + ] + ); + + $choices = Config::get_all_graphql_types(); + acf_render_field_wrap( + [ + 'label' => __( 'GraphQL Types to Show the Field Group On', 'wp-graphql-acf' ), + 'instructions' => __( 'Select the Types in the WPGraphQL Schema to show the fields in this field group on', 'wp-graphql-acf' ), + 'type' => 'checkbox', + 'prefix' => 'acf_field_group', + 'name' => 'graphql_types', + 'value' => ! empty( $field_group['graphql_types'] ) ? $field_group['graphql_types'] : [], + 'toggle' => true, + 'choices' => $choices, + ] + ); + + ?> +
+ +
+ + __( 'Show in GraphQL', 'wp-graphql-acf' ), + 'instructions' => __( 'Whether the field should be queryable via GraphQL', 'wp-graphql-acf' ), + 'name' => 'show_in_graphql', + 'type' => 'true_false', + 'ui' => 1, + 'default_value' => 1, + 'value' => isset( $field['show_in_graphql'] ) ? (bool) $field['show_in_graphql'] : true, + ], + true + ); + + } + + /** + * This enqueues admin script. + * + * @param string $screen The screen that scripts are being enqueued to + * + * @return void + */ + public function enqueue_graphql_acf_scripts( string $screen ) { + global $post; + + if ( $screen == 'post-new.php' || $screen == 'post.php' ) { + if ( 'acf-field-group' === $post->post_type ) { + wp_enqueue_script( 'graphql-acf', plugins_url( 'src/js/main.js', dirname( __FILE__ ) ), array( + 'jquery', + 'acf-input', + 'acf-field-group' + ) ); + } + } } } diff --git a/src/class-config.php b/src/class-config.php index 7a6014b..d13003c 100644 --- a/src/class-config.php +++ b/src/class-config.php @@ -7,6 +7,7 @@ namespace WPGraphQL\ACF; +use Exception; use GraphQL\Type\Definition\ResolveInfo; use WPGraphQL\AppContext; use WPGraphQL\Data\DataSource; @@ -16,43 +17,64 @@ use WPGraphQL\Model\Post; use WPGraphQL\Model\Term; use WPGraphQL\Model\User; +use WPGraphQL\Registry\TypeRegistry; +use WPGraphQL\Utils\Utils; /** * Config class. */ class Config { + /** + * @var TypeRegistry + */ protected $type_registry; + /** + * Stores the location rules for back compat + * @var array + */ + protected $location_rules = []; + /** * @var array List of field names registered to the Schema */ protected $registered_field_names; + /** + * @var array List of options page slugs registered to the Schema + */ + protected $registered_options_pages = []; + /** * Initialize WPGraphQL to ACF * - * @param \WPGraphQL\Registry\TypeRegistry $type_registry Instance of the WPGraphQL TypeRegistry + * @param TypeRegistry $type_registry Instance of the WPGraphQL TypeRegistry + * + * @throws Exception */ - public function init( \WPGraphQL\Registry\TypeRegistry $type_registry ) { + public function init( TypeRegistry $type_registry ) { /** * Set the TypeRegistry */ $this->type_registry = $type_registry; + $this->register_initial_types(); + + /** + * Gets the location rules for backward compatibility. + * + * This allows for ACF Field Groups that were registered before the "graphql_types" + * field was respected can still work with the old GraphQL Schema rules that mapped + * from the ACF Location rules. + */ + $this->location_rules = $this->get_location_rules(); /** * Add ACF Fields to GraphQL Types */ - $this->add_acf_fields_to_post_object_types(); - $this->add_acf_fields_to_term_objects(); - $this->add_acf_fields_to_comments(); - $this->add_acf_fields_to_menus(); - $this->add_acf_fields_to_menu_items(); - $this->add_acf_fields_to_media_items(); - $this->add_acf_fields_to_individual_posts(); - $this->add_acf_fields_to_users(); - $this->add_acf_fields_to_options_pages(); + $this->add_options_pages_to_schema(); + $this->add_acf_fields_to_graphql_types(); // This filter tells WPGraphQL to resolve revision meta for ACF fields from the revision's meta, instead // of the parent (published post) meta. @@ -96,6 +118,175 @@ public function init( \WPGraphQL\Registry\TypeRegistry $type_registry ) { }, 10, 4 ); } + /** + * Registers initial Types for use with ACF Fields + * + * @throws Exception + */ + public function register_initial_types() { + + $this->type_registry->register_interface_type( + 'AcfFieldGroup', + [ + 'description' => __( 'A Field Group registered by ACF', 'wp-graphql-acf' ), + 'fields' => [ + 'fieldGroupName' => [ + 'description' => __( 'The name of the ACF Field Group', 'wp-graphql-acf' ), + 'type' => 'String', + ], + ] + ] + ); + + $this->type_registry->register_object_type( + 'AcfLink', + [ + 'description' => __( 'ACF Link field', 'wp-graphql-acf' ), + 'fields' => [ + 'url' => [ + 'type' => 'String', + 'description' => __( 'The url of the link', 'wp-graphql-acf' ), + ], + 'title' => [ + 'type' => 'String', + 'description' => __( 'The title of the link', 'wp-graphql-acf' ), + ], + 'target' => [ + 'type' => 'String', + 'description' => __( 'The target of the link (_blank, etc)', 'wp-graphql-acf' ), + ], + ], + ] + ); + + } + + + + /** + * Gets the location rules + * @return array + */ + protected function get_location_rules() { + + $field_groups = acf_get_field_groups(); + if ( empty( $field_groups ) || ! is_array( $field_groups ) ) { + return []; + } + + $rules = []; + + // Each field group that doesn't have GraphQL Types explicitly set should get the location + // rules interpreted. + foreach ( $field_groups as $field_group ) { + if ( ! isset( $field_group['graphql_types'] ) || ! is_array( $field_group['graphql_types'] ) ) { + $rules[] = $field_group; + } + } + + if ( empty( $rules ) ) { + return []; + } + + // If there are field groups with no graphql_types field set, inherit the rules from + // ACF Location Rules + $rules = new LocationRules(); + $rules->determine_location_rules(); + return $rules->get_rules(); + } + + protected function add_options_pages_to_schema() { + + global $acf_options_page; + + if ( ! isset( $acf_options_page ) ) { + return ; + } + + /** + * Get a list of post types that have been registered to show in graphql + */ + $graphql_options_pages = acf_get_options_pages(); + + /** + * If there are no post types exposed to GraphQL, bail + */ + if ( empty( $graphql_options_pages ) || ! is_array( $graphql_options_pages ) ) { + return; + } + + $options_pages_to_register = []; + + /** + * Loop over the post types exposed to GraphQL + */ + foreach ( $graphql_options_pages as $options_page_key => $options_page ) { + if ( ! isset( $options_page['show_in_graphql'] ) || false === (bool) $options_page['show_in_graphql'] ) { + continue; + } + + /** + * Get options page properties. + */ + $page_title = $options_page['page_title']; + $page_slug = $options_page['menu_slug']; + $type_name = isset( $options_page['graphql_field_name'] ) ? Utils::format_type_name( $options_page['graphql_field_name'] ) : Utils::format_type_name( $options_page['menu_slug'] ); + + $options_pages_to_register[ $type_name ] = [ + 'title' => $page_title, + 'slug' => $page_slug, + 'type_name' => $type_name, + 'options_page' => $options_page, + ]; + + } + + if ( is_array( $options_pages_to_register ) && ! empty( $options_pages_to_register ) ) { + + foreach ( $options_pages_to_register as $page_to_register ) { + + $page_title = $page_to_register['title']; + $page_slug = $page_to_register['slug']; + $type_name = isset( $page_to_register['type_name'] ) ? Utils::format_type_name( $page_to_register['type_name'] ) : Utils::format_type_name( $page_to_register['slug'] ); + $options_page = $page_to_register['options_page']; + + $this->type_registry->register_object_type( $type_name, [ + 'description' => sprintf( __( '%s options.', 'wp-graphql-acf' ), $page_title ), + 'fields' => [ + 'pageTitle' => [ + 'type' => 'String', + 'resolve' => function( $source ) use ( $page_title ) { + return ! empty( $page_title ) ? $page_title : null; + }, + ], + 'pageSlug' => [ + 'type' => 'String', + 'resolve' => function( $source ) use ( $page_slug ) { + return ! empty( $page_slug ) ? $page_slug : null; + }, + ], + ], + ] ); + + $field_name = Utils::format_field_name( $type_name ); + + $this->type_registry->register_field( + 'RootQuery', + $field_name, + [ + 'type' => $type_name, + 'description' => sprintf( __( '%s options.', 'wp-graphql-acf' ), $page_title ), + 'resolve' => function() use ( $options_page ) { + return ! empty( $options_page ) ? $options_page : null; + } + ] + ); + + } + } + + } + /** * Determines whether a field group should be exposed to the GraphQL Schema. By default, field * groups will not be exposed to GraphQL. @@ -168,77 +359,6 @@ public static function camel_case( $str, array $no_strip = [] ) { return $str; } - /** - * Add ACF Fields to Post Object Types. - * - * This gets the Post Types that are configured to show_in_graphql and iterates - * over them to expose ACF Fields to their Type in the GraphQL Schema. - */ - protected function add_acf_fields_to_post_object_types() { - - /** - * Get a list of post types that have been registered to show in graphql - */ - $graphql_post_types = get_post_types( [ 'show_in_graphql' => true ] ); - - /** - * If there are no post types exposed to GraphQL, bail - */ - if ( empty( $graphql_post_types ) || ! is_array( $graphql_post_types ) ) { - return; - } - - /** - * Loop over the post types exposed to GraphQL - */ - foreach ( $graphql_post_types as $post_type ) { - - /** - * Get the field groups associated with the post type - */ - $field_groups = acf_get_field_groups( - [ - 'post_type' => $post_type, - ] - ); - - /** - * If there are no field groups for this post type, move on to the next one. - */ - if ( empty( $field_groups ) || ! is_array( $field_groups ) ) { - continue; - } - - /** - * Get the post_type_object - */ - $post_type_object = get_post_type_object( $post_type ); - - /** - * Loop over the field groups for this post type - */ - foreach ( $field_groups as $field_group ) { - - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $config = [ - 'name' => $field_name, - 'description' => $field_group['description'], - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; - - $this->register_graphql_field( $post_type_object->graphql_single_name, $field_name, $config ); - } - } - - } - /** * Undocumented function * @@ -253,6 +373,10 @@ protected function get_acf_field_value( $root, $acf_field, $format = false ) { $value = null; $id = null; + if ( is_array( $root ) && isset( $root['node'] ) ) { + $id = $root['node']->ID; + } + if ( is_array( $root ) && ! ( ! empty( $root['type'] ) && 'options_page' === $root['type'] ) ) { if ( isset( $root[ $acf_field['key'] ] ) ) { @@ -266,23 +390,24 @@ protected function get_acf_field_value( $root, $acf_field, $format = false ) { } else { switch ( true ) { + case $root instanceof Term: - $id = acf_get_term_post_id( $root->taxonomyName, $root->term_id ); + $id = 'term_' . $root->term_id; break; case $root instanceof Post: - $id = absint( $root->ID ); + $id = absint( $root->databaseId ); break; case $root instanceof MenuItem: $id = absint( $root->menuItemId ); break; case $root instanceof Menu: - $id = acf_get_term_post_id( 'nav_menu', $root->menuId ); + $id = 'term_' . $root->menuId; break; case $root instanceof User: $id = 'user_' . absint( $root->userId ); break; case $root instanceof Comment: - $id = 'comment_' . absint( $root->comment_ID ); + $id = 'comment_' . absint( $root->databaseId ); break; case is_array( $root ) && ! empty( $root['type'] ) && 'options_page' === $root['type']: $id = $root['post_id']; @@ -291,12 +416,15 @@ protected function get_acf_field_value( $root, $acf_field, $format = false ) { $id = null; break; } + } + + if ( empty( $value ) ) { /** * Filters the root ID, allowing additional Models the ability to provide a way to resolve their ID * - * @param int $id The ID of the object. Default null - * @param mixed $root The Root object being resolved. The ID is typically a property of this object. + * @param int $id The ID of the object. Default null + * @param mixed $root The Root object being resolved. The ID is typically a property of this object. */ $id = apply_filters( 'graphql_acf_get_root_id', $id, $root ); @@ -310,6 +438,10 @@ protected function get_acf_field_value( $root, $acf_field, $format = false ) { $format = true; } + if ( 'select' === $acf_field['type'] ) { + $format = true; + } + /** * Check if cloned field and retrieve the key accordingly. */ @@ -322,6 +454,7 @@ protected function get_acf_field_value( $root, $acf_field, $format = false ) { $field_value = get_field( $key, $id, $format ); $value = ! empty( $field_value ) ? $field_value : null; + } /** @@ -392,13 +525,13 @@ public static function get_supported_fields() { /** * Undocumented function * - * @param [type] $type_name Undocumented. - * @param [type] $field_name Undocumented. - * @param [type] $config Undocumented. + * @param string $type_name The name of the GraphQL Type to add the field to. + * @param string $field_name The name of the field to add to the GraphQL Type. + * @param array $config The GraphQL configuration of the field. * * @return mixed */ - protected function register_graphql_field( $type_name, $field_name, $config ) { + protected function register_graphql_field( string $type_name, string $field_name, array $config ) { $acf_field = isset( $config['acf_field'] ) ? $config['acf_field'] : null; $acf_type = isset( $acf_field['type'] ) ? $acf_field['type'] : null; @@ -406,8 +539,6 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { return false; } - - /** * filter the field config for custom field types * @@ -417,7 +548,6 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { 'type' => null, 'resolve' => isset( $config['resolve'] ) && is_callable( $config['resolve'] ) ? $config['resolve'] : function( $root, $args, $context, $info ) use ( $acf_field ) { $value = $this->get_acf_field_value( $root, $acf_field ); - return ! empty( $value ) ? $value : null; }, ], $type_name, $field_name, $config ); @@ -466,7 +596,16 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { * @see: https://github.com/wp-graphql/wp-graphql-acf/issues/25 */ if ( empty( $acf_field['multiple'] ) ) { - $field_config['type'] = 'String'; + if('array' === $acf_field['return_format'] ){ + $field_config['type'] = [ 'list_of' => 'String' ]; + $field_config['resolve'] = function( $root ) use ( $acf_field) { + $value = $this->get_acf_field_value( $root, $acf_field, true); + + return ! empty( $value ) && is_array( $value ) ? $value : []; + }; + }else{ + $field_config['type'] = 'String'; + } } else { $field_config['type'] = [ 'list_of' => 'String' ]; $field_config['resolve'] = function( $root ) use ( $acf_field ) { @@ -479,10 +618,8 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { case 'radio': $field_config['type'] = 'String'; break; - case 'range': - $field_config['type'] = 'Float'; - break; case 'number': + case 'range': $field_config['type'] = 'Float'; break; case 'true_false': @@ -571,7 +708,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { } else { $type_names = []; foreach ( $acf_field['post_type'] as $post_type ) { - if ( in_array( $post_type, \get_post_types( [ 'show_in_graphql' => true ]), true ) ) { + if ( in_array( $post_type, \get_post_types( [ 'show_in_graphql' => true ] ), true ) ) { $type_names[ $post_type ] = get_post_type_object( $post_type )->graphql_single_name; } } @@ -608,7 +745,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { $return = []; if ( ! empty( $value ) ) { if ( is_array( $value ) ) { - foreach ($value as $id ) { + foreach ( $value as $id ) { $post = get_post( $id ); if ( ! empty( $post ) ) { $return[] = new Post( $post ); @@ -650,34 +787,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { ]; break; case 'link': - - $field_type_name = 'ACF_Link'; - if ( $this->type_registry->get_type( $field_type_name ) == $field_type_name ) { - $field_config['type'] = $field_type_name; - break; - } - - register_graphql_object_type( - $field_type_name, - [ - 'description' => __( 'ACF Link field', 'wp-graphql-acf' ), - 'fields' => [ - 'url' => [ - 'type' => 'String', - 'description' => __( 'The url of the link', 'wp-graphql-acf' ), - ], - 'title' => [ - 'type' => 'String', - 'description' => __( 'The title of the link', 'wp-graphql-acf' ), - ], - 'target' => [ - 'type' => 'String', - 'description' => __( 'The target of the link (_blank, etc)', 'wp-graphql-acf' ), - ], - ], - ] - ); - $field_config['type'] = $field_type_name; + $field_config['type'] = 'AcfLink'; break; case 'image': case 'file': @@ -736,7 +846,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { $return = []; if ( ! empty( $value ) ) { if ( is_array( $value ) ) { - foreach ($value as $id ) { + foreach ( $value as $id ) { $user = get_user_by( 'id', $id ); if ( ! empty( $user ) ) { $user = new User( $user ); @@ -778,7 +888,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { } } - $is_multiple = isset($acf_field['field_type']) && in_array( $acf_field['field_type'], array('checkbox', 'multi_select')); + $is_multiple = isset( $acf_field['field_type'] ) && in_array( $acf_field['field_type'], array( 'checkbox', 'multi_select' ) ); $field_config = [ 'type' => $is_multiple ? ['list_of' => $type ] : $type, @@ -805,19 +915,20 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { $field_config = null; break; case 'group': + $field_type_name = $type_name . '_' . ucfirst( self::camel_case( $acf_field['name'] ) ); - if ( $this->type_registry->get_type( $field_type_name ) ) { + if ( null !== $this->type_registry->get_type( $field_type_name ) ) { $field_config['type'] = $field_type_name; break; } - register_graphql_object_type( + $this->type_registry->register_object_type( $field_type_name, [ 'description' => __( 'Field Group', 'wp-graphql-acf' ), + 'interfaces' => [ 'AcfFieldGroup' ], 'fields' => [ 'fieldGroupName' => [ - 'type' => 'String', 'resolve' => function( $source ) use ( $acf_field ) { return ! empty( $acf_field['name'] ) ? $acf_field['name'] : null; }, @@ -826,10 +937,10 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { ] ); - $this->add_field_group_fields( $acf_field, $field_type_name ); $field_config['type'] = $field_type_name; + break; case 'google_map': @@ -865,7 +976,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { // ACF 5.8.6 added more data to Google Maps field value // https://www.advancedcustomfields.com/changelog/ - if (\acf_version_compare(acf_get_db_version(), '>=', '5.8.6')) { + if ( \acf_version_compare(acf_get_db_version(), '>=', '5.8.6' ) ) { $fields += [ 'streetName' => [ 'type' => 'String', @@ -940,7 +1051,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { ]; } - register_graphql_object_type( + $this->type_registry->register_object_type( $field_type_name, [ 'description' => __( 'Google Map field', 'wp-graphql-acf' ), @@ -957,13 +1068,13 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { break; } - register_graphql_object_type( + $this->type_registry->register_object_type( $field_type_name, [ 'description' => __( 'Field Group', 'wp-graphql-acf' ), + 'interfaces' => [ 'AcfFieldGroup' ], 'fields' => [ 'fieldGroupName' => [ - 'type' => 'String', 'resolve' => function( $source ) use ( $acf_field ) { return ! empty( $acf_field['name'] ) ? $acf_field['name'] : null; }, @@ -1037,11 +1148,11 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { } else { - register_graphql_object_type( $flex_field_layout_name, [ + $this->type_registry->register_object_type( $flex_field_layout_name, [ 'description' => __( 'Group within the flex field', 'wp-graphql-acf' ), + 'interfaces' => [ 'AcfFieldGroup' ], 'fields' => [ 'fieldGroupName' => [ - 'type' => 'String', 'resolve' => function( $source ) use ( $flex_field_layout_name ) { return ! empty( $flex_field_layout_name ) ? $flex_field_layout_name : null; }, @@ -1058,7 +1169,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { } } - register_graphql_union_type( $field_type_name, [ + $this->type_registry->register_union_type( $field_type_name, [ 'typeNames' => $union_types, 'resolveType' => function( $value ) use ( $union_types ) { return isset( $union_types[ $value['acf_fc_layout'] ] ) ? $this->type_registry->get_type( $union_types[ $value['acf_fc_layout'] ] ) : null; @@ -1082,7 +1193,6 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { } $config = array_merge( $config, $field_config ); - $this->registered_field_names[] = $acf_field['name']; return $this->type_registry->register_field( $type_name, $field_name, $config ); } @@ -1094,7 +1204,7 @@ protected function register_graphql_field( $type_name, $field_name, $config ) { * @param string $type_name The Type name in the GraphQL Schema to add fields to. * @param bool $layout Whether or not these fields are part of a Flex Content layout. */ - protected function add_field_group_fields( $field_group, $type_name, $layout = false ) { + protected function add_field_group_fields( array $field_group, string $type_name, $layout = false ) { /** * If the field group has the show_in_graphql setting configured, respect it's setting @@ -1175,613 +1285,211 @@ protected function add_field_group_fields( $field_group, $type_name, $layout = f } /** - * Add field groups to Taxonomies + * Returns all available GraphQL Types * - * @return void + * @return array */ - protected function add_acf_fields_to_term_objects() { - - /** - * Get a list of taxonomies that have been registered to show in graphql - */ - $graphql_taxonomies = \WPGraphQL::get_allowed_taxonomies(); - - /** - * If there are no taxonomies exposed to GraphQL, bail - */ - if ( empty( $graphql_taxonomies ) || ! is_array( $graphql_taxonomies ) ) { - return; + public static function get_all_graphql_types() { + $graphql_types = array(); + + // Use GraphQL to get the Interface and the Types that implement them + $query = ' + query GetPossibleTypes($name:String!){ + __type(name:$name){ + name + description + possibleTypes { + name + description + } + } } + '; + + $interfaces = [ + 'ContentNode' => [ + 'label' => __( 'Post Type', 'wp-graphql-acf' ), + 'plural_label' => __( 'All Post Types', 'wp-graphql-acf' ), + ], + 'TermNode' => [ + 'label' => __( 'Taxonomy', 'wp-graphql-acf' ), + 'plural_label' => __( 'All Taxonomies', 'wp-graphql-acf' ), + ], + 'ContentTemplate' => [ + 'label' => __( 'Page Template', 'wp-graphql-acf' ), + 'plural_label' => __( 'All Templates Assignable to Content', 'wp-graphql-acf' ), + ] + ]; - /** - * Loop over the taxonomies exposed to GraphQL - */ - foreach ( $graphql_taxonomies as $taxonomy ) { + foreach ( $interfaces as $interface_name => $config ) { - /** - * Get the field groups associated with the taxonomy - */ - $field_groups = acf_get_field_groups( - [ - 'taxonomy' => $taxonomy, + $interface_query = graphql([ + 'query' => $query, + 'variables' => [ + 'name' => $interface_name ] - ); - - /** - * If there are no field groups for this taxonomy, move on to the next one. - */ - if ( empty( $field_groups ) || ! is_array( $field_groups ) ) { - continue; - } - - /** - * Get the Taxonomy object - */ - $tax_object = get_taxonomy( $taxonomy ); + ]); - if ( empty( $tax_object ) || ! isset( $tax_object->graphql_single_name ) ) { - return; - } + $possible_types = $interface_query['data']['__type']['possibleTypes']; + asort( $possible_types ); - /** - * Loop over the field groups for this post type - */ - foreach ( $field_groups as $field_group ) { - - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $description = $field_group['description'] ? $field_group['description'] . ' | ' : ''; - $config = [ - 'name' => $field_name, - 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%1$s" was assigned to the "%2$s" taxonomy', 'wp-graphql-acf' ), $field_group['title'], $tax_object->name ), - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; + if ( ! empty( $possible_types ) && is_array( $possible_types ) ) { - $this->register_graphql_field( $tax_object->graphql_single_name, $field_name, $config ); - } - } - } + // Intentionally not translating "ContentNode Interface" as this is part of the GraphQL Schema and should not be translated. + $graphql_types[ $interface_name ] = '' . $interface_name . ' Interface (' . $config['plural_label'] . ')'; + $label = ' (' . $config['label'] . ')'; + foreach ( $possible_types as $type ) { + $type_label = $type['name'] . $label; + $type_key = $type['name']; - /** - * Add ACF Fields to comments - * - * @return void - */ - protected function add_acf_fields_to_comments() { - - $comment_field_groups = []; - - /** - * Get the field groups associated with the taxonomy - */ - $field_groups = acf_get_field_groups(); - - foreach ( $field_groups as $field_group ) { - if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) { - foreach ( $field_group['location'] as $locations ) { - if ( ! empty( $locations ) && is_array( $locations ) ) { - foreach ( $locations as $location ) { - if ( 'comment' === $location['param'] && '!=' === $location['operator'] ) { - continue; - } - if ( 'comment' === $location['param'] && '==' === $location['operator'] ) { - $comment_field_groups[] = $field_group; - } - } - } + $graphql_types[ $type_key ] = $type_label; } } - } - if ( empty( $comment_field_groups ) ) { - return; } /** - * Loop over the field groups for this post type + * Add comment to GraphQL types */ - foreach ( $comment_field_groups as $field_group ) { - - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $description = $field_group['description'] ? $field_group['description'] . ' | ' : ''; - $config = [ - 'name' => $field_name, - 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to Comments', 'wp-graphql-acf' ), $field_group['title'] ), - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; - - $this->register_graphql_field( 'Comment', $field_name, $config ); - - } - - } - - /** - * Add Fields to Menus in the GraphQL Schema - * - * @return void - */ - protected function add_acf_fields_to_menus() { - - $menu_field_groups = []; + $graphql_types['Comment'] = __( 'Comment', 'wp-graphql-acf' ); /** - * Get the field groups associated with the taxonomy + * Add menu to GraphQL types */ - $field_groups = acf_get_field_groups(); - - foreach ( $field_groups as $field_group ) { - if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) { - foreach ( $field_group['location'] as $locations ) { - if ( ! empty( $locations ) && is_array( $locations ) ) { - foreach ( $locations as $location ) { - if ( 'nav_menu' === $location['param'] && '!=' === $location['operator'] ) { - continue; - } - if ( 'nav_menu' === $location['param'] && '==' === $location['operator'] ) { - $menu_field_groups[] = $field_group; - break; - } - } - } - } - } - } + $graphql_types['Menu'] = __( 'Menu', 'wp-graphql-acf' ); - if ( empty( $menu_field_groups ) ) { - return; - } + /** + * Add menu items to GraphQL types + */ + $graphql_types['MenuItem'] = __( 'Menu Item', 'wp-graphql-acf' ); /** - * Loop over the field groups for this post type + * Add users to GraphQL types */ - foreach ( $menu_field_groups as $field_group ) { + $graphql_types['User'] = __( 'User', 'wp-graphql-acf' ); - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $description = $field_group['description'] ? $field_group['description'] . ' | ' : ''; - $config = [ - 'name' => $field_name, - 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to Menus', 'wp-graphql-acf' ), $field_group['title'] ), - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; + /** + * Add options pages to GraphQL types + */ + global $acf_options_page; - $this->register_graphql_field( 'Menu', $field_name, $config ); + if ( isset( $acf_options_page ) ) { + /** + * Get a list of post types that have been registered to show in graphql + */ + $graphql_options_pages = acf_get_options_pages(); - } + /** + * If there are no post types exposed to GraphQL, bail + */ + if ( ! empty( $graphql_options_pages ) && is_array( $graphql_options_pages ) ) { - } + /** + * Prepare type key prefix and label surfix + */ + $label = ' (' . __( 'ACF Options Page', 'wp-graphql-acf' ) . ')'; - /** - * Add ACF Field Groups to Menu Items - * - * @return void - */ - protected function add_acf_fields_to_menu_items() { + /** + * Loop over the post types exposed to GraphQL + */ + foreach ( $graphql_options_pages as $options_page_key => $options_page ) { + if ( ! isset( $options_page['show_in_graphql'] ) || false === (bool) $options_page['show_in_graphql'] ) { + continue; + } - $menu_item_field_groups = []; + /** + * Get options page properties. + */ + $page_title = $options_page['page_title']; + $type_label = $page_title . $label; + $type_name = isset( $options_page['graphql_field_name'] ) ? Utils::format_type_name( $options_page['graphql_field_name'] ) : Utils::format_type_name( $options_page['menu_slug'] ); - /** - * Get the field groups associated with the taxonomy - */ - $field_groups = acf_get_field_groups(); - foreach ( $field_groups as $field_group ) { - if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) { - foreach ( $field_group['location'] as $locations ) { - if ( ! empty( $locations ) && is_array( $locations ) ) { - foreach ( $locations as $location ) { - if ( 'nav_menu_item' === $location['param'] && '!=' === $location['operator'] ) { - continue; - } - if ( 'nav_menu_item' === $location['param'] && '==' === $location['operator'] ) { - $menu_item_field_groups[] = $field_group; - } - } - } + $graphql_types[ $type_name ] = $type_label; } } } - if ( empty( $menu_item_field_groups ) ) { - return; - } - - /** - * Loop over the field groups for this post type - */ - foreach ( $menu_item_field_groups as $field_group ) { - - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $description = $field_group['description'] ? $field_group['description'] . ' | ' : ''; - $config = [ - 'name' => $field_name, - 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to Menu Items', 'wp-graphql-acf' ), $field_group['title'] ), - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; - - $this->register_graphql_field( 'MenuItem', $field_name, $config ); - - } + return $graphql_types; } /** - * Add ACF Field Groups to Media Items (attachments) - * - * @return void + * Adds acf field groups to GraphQL types. */ - protected function add_acf_fields_to_media_items() { - - $media_item_field_groups = []; - + protected function add_acf_fields_to_graphql_types() { /** - * Get the field groups associated with the taxonomy + * Get all the field groups */ $field_groups = acf_get_field_groups(); - foreach ( $field_groups as $field_group ) { - if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) { - foreach ( $field_group['location'] as $locations ) { - if ( ! empty( $locations ) && is_array( $locations ) ) { - foreach ( $locations as $location ) { - if ( 'attachment' === $location['param'] && '!=' === $location['operator'] ) { - continue; - } - if ( 'attachment' === $location['param'] && '==' === $location['operator'] ) { - $media_item_field_groups[] = $field_group; - } - } - } - } - } - } - - if ( empty( $media_item_field_groups ) ) { - return; - } - /** - * Loop over the field groups for this post type + * If there are no acf field groups, bail */ - foreach ( $media_item_field_groups as $field_group ) { - - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $description = $field_group['description'] ? $field_group['description'] . ' | ' : ''; - $config = [ - 'name' => $field_name, - 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%s" was assigned to attachments', 'wp-graphql-acf' ), $field_group['title'] ), - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; - - $this->register_graphql_field( 'MediaItem', $field_name, $config ); - + if ( empty( $field_groups ) || ! is_array( $field_groups ) ) { + return; } - } - - protected function add_acf_fields_to_individual_posts() { - - $post_field_groups = []; - - /** - * Get the field groups associated with the taxonomy - */ - $field_groups = acf_get_field_groups(); - - $allowed_post_types = get_post_types( [ - 'show_ui' => true, - 'show_in_graphql' => true - ] ); /** - * Remove the `attachment` post_type, as it's treated special and we don't - * want to add field groups in the same way we do for other post types + * Loop over all the field groups */ - unset( $allowed_post_types['attachment'] ); - - foreach ( $field_groups as $field_group ) { - if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) { - foreach ( $field_group['location'] as $locations ) { - if ( ! empty( $locations ) && is_array( $locations ) ) { - foreach ( $locations as $location ) { - - /** - * If the operator is not equal to, we don't need to do anything, - * so we can just continue - */ - if ( '!=' === $location['operator'] ) { - continue; - } - /** - * If the param (the post_type) is in the array of allowed_post_types - */ - if ( in_array( $location['param'], $allowed_post_types, true ) && '==' === $location['operator'] ) { + $field_group_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : $field_group['title']; + $field_group_name = Utils::format_field_name( $field_group_name ); - $post_field_groups[] = [ - 'type' => $location['param'], - 'field_group' => $field_group, - 'post_id' => $location['value'] - ]; - } - } + $manually_set_graphql_types = isset( $field_group['map_graphql_types_from_location_rules'] ) ? (bool) $field_group['map_graphql_types_from_location_rules'] : false; + + if ( false === $manually_set_graphql_types ) { + if ( ! isset( $field_group['graphql_types'] ) || empty( $field_group['graphql_types'] ) ) { + $field_group['graphql_types'] = []; + $location_rules = $this->get_location_rules(); + if ( isset( $location_rules[ $field_group_name ] ) ) { + $field_group['graphql_types'] = $location_rules[ $field_group_name ]; } } } - } - - - /** - * If no field groups are assigned to a specific post, we don't need to modify the Schema - */ - if ( empty( $post_field_groups ) ) { - return; - } - - /** - * Loop over the field groups assigned to a specific post - * and register them to the Schema - */ - foreach ( $post_field_groups as $key => $group ) { - if ( empty( $group['field_group'] ) || ! is_array( $group['field_group'] ) ) { + if ( ! is_array( $field_group['graphql_types'] ) || empty( $field_group['graphql_types'] ) ) { continue; } - $post_object = get_post( (int) $group['post_id'] ); - - $allowed_post_types = get_post_types( [ 'show_in_graphql' => true ] ); - if ( ! $post_object instanceof \WP_Post || ! in_array( $post_object->post_type, $allowed_post_types, true ) ) { - continue; + /** + * Determine if the field group should be exposed + * to graphql + */ + if ( ! $this->should_field_group_show_in_graphql( $field_group ) ) { + return; } - $field_group = $group['field_group']; - $post_type_object = get_post_type_object( $post_object->post_type ); - + $graphql_types = array_unique( $field_group['graphql_types'] ); + $graphql_types = array_filter( $graphql_types ); + /** + * Prepare default info + */ $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $description = $field_group['description'] ? $field_group['description'] . ' | ' : ''; - $config = [ - 'name' => $field_name, - 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%1$s" was assigned to an individual post of the post_type: "%2$s". The group will be present in the Schema for the "%3$s" Type, but will only resolve if the entity has content saved.', 'wp-graphql-acf' ), $field_group['title'], $post_type_object->name, $post_type_object->graphql_plural_name ), - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; - - $this->register_graphql_field( $post_type_object->graphql_single_name, $field_name, $config ); - - } - - } - - /** - * Add field groups to users when assigned to user edit/register screens - */ - protected function add_acf_fields_to_users() { - - /** - * Get the field groups associated with the User edit form - */ - $user_edit_field_groups = acf_get_field_groups( [ - 'user_form' => 'edit', - ] ); - - /** - * Get the field groups associated with the User register form - */ - $user_register_field_groups = acf_get_field_groups( [ - 'user_form' => 'register', - ] ); - - /** - * Get a unique list of groups that match the register and edit user location rules - */ - $field_groups = array_merge( $user_edit_field_groups, $user_register_field_groups ); - $field_groups = array_intersect_key( $field_groups, array_unique( array_map( 'serialize', $field_groups ) ) ); - - - foreach ( $field_groups as $field_group ) { - - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); $field_group['type'] = 'group'; $field_group['name'] = $field_name; - $description = $field_group['description'] ? $field_group['description'] . ' | ' : ''; $config = [ 'name' => $field_name, - 'description' => $description . sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%1$s" was assigned to Users edit or register form', 'wp-graphql-acf' ), $field_group['title'] ), 'acf_field' => $field_group, 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { + 'resolve' => function ( $root ) use ( $field_group ) { return isset( $root ) ? $root : null; } ]; - $this->register_graphql_field( 'User', $field_name, $config ); - - - } - - } - - /** - * Adds options pages and options page field groups to the schema. - */ - protected function add_acf_fields_to_options_pages() { - global $acf_options_page; - - if ( ! isset( $acf_options_page ) ) { - return ; - } - - /** - * Get a list of post types that have been registered to show in graphql - */ - $graphql_options_pages = acf_get_options_pages(); - - /** - * If there are no post types exposed to GraphQL, bail - */ - if ( empty( $graphql_options_pages ) || ! is_array( $graphql_options_pages ) ) { - return; - } - - /** - * Loop over the post types exposed to GraphQL - */ - foreach ( $graphql_options_pages as $options_page_key => $options_page ) { - if ( empty( $options_page['show_in_graphql'] ) ) { - continue; - } - - /** - * Get options page properties. - */ - $page_title = $options_page['page_title']; - $page_slug = $options_page['menu_slug']; - - /** - * Get the field groups associated with the options page - */ - $field_groups = acf_get_field_groups( - [ - 'options_page' => $options_page['menu_slug'], - ] - ); - - /** - * If there are no field groups for this options page, move on to the next one. - */ - if ( empty( $field_groups ) || ! is_array( $field_groups ) ) { - continue; - } - - /** - * Loop over the field groups for this options page. - */ - $options_page_fields = array(); - foreach ( $field_groups as $field_group ) { - $field_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : Config::camel_case( $field_group['title'] ); - - $field_group['type'] = 'group'; - $field_group['name'] = $field_name; - $config = [ - 'name' => $field_name, - 'description' => $field_group['description'], - 'acf_field' => $field_group, - 'acf_field_group' => null, - 'resolve' => function( $root ) use ( $field_group ) { - return isset( $root ) ? $root : null; - } - ]; - - $options_page_fields[ $field_name ] = $config; - - } + $qualifier = sprintf( __( 'Added to the GraphQL Schema because the ACF Field Group "%1$s" was set to Show in GraphQL.', 'wp-graphql-acf' ), $field_group['title'] ); + $config['description'] = $field_group['description'] ? $field_group['description'] . ' | ' . $qualifier : $qualifier; /** - * Continue if no options to show in GraphQL + * Loop over the GraphQL types for this field group on */ - if ( empty( $options_page_fields ) ) { - continue; - } - - /** - * Create field and type names. Use explicit graphql_field_name - * if available and fallback to generating from title if not available. - */ - if ( ! empty( $options_page['graphql_field_name'] ) ) { - $field_name = $options_page['graphql_field_name']; - $type_name = ucfirst( $options_page['graphql_field_name'] ); - } else { - $field_name = Config::camel_case( $page_title ); - $type_name = ucfirst( Config::camel_case( $page_title ) ); - } - - /** - * Register options page type to schema. - */ - register_graphql_object_type( - $type_name, - [ - 'description' => sprintf( __( '%s options', 'wp-graphql-acf' ), $page_title ), - 'fields' => [ - 'pageTitle' => [ - 'type' => 'String', - 'resolve' => function( $source ) use ( $page_title ) { - return ! empty( $page_title ) ? $page_title : null; - }, - ], - 'pageSlug' => [ - 'type' => 'String', - 'resolve' => function( $source ) use ( $page_slug ) { - return ! empty( $page_slug ) ? $page_slug : null; - }, - ], - ], - ] - ); - - /** - * Register options page type to the "RootQuery" - */ - $options_page['type'] = 'options_page'; - register_graphql_field( - 'RootQuery', - $field_name, - [ - 'type' => $type_name, - 'description' => sprintf( __( '%s options', 'wp-graphql-acf' ), $options_page['page_title'] ), - 'resolve' => function() use ( $options_page ) { - return ! empty( $options_page ) ? $options_page : null; - } - ] - ); - - /** - * Register option page fields to the option page type. - */ - foreach ( $options_page_fields as $name => $config ) { - $this->register_graphql_field( $type_name, $name, $config ); + foreach ( $graphql_types as $graphql_type ) { + $this->register_graphql_field( $graphql_type, $field_name, $config ); } } + } } diff --git a/src/js/main.js b/src/js/main.js new file mode 100644 index 0000000..6a807b8 --- /dev/null +++ b/src/js/main.js @@ -0,0 +1,296 @@ +$j = jQuery.noConflict(); + +$j(document).ready(function () { + + var GraphqlLocationManager = new acf.Model({ + id: 'graphqlLocationManager', + wait: 'ready', + events: { + 'click .add-location-rule': 'onClickAddRule', + 'click .add-location-group': 'onClickAddGroup', + 'click .remove-location-rule': 'onClickRemoveRule', + 'change .refresh-location-rule': 'onChangeRemoveRule', + 'change .rule-groups .operator select': 'onChangeRemoveRule', + 'change .rule-groups .value select': 'onChangeRemoveRule', + }, + requestPending: false, + initialize: function () { + this.$el = $j('#acf-field-group-locations'); + this.getGraphqlTypes(); + }, + + onClickAddRule: function (e, $el) { + this.getGraphqlTypes(); + }, + + onClickRemoveRule: function (e, $el) { + this.getGraphqlTypes(); + }, + + onChangeRemoveRule: function (e, $el) { + setTimeout(function () { + GraphqlLocationManager.getGraphqlTypes(); + }, 500); + + }, + + onClickAddGroup: function (e, $el) { + this.getGraphqlTypes(); + }, + + isRequestPending: function() { + return this.requestPending; + }, + + startRequest: function () { + this.requestPending = true; + }, + + finishRequest: function() { + this.requestPending = false; + }, + + getGraphqlTypes: function () { + getGraphqlTypesFromLocationRules(); + }, + }); + + /** + * Set the visibility of the GraphQL Fields based on the `show_in_graphql` + * field state. + */ + function setGraphqlFieldVisibility() { + + var showInGraphQLCheckbox = $j('#acf_field_group-show_in_graphql'); + var graphqlFields = $j('#wpgraphql-acf-meta-box .acf-field'); + + graphqlFields.each(function (i, el) { + if ($j(this).attr('data-name') !== 'show_in_graphql') { + if (!showInGraphQLCheckbox.is(':checked')) { + $j(this).hide(); + } else { + $j(this).show(); + } + } + }); + + showInGraphQLCheckbox.on('change', function () { + setGraphqlFieldVisibility(); + getGraphqlTypesFromLocationRules(); + }) + + } + + function toggleFieldRequirement() { + + $j('#acf_field_group-show_in_graphql').on('change', function () { + var graphqlFieldNameWrap = $j('.acf-field[data-name="graphql_field_name"]'), + graphqlLabel = graphqlFieldNameWrap.find('label'), + graphqlInput = $j('#acf_field_group-graphql_field_name'); + + if ($j(this).is(':checked')) { + + // Add span.acf-required if necessary. + if (graphqlFieldNameWrap.find('.acf-required').length === 0) { + graphqlLabel.append('*'); + } + + // Toggle required attributes and visual features. + graphqlFieldNameWrap.addClass('is-required'); + graphqlLabel.find('.acf-required').show(); + graphqlInput.attr('required', true); + } else { + graphqlFieldNameWrap.removeClass('is-required'); + graphqlLabel.find('.acf-required').hide(); + graphqlInput.attr('required', false); + } + + }); + + } + + /** + * Listen to state changes for checkboxes for Interfaces and the checkboxes for + * Possible Types of the interfaces + */ + function initInterfaceCheckboxes() { + + // Find all the checkboxes for Interface types + $j('span[data-interface]').each(function (i, el) { + + // Get the interface name + let interfaceName = $j(el).data('interface'); + + // Get the checkbox for the interface + let interfaceCheckbox = $j('input[value="' + interfaceName + '"]'); + + // Find all checkboxes that implement the interface + let possibleTypesCheckboxes = $j('span[data-implements="' + interfaceName + '"]').siblings('input[type="checkbox"]'); + + // Prepend some space before to nest the Types beneath the Interface + possibleTypesCheckboxes.before("  "); + + // Listen for changes on the Interface checkbox + interfaceCheckbox.change(function () { + possibleTypesCheckboxes.prop('checked', $j(this).is(":checked")); + }) + + // Listen for changes to the checkboxes that implement the Interface + possibleTypesCheckboxes.change(function () { + + // Set the checked state of the Interface checkbox + if (!$j(this).is(":checked") && interfaceCheckbox.is(":checked")) { + interfaceCheckbox.prop("checked", false); + } + + // Set the state of the Implementing checkboxes + if ($j(possibleTypesCheckboxes).not(":checked").length === 0) { + interfaceCheckbox.prop("checked", true); + } + + }) + + }); + + } + + /** + * JavaScript version of the PHP lcfirst + * + * @param str + * @returns {string} + */ + function lcfirst(str) { + str += '' + const f = str.charAt(0) + .toLowerCase() + return f + str.substr(1) + } + + /** + * JavaScript version of the PHP ucwords + * + * @param str + * @returns {string} + */ + function ucwords(str) { + return str.toLowerCase().replace(/\b[a-z]/g, function (letter) { + return letter.toUpperCase(); + }) + } + + /** + * Based on the WPGraphQL format_field_name function + * + * See: https://github.com/wp-graphql/wp-graphql/blob/cc0b383259383898c3a1bebe65adf1140290b37e/src/Utils/Utils.php#L85-L100 + */ + function formatFieldName(fieldName) { + + fieldName.replace('[^a-zA-Z0-9 -]', '-'); + fieldName = lcfirst(fieldName); + fieldName = lcfirst(fieldName.split('-').join(' ')); + fieldName = ucwords(fieldName); + fieldName = lcfirst(fieldName.split(' ').join('')); + return fieldName; + + } + + /** + * Set the GraphQL Field Name value based on the Field Group Title + * if the graphql_field_name has not already been set. + */ + function setGraphqlFieldName() { + var graphqlFieldNameField = $j('#acf_field_group-graphql_field_name'); + var fieldGroupTitle = $j('#titlediv #title'); + if ('' === graphqlFieldNameField.val()) { + graphqlFieldNameField.val(formatFieldName(fieldGroupTitle.val())); + } + fieldGroupTitle.on('change', function () { + setGraphqlFieldName(); + }); + } + + + /** + * Determine whether users should be able to interact with the checkboxes + * to manually set the GraphQL Types for the ACF Field Group + */ + function graphqlMapTypesFromLocations() { + var checkboxes = $j('.acf-field[data-name="graphql_types"] .acf-checkbox-list input[type="checkbox"]'); + var manualMapTypes = $j('#acf_field_group-map_graphql_types_from_location_rules'); + + if (manualMapTypes.not(':checked')) { + getGraphqlTypesFromLocationRules(); + } + + checkboxes.each(function (i, el) { + if (manualMapTypes.is(':checked')) { + $j(this).removeAttr("disabled"); + } else { + $j(this).attr("disabled", true); + } + }); + manualMapTypes.on('change', function () { + graphqlMapTypesFromLocations(); + }); + } + + function getGraphqlTypesFromLocationRules() { + + var showInGraphQLCheckbox = $j('#acf_field_group-show_in_graphql'); + var form = $j('#post'); + var formInputs = $j('#post :input'); + var serialized = formInputs.serialize(); + var checkboxes = $j('.acf-field[data-name="graphql_types"] .acf-checkbox-list input[type="checkbox"]'); + var manualMapTypes = $j('#acf_field_group-map_graphql_types_from_location_rules'); + + // If Manual Type selection is checked, + // Don't attempt to get GraphQL Types from the location rules + if (manualMapTypes.is(':checked')) { + return; + } + + if ( ! showInGraphQLCheckbox.is(':checked') ) { + return; + } + + if ( 'pending' !== form.attr('data-request-pending') ) { + + // Start the request + form.attr('data-request-pending', 'pending' ); + + // Make the request + $j.post(ajaxurl, { + action: 'get_acf_field_group_graphql_types', + data: serialized + }, function (res) { + var types = res && res['graphql_types'] ? res['graphql_types'] : []; + + checkboxes.each(function (i, el) { + var checkbox = $j(this); + var value = $j(this).val(); + checkbox.prop('checked', false); + if (types && types.length) { + if (-1 !== $j.inArray(value, types)) { + checkbox.prop('checked', true); + } + } + checkbox.trigger("change"); + }) + + // Signal that the request is finished + form.removeAttr('data-request-pending'); + + }); + } + + }; + + // Initialize the functionality to track the state of the Interface checkboxes. + initInterfaceCheckboxes(); + toggleFieldRequirement(); + setGraphqlFieldVisibility(); + setGraphqlFieldName(); + graphqlMapTypesFromLocations(); + +}); diff --git a/src/location-rules.php b/src/location-rules.php new file mode 100644 index 0000000..af6f678 --- /dev/null +++ b/src/location-rules.php @@ -0,0 +1,985 @@ +acf_field_groups = isset( $acf_field_groups ) && ! empty( $acf_field_groups ) ? $acf_field_groups : acf_get_field_groups(); + } + + /** + * Given a field name, formats it for GraphQL + * + * @param string $field_name The field name to format + * + * @return string + */ + public function format_field_name( string $field_name ) { + + $replaced = preg_replace( '[^a-zA-Z0-9 -]', '_', $field_name ); + + // If any values were replaced, use the replaced string as the new field name + if ( ! empty( $replaced ) ) { + $field_name = $replaced; + } + + $field_name = lcfirst( $field_name ); + $field_name = lcfirst( str_replace( '-', ' ', ucwords( $field_name, '_' ) ) ); + $field_name = lcfirst( str_replace( ' ', '', ucwords( $field_name, ' ' ) ) ); + + return $field_name; + } + + /** + * Given a type name, formats it for GraphQL + * + * @param string $type_name The type name to format + * + * @return string + */ + public function format_type_name( string $type_name ) { + return ucfirst( $this->format_field_name( $type_name ) ); + } + + /** + * Given the name of a GraphqL Field Group and the name of a GraphQL Type, this sets the + * field group to show in that Type + * + * @param string $field_group_name The name of the ACF Field Group + * @param string $graphql_type_name The name of the GraphQL Type + */ + public function set_graphql_type( string $field_group_name, string $graphql_type_name ) { + $this->mapped_field_groups[ Utils::format_field_name( $field_group_name ) ][] = $this->format_type_name( $graphql_type_name ); + } + + /** + * Given the name of a GraphqL Field Group and the name of a GraphQL Type, this unsets the + * GraphQL Type for the field group + * + * @param string $field_group_name The name of the ACF Field Group + * @param string $graphql_type_name The name of the GraphQL Type + */ + public function unset_graphql_type( string $field_group_name, string $graphql_type_name ) { + $this->unset_types[ $this->format_field_name( $field_group_name ) ][] = $this->format_type_name( $graphql_type_name ); + } + + /** + * Get the rules + * + * @return array + */ + public function get_rules() { + + if ( empty( $this->mapped_field_groups ) ) { + return []; + } + + + if ( empty( $this->unset_types ) ) { + return $this->mapped_field_groups; + } + + /** + * Remove any Types that were flagged to unset + */ + foreach ( $this->unset_types as $field_group => $types ) { + + // If there are no mapped field groups for the rule being unset, return the mapped groups as is + if ( ! isset( $this->mapped_field_groups[ $field_group ] ) ) { + return $this->mapped_field_groups; + } + + // If the types to unset are empty or not an array, return the mapped field groups as is + if ( empty( $types ) || ! is_array( $types ) ) { + return $this->mapped_field_groups; + } + + // Loop over the types to unset, find the key of the type in the array, then unset it + foreach ( $types as $type ) { + if ( ( $key = array_search( $type, $this->mapped_field_groups[ $field_group ] ) ) !== false ) { + unset( $this->mapped_field_groups[ $field_group ][ $key ] ); + } + } + + } + + // Return the mapped field groups, with the unset fields (if any) removed + return $this->mapped_field_groups; + + } + + /** + * Checks for conflicting rule types to avoid impossible states. + * + * If a field group is assigned to a rule such as "post_type == post" AND "taxonomy == Tag" + * this would be an impossible state, as an object can't be a Post and a Tag. + * + * If we detect conflicting rules, the rule set is not applied at all. + * + * @param array $and_params The parameters of the rule group + * @param mixed $param The current param being evaluated + * @param array $allowed_params The allowed params that shouldn't conflict + * + * @return bool + */ + public function check_for_conflicts( array $and_params, $param, $allowed_params = [] ) { + + if ( empty( $and_params ) ) { + return false; + } + + $has_conflict = false; + $keys = array_keys( $and_params, $param ); + + if ( isset( $keys[0] ) ) { + unset( $and_params[ $keys[0] ] ); + } + + if ( ! empty( $and_params ) ) { + foreach ( $and_params as $key => $and_param ) { + if ( false === array_search( $and_param, $allowed_params, true ) ) { + $has_conflict = true; + } + } + } + + return $has_conflict; + + } + + /** + * Checks for conflicting rule types to avoid impossible states. + * + * If a field group is assigned to a rule such as "post_type == post" AND "taxonomy == Tag" + * this would be an impossible state, as an object can't be a Post and a Tag. + * + * If we detect conflicting rules, the rule set is not applied at all. + * + * @param array $and_params The parameters of the rule group + * @param mixed $param The current param being evaluated + * + * @return bool + */ + public function check_params_for_conflicts( array $and_params = [], $param ) { + switch ( $param ) { + case 'post_type': + $allowed_and_params = [ + 'post_status', + 'post_format', + 'post_category', + 'post_taxonomy', + 'post', + ]; + break; + case 'post_template': + case 'page_template': + $allowed_and_params = [ + 'page_type', + 'page_parent', + 'page', + ]; + break; + case 'post_status': + $allowed_and_params = [ + 'post_type', + 'post_format', + 'post_category', + 'post_taxonomy', + ]; + break; + case 'post_format': + case 'post_category': + case 'post': + case 'post_taxonomy': + $allowed_and_params = [ + 'post_status', + 'post_type', + 'post_format', + 'post_category', + 'post_taxonomy', + 'post', + ]; + break; + case 'page': + case 'page_parent': + case 'page_type': + $allowed_and_params = [ + 'page_template', + 'page_type', + 'page_parent', + 'page', + ]; + break; + case 'current_user': + case 'current_user_role': + // @todo: + // Right now, if you set current_user or current_user_role as the only rule, + // ACF adds the field group to every possible location in the Admin. + // This seems a bit heavy handed. 🤔 + // We need to think through this a bit more, and how this rule + // Can be composed with other rules, etc. + $allowed_and_params = []; + break; + case 'user_form': + case 'user_role': + $allowed_and_params = [ + 'user_form', + 'user_role', + ]; + break; + case 'taxonomy': + case 'attachment': + case 'comment': + case 'widget': + case 'nav_menu': + case 'nav_menu_item': + case 'options_page': + default: + $allowed_and_params = []; + break; + + } + + return $this->check_for_conflicts( $and_params, $param, $allowed_and_params ); + + } + + /** + * Determine how an ACF Location Rule should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_rules( string $field_group_name, string $param, string $operator, string $value ) { + + // Depending on the param of the rule, there's different logic to + // map to the Schema + switch ( $param ) { + case 'post_type': + $this->determine_post_type_rules( $field_group_name, $param, $operator, $value ); + break; + case 'post_template': + case 'page_template': + $this->determine_post_template_rules( $field_group_name, $param, $operator, $value ); + break; + case 'post_status': + $this->determine_post_status_rules( $field_group_name, $param, $operator, $value ); + break; + case 'post_format': + case 'post_category': + case 'post_taxonomy': + $this->determine_post_taxonomy_rules( $field_group_name, $param, $operator, $value ); + break; + case 'post': + $this->determine_post_rules( $field_group_name, $param, $operator, $value ); + break; + case 'page_type': + $this->determine_page_type_rules( $field_group_name, $param, $operator, $value ); + break; + case 'page_parent': + case 'page': + // If page or page_parent is set, regardless of operator and value, + // we can add the field group to the Page type + $this->set_graphql_type( $field_group_name, 'Page' ); + break; + case 'current_user': + case 'current_user_role': + // @todo: + // Right now, if you set current_user or current_user_role as the only rule, + // ACF adds the field group to every possible location in the Admin. + // This seems a bit heavy handed. 🤔 + // We need to think through this a bit more, and how this rule + // Can be composed with other rules, etc. + break; + case 'user_form': + case 'user_role': + // If user_role or user_form params are set, we need to expose the field group + // to the User type + $this->set_graphql_type( $field_group_name, 'User' ); + break; + case 'taxonomy': + $this->determine_taxonomy_rules( $field_group_name, $param, $operator, $value ); + break; + case 'attachment': + $this->determine_attachment_rules( $field_group_name, $param, $operator, $value ); + break; + case 'comment': + $this->determine_comment_rules( $field_group_name, $param, $operator, $value ); + break; + case 'widget': + // @todo: Widgets are not currently supported in WPGraphQL + break; + case 'nav_menu': + $this->determine_nav_menu_rules( $field_group_name, $param, $operator, $value ); + break; + case 'nav_menu_item': + $this->determine_nav_menu_item_item_rules( $field_group_name, $param, $operator, $value ); + break; + case 'options_page': + $this->determine_options_rules( $field_group_name, $param, $operator, $value ); + break; + default: + // If a built-in location rule could not be matched, + // Custom rules (from extensions, etc) can hook in here and apply their + // rules to the WPGraphQL Schema + do_action( 'graphql_acf_match_location_rule', $field_group_name, $param, $operator, $value, $this ); + break; + + } + + } + + /** + * Determine GraphQL Schema location rules based on ACF Location rules for field groups + * that are configured with no `graphql_types` field. + * + * @return void + */ + public function determine_location_rules() { + + if ( ! empty( $this->acf_field_groups ) ) { + foreach ( $this->acf_field_groups as $field_group ) { + + $field_group_name = isset( $field_group['graphql_field_name'] ) ? $field_group['graphql_field_name'] : $field_group['title']; + + if ( ! empty( $field_group['location'] ) && is_array( $field_group['location'] ) ) { + + foreach ( $field_group['location'] as $location_rule_group ) { + if ( ! empty( $location_rule_group ) ) { + + foreach ( $location_rule_group as $group => $rule ) { + + // Determine the and params for the rule group + $and_params = wp_list_pluck( $location_rule_group, 'param' ); + $and_params = ! empty( $and_params ) ? array_values( $and_params ) : []; + + $operator = isset( $rule['operator'] ) ? $rule['operator'] : '=='; + $param = isset( $rule['param'] ) ? $rule['param'] : null; + $value = isset( $rule['value'] ) ? $rule['value'] : null; + + if ( empty( $param ) || empty( $value ) ) { + continue; + } + + if ( true === $this->check_params_for_conflicts( $and_params, $param ) ) { + continue; + } + + $this->determine_rules( $field_group_name, $param, $operator, $value ); + + } + } + } + } + } + } + + } + + /** + * Returns an array of Post Templates + * + * @return array + */ + public function get_graphql_post_template_types() { + + $registered_page_templates = wp_get_theme()->get_post_templates(); + + $page_templates['default'] = 'DefaultTemplate'; + + if ( ! empty( $registered_page_templates ) && is_array( $registered_page_templates ) ) { + + foreach ( $registered_page_templates as $post_type_templates ) { + // Post templates are returned as an array of arrays. PHPStan believes they're returned as + // an array of strings and believes this will always evaluate to false. + // We should ignore the phpstan check here. + // @phpstan-ignore-next-line + if ( ! empty( $post_type_templates ) && is_array( $post_type_templates ) ) { + foreach ( $post_type_templates as $file => $name ) { + + $name = ucwords( $name ); + $replaced_name = preg_replace( '/[^\w]/', '', $name ); + + if ( ! empty( $replaced_name ) ) { + $name = $replaced_name; + } + + if ( preg_match( '/^\d/', $name ) || false === strpos( strtolower( $name ), 'template' ) ) { + $name = 'Template_' . $name; + } + + $page_templates[ $file ] = $name; + } + } + } + } + + return $page_templates; + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_post_type_rules( string $field_group_name, string $param, string $operator, string $value ) { + $allowed_post_types = get_post_types( [ 'show_in_graphql' => true ] ); + + if ( empty( $allowed_post_types ) ) { + return; + } + + if ( '==' === $operator ) { + + // If all post types + if ( 'all' === $value ) { + + // loop over and set all post types + foreach ( $allowed_post_types as $allowed_post_type ) { + + $post_type_object = get_post_type_object( $allowed_post_type ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + } else { + if ( in_array( $value, $allowed_post_types, true ) ) { + $post_type_object = get_post_type_object( $value ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + } + + + } + + if ( '!=' === $operator ) { + + if ( 'all' !== $value ) { + // loop over and set all post types + foreach ( $allowed_post_types as $allowed_post_type ) { + $post_type_object = get_post_type_object( $allowed_post_type ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + } + + $post_type_object = get_post_type_object( $value ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->unset_graphql_type( $field_group_name, $graphql_name ); + } + } + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_post_template_rules( string $field_group_name, string $param, string $operator, string $value ) { + + $templates = $this->get_graphql_post_template_types(); + + if ( ! is_array( $templates ) || empty( $templates ) ) { + return; + } + + if ( '==' === $operator ) { + + // If the template is available in GraphQL, set it + if ( isset( $templates[ $value ] ) ) { + $this->set_graphql_type( $field_group_name, $templates[ $value ] ); + } + } + + if ( '!=' === $operator ) { + + foreach ( $templates as $name => $template_type ) { + $this->set_graphql_type( $field_group_name, $template_type ); + } + + // If the Template is available in GraphQL, unset it + if ( isset( $templates[ $value ] ) ) { + $this->unset_graphql_type( $field_group_name, $templates[ $value ] ); + } + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_post_status_rules( string $field_group_name, string $param, string $operator, string $value ) { + // @todo: Should post status affect the GraphQL Schema at all? + // If a field group is set to show on "post_status == publish" as the only rule, what post type does that apply to? All? 🤔 + // If a field group is set to show on "post_status != draft" does that mean the field group should be available on all post types in the Schema by default? + // This seems like a very difficult rule to translate to the Schema. + // Like, lets say I add a field group called: "Editor Notes" that I want to show for any status that is not "publish". In theory, if that's my only rule, that seems like it should apply to all post types across the board, and show in the Admin in any state of the post, other than publish. 🤔 + + // ACF Admin behavior seems to add it to the Admin on all post types, so WPGraphQL + // should respect this rule and also add it to all post types. The resolver should + // then determine whether to resolve the data or not, based on this rule. + + // If Post Status is used to qualify a field group location, + // It will be added to the Schema for any Post Type that is set to show in GraphQL + $allowed_post_types = get_post_types( [ 'show_in_graphql' => true ] ); + foreach ( $allowed_post_types as $post_type ) { + + $post_type_object = get_post_type_object( $post_type ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_post_format_rules( string $field_group_name, string $param, string $operator, string $value ) { + + $post_format_taxonomy = get_taxonomy( 'post_format' ); + $post_format_post_types = $post_format_taxonomy->object_type; + + if ( ! is_array( $post_format_post_types ) || empty( $post_format_post_types ) ) { + return; + } + + // If Post Format is used to qualify a field group location, + // It will be added to the Schema for any Post Type that supports post formats + // And shows in GraphQL + $allowed_post_types = get_post_types( [ 'show_in_graphql' => true ] ); + foreach ( $allowed_post_types as $post_type ) { + if ( in_array( $post_type, $post_format_post_types, true ) ) { + $post_type_object = get_post_type_object( $value ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_post_taxonomy_rules( string $field_group_name, string $param, string $operator, string $value ) { + + // If Post Taxonomy is used to qualify a field group location, + // It will be added to the Schema for the Post post type + $this->set_graphql_type( $field_group_name, 'Post' ); + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_post_rules( string $field_group_name, string $param, string $operator, string $value ) { + + // If a Single post is used to qualify a field group location, + // It will be added to the Schema for the GraphQL Type for the post_type of the Post + // it is assigned to + + if ( '==' === $operator ) { + + if ( absint( $value ) ) { + $post = get_post( absint( $value ) ); + if ( $post instanceof \WP_Post ) { + $post_type_object = get_post_type_object( $post->post_type ); + if ( isset( $post_type_object->show_in_graphql ) && true === $post_type_object->show_in_graphql ) { + if ( isset( $post_type_object->graphql_single_name ) ) { + $this->set_graphql_type( $field_group_name, $post_type_object->graphql_single_name ); + } + } + } + } + + } + + // If a single post is used as not equal, + // the field group should be added to ALL post types in the Schema + if ( '!=' === $operator ) { + + $allowed_post_types = get_post_types( [ 'show_in_graphql' => true ] ); + + if ( empty( $allowed_post_types ) ) { + return; + } + + // loop over and set all post types + foreach ( $allowed_post_types as $allowed_post_type ) { + + $post_type_object = get_post_type_object( $allowed_post_type ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_page_type_rules( string $field_group_name, string $param, string $operator, string $value ) { + + // If front_page or posts_page is set to equal_to or not_equal_to + // then the field group should be shown on the Post type + if ( in_array( $value, [ 'front_page', 'posts_page' ], true ) ) { + $this->set_graphql_type( $field_group_name, 'Page' ); + } + + // If top_level, parent, or child is set as equal_to or not_equal_to + // then the field group should be shown on all hierarchical post types + if ( in_array( $value, [ 'top_level', 'parent', 'child' ], true ) ) { + + $hierarchical_post_types = get_post_types( [ + 'show_in_graphql' => true, + 'hierarchical' => true + ] ); + + if ( empty( $hierarchical_post_types ) ) { + return; + } + + // loop over and set all post types + foreach ( $hierarchical_post_types as $allowed_post_type ) { + + $post_type_object = get_post_type_object( $allowed_post_type ); + $graphql_name = isset( $post_type_object->graphql_single_name ) ? $post_type_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_taxonomy_rules( string $field_group_name, string $param, string $operator, string $value ) { + + $allowed_taxonomies = get_taxonomies( [ 'show_in_graphql' => true ] ); + + if ( empty( $allowed_taxonomies ) ) { + return; + } + + if ( '==' === $operator ) { + + // If all post types + if ( 'all' === $value ) { + + // loop over and set all post types + foreach ( $allowed_taxonomies as $allowed_taxonomy ) { + + $tax_object = get_taxonomy( $allowed_taxonomy ); + $graphql_name = isset( $tax_object->graphql_single_name ) ? $tax_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + + } else { + if ( in_array( $value, $allowed_taxonomies, true ) ) { + $tax_object = get_taxonomy( $value ); + $graphql_name = isset( $tax_object->graphql_single_name ) ? $tax_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + } + + + } + + if ( '!=' === $operator ) { + + if ( 'all' !== $value ) { + + // loop over and set all post types + foreach ( $allowed_taxonomies as $allowed_taxonomy ) { + + $tax_object = get_taxonomy( $allowed_taxonomy ); + $graphql_name = isset( $tax_object->graphql_single_name ) ? $tax_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->set_graphql_type( $field_group_name, $graphql_name ); + } + } + + $tax_object = get_taxonomy( $value ); + $graphql_name = isset( $tax_object->graphql_single_name ) ? $tax_object->graphql_single_name : null; + if ( ! empty( $graphql_name ) ) { + $this->unset_graphql_type( $field_group_name, $graphql_name ); + } + + } + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_attachment_rules( string $field_group_name, string $param, string $operator, string $value ) { + + if ( '==' === $operator ) { + $this->set_graphql_type( $field_group_name, 'MediaItem' ); + } + + if ( '!=' === $operator && 'all' === $value ) { + $this->unset_graphql_type( $field_group_name, 'MediaItem' ); + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_comment_rules( string $field_group_name, string $param, string $operator, string $value ) { + + if ( '==' === $operator ) { + $this->set_graphql_type( $field_group_name, 'Comment' ); + } + + if ( '!=' === $operator ) { + + // If not equal to all, unset from all comments + if ( 'all' === $value ) { + $this->unset_graphql_type( $field_group_name, 'Comment' ); + + // If not equal to just a specific post type/comment relationship, + // show the field group on the Comment Type + } else { + $this->set_graphql_type( $field_group_name, 'Comment' ); + } + + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_nav_menu_rules( string $field_group_name, string $param, string $operator, string $value ) { + + if ( '==' === $operator ) { + $this->set_graphql_type( $field_group_name, 'Menu' ); + } + + if ( '!=' === $operator ) { + + // If not equal to all, unset from all Menu + if ( 'all' === $value ) { + $this->unset_graphql_type( $field_group_name, 'Menu' ); + + // If not equal to just a Menu, + // show the field group on all Menus + } else { + $this->set_graphql_type( $field_group_name, 'Menu' ); + } + + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_nav_menu_item_item_rules( string $field_group_name, string $param, string $operator, string $value ) { + + if ( '==' === $operator ) { + $this->set_graphql_type( $field_group_name, 'MenuItem' ); + } + + if ( '!=' === $operator ) { + + // If not equal to all, unset from all MenuItem + if ( 'all' === $value ) { + $this->unset_graphql_type( $field_group_name, 'MenuItem' ); + + // If not equal to one Menu / location, + // show the field group on all MenuItems + } else { + $this->set_graphql_type( $field_group_name, 'MenuItem' ); + } + + } + + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_block_rules( string $field_group_name, string $param, string $operator, string $value ) { + // @todo: ACF Blocks are not formally supported by WPGraphQL / WPGraphQL for ACF. More to come in the future! + } + + /** + * Determines how the ACF Rules should apply to the WPGraphQL Schema + * + * @param string $field_group_name The name of the ACF Field Group the rule applies to + * @param string $param The parameter of the rule + * @param string $operator The operator of the rule + * @param string $value The value of the rule + */ + public function determine_options_rules( string $field_group_name, string $param, string $operator, string $value ) { + + if ( '==' === $operator ) { + $options_page = acf_get_options_page( $value ); + + if ( ! isset( $options_page['show_in_graphql'] ) || false === (bool) $options_page['show_in_graphql'] ) { + return; + } + $type_name = isset( $options_page['graphql_field_name'] ) ? Utils::format_type_name( $options_page['graphql_field_name'] ) : Utils::format_type_name( $options_page['menu_slug'] ); + $this->set_graphql_type( $field_group_name, $type_name ); + } + + if ( '!=' === $operator ) { + + $options_pages = acf_get_options_pages(); + + if ( empty( $options_pages ) || ! is_array( $options_pages ) ) { + return; + } + + // Show all options pages + foreach ( $options_pages as $options_page ) { + if ( ! isset( $options_page['show_in_graphql'] ) || false === (bool) $options_page['show_in_graphql'] ) { + continue; + } + $type_name = isset( $options_page['graphql_field_name'] ) ? Utils::format_type_name( $options_page['graphql_field_name'] ) : Utils::format_type_name( $options_page['menu_slug'] ); + $this->set_graphql_type( $field_group_name, $type_name ); + } + + // Get the options page to unset + $options_page = acf_get_options_page( $value ); + if ( ! isset( $options_page['show_in_graphql'] ) || false === $options_page['show_in_graphql'] ) { + return; + } + $type_name = isset( $options_page['graphql_field_name'] ) ? Utils::format_type_name( $options_page['graphql_field_name'] ) : Utils::format_type_name( $options_page['menu_slug'] ); + $this->unset_graphql_type( $field_group_name, $type_name ); + } + + } + +} diff --git a/tests/_bootstrap/bootstrap.php b/tests/_bootstrap/bootstrap.php new file mode 100644 index 0000000..e69de29 diff --git a/tests/_data/images/test.png b/tests/_data/images/test.png new file mode 100644 index 0000000..00c7c6a Binary files /dev/null and b/tests/_data/images/test.png differ diff --git a/tests/_output/.gitignore b/tests/_output/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/tests/_output/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/_output/c3tmp/codecoverage.clover.xml b/tests/_output/c3tmp/codecoverage.clover.xml deleted file mode 100644 index 3b1cffa..0000000 --- a/tests/_output/c3tmp/codecoverage.clover.xml +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/_output/c3tmp/codecoverage.serialized b/tests/_output/c3tmp/codecoverage.serialized deleted file mode 100644 index 0598ec1..0000000 Binary files a/tests/_output/c3tmp/codecoverage.serialized and /dev/null differ diff --git a/tests/_output/c3tmp/codecoverage.tar b/tests/_output/c3tmp/codecoverage.tar deleted file mode 100644 index c6a19dc..0000000 Binary files a/tests/_output/c3tmp/codecoverage.tar and /dev/null differ diff --git a/tests/_output/coverage.wpunit.cov b/tests/_output/coverage.wpunit.cov deleted file mode 100644 index 48a00b0..0000000 --- a/tests/_output/coverage.wpunit.cov +++ /dev/null @@ -1,1319 +0,0 @@ -setData(array ( - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/wp-graphql-jwt-authentication.php' => - array ( - 23 => - array ( - ), - 24 => - array ( - ), - 25 => - array ( - ), - 27 => - array ( - ), - 28 => - array ( - ), - 29 => - array ( - ), - 31 => - array ( - ), - 53 => - array ( - ), - 54 => - array ( - ), - 55 => - array ( - ), - 56 => - array ( - ), - 57 => - array ( - ), - 59 => - array ( - ), - 66 => - array ( - ), - 71 => - array ( - ), - 72 => - array ( - ), - 86 => - array ( - ), - 88 => - array ( - ), - 100 => - array ( - ), - 102 => - array ( - ), - 114 => - array ( - ), - 115 => - array ( - ), - 116 => - array ( - ), - 119 => - array ( - ), - 120 => - array ( - ), - 121 => - array ( - ), - 124 => - array ( - ), - 125 => - array ( - ), - 126 => - array ( - ), - 129 => - array ( - ), - 130 => - array ( - ), - 131 => - array ( - ), - 134 => - array ( - ), - 135 => - array ( - ), - 136 => - array ( - ), - 138 => - array ( - ), - 151 => - array ( - ), - 152 => - array ( - ), - 153 => - array ( - ), - 154 => - array ( - ), - 165 => - array ( - ), - 170 => - array ( - ), - 171 => - array ( - ), - 172 => - array ( - ), - 173 => - array ( - ), - 175 => - array ( - ), - 176 => - array ( - ), - 177 => - array ( - ), - 178 => - array ( - ), - 183 => - array ( - ), - 184 => - array ( - ), - 185 => - array ( - ), - 186 => - array ( - ), - 188 => - array ( - ), - 192 => - array ( - ), - 195 => - array ( - ), - 196 => - array ( - ), - 198 => - array ( - ), - ), - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/Auth.php' => - array ( - 26 => - array ( - ), - 27 => - array ( - ), - 29 => - array ( - ), - 46 => - array ( - ), - 47 => - array ( - ), - 48 => - array ( - ), - 53 => - array ( - ), - 58 => - array ( - ), - 59 => - array ( - ), - 60 => - array ( - ), - 65 => - array ( - ), - 70 => - array ( - ), - 71 => - array ( - ), - 72 => - array ( - ), - 73 => - array ( - ), - 74 => - array ( - ), - 79 => - array ( - ), - 80 => - array ( - ), - 88 => - array ( - ), - 89 => - array ( - ), - 90 => - array ( - ), - 92 => - array ( - ), - 93 => - array ( - ), - 102 => - array ( - ), - 107 => - array ( - ), - 114 => - array ( - ), - 116 => - array ( - ), - 118 => - array ( - ), - 120 => - array ( - ), - 132 => - array ( - ), - 133 => - array ( - ), - 134 => - array ( - ), - 142 => - array ( - ), - 148 => - array ( - ), - 149 => - array ( - ), - 150 => - array ( - ), - 151 => - array ( - ), - 152 => - array ( - ), - 153 => - array ( - ), - 154 => - array ( - ), - 155 => - array ( - ), - 156 => - array ( - ), - 157 => - array ( - ), - 158 => - array ( - ), - 166 => - array ( - ), - 171 => - array ( - ), - 172 => - array ( - ), - 182 => - array ( - ), - 187 => - array ( - ), - 189 => - array ( - ), - 203 => - array ( - ), - 204 => - array ( - ), - 205 => - array ( - ), - 213 => - array ( - ), - 218 => - array ( - ), - 219 => - array ( - ), - 220 => - array ( - ), - 221 => - array ( - ), - 226 => - array ( - ), - 231 => - array ( - ), - 232 => - array ( - ), - 233 => - array ( - ), - 241 => - array ( - ), - 242 => - array ( - ), - 256 => - array ( - ), - 261 => - array ( - ), - 266 => - array ( - ), - 267 => - array ( - ), - 269 => - array ( - ), - 271 => - array ( - ), - 272 => - array ( - ), - 282 => - array ( - ), - 284 => - array ( - ), - 285 => - array ( - ), - 295 => - array ( - ), - 296 => - array ( - ), - 306 => - array ( - ), - 307 => - array ( - ), - 314 => - array ( - ), - 315 => - array ( - ), - 316 => - array ( - ), - 318 => - array ( - ), - 319 => - array ( - ), - 321 => - array ( - ), - 322 => - array ( - ), - 325 => - array ( - ), - 326 => - array ( - ), - 341 => - array ( - ), - 346 => - array ( - ), - 347 => - array ( - ), - 348 => - array ( - ), - 349 => - array ( - ), - 351 => - array ( - ), - 353 => - array ( - ), - 370 => - array ( - ), - 375 => - array ( - ), - 380 => - array ( - ), - 385 => - array ( - ), - 390 => - array ( - ), - 392 => - array ( - ), - 397 => - array ( - ), - 398 => - array ( - ), - 416 => - array ( - ), - 421 => - array ( - ), - 422 => - array ( - ), - 423 => - array ( - ), - 424 => - array ( - ), - 425 => - array ( - ), - 426 => - array ( - ), - 427 => - array ( - ), - 432 => - array ( - ), - 434 => - array ( - ), - 436 => - array ( - ), - 438 => - array ( - ), - 440 => - array ( - ), - 442 => - array ( - ), - 460 => - array ( - ), - 465 => - array ( - ), - 471 => - array ( - ), - 472 => - array ( - ), - 474 => - array ( - ), - 476 => - array ( - ), - 478 => - array ( - ), - 480 => - array ( - ), - 482 => - array ( - ), - 486 => - array ( - ), - 487 => - array ( - ), - 488 => - array ( - ), - 501 => - array ( - ), - 509 => - array ( - ), - 514 => - array ( - ), - 521 => - array ( - ), - 522 => - array ( - ), - 523 => - array ( - ), - 528 => - array ( - ), - 529 => - array ( - ), - 531 => - array ( - ), - 536 => - array ( - ), - 537 => - array ( - ), - 538 => - array ( - ), - 543 => - array ( - ), - 548 => - array ( - ), - 550 => - array ( - ), - 551 => - array ( - ), - 556 => - array ( - ), - 557 => - array ( - ), - 558 => - array ( - ), - 563 => - array ( - ), - 564 => - array ( - ), - 565 => - array ( - ), - 570 => - array ( - ), - 572 => - array ( - ), - 573 => - array ( - ), - 574 => - array ( - ), - 575 => - array ( - ), - 580 => - array ( - ), - 581 => - array ( - ), - 582 => - array ( - ), - 583 => - array ( - ), - 585 => - array ( - ), - 587 => - array ( - ), - 589 => - array ( - ), - 602 => - array ( - ), - 607 => - array ( - ), - 612 => - array ( - ), - 619 => - array ( - ), - 621 => - array ( - ), - 628 => - array ( - ), - 630 => - array ( - ), - 632 => - array ( - ), - ), - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/Login.php' => - array ( - 20 => - array ( - ), - 22 => - array ( - ), - 23 => - array ( - ), - 27 => - array ( - ), - 29 => - array ( - ), - 30 => - array ( - ), - 31 => - array ( - ), - 32 => - array ( - ), - 33 => - array ( - ), - 34 => - array ( - ), - 35 => - array ( - ), - 36 => - array ( - ), - 37 => - array ( - ), - 38 => - array ( - ), - 39 => - array ( - ), - 40 => - array ( - ), - 41 => - array ( - ), - 42 => - array ( - ), - 43 => - array ( - ), - 44 => - array ( - ), - 45 => - array ( - ), - 46 => - array ( - ), - 47 => - array ( - ), - 48 => - array ( - ), - 49 => - array ( - ), - 50 => - array ( - ), - 51 => - array ( - ), - 52 => - array ( - ), - 53 => - array ( - ), - 54 => - array ( - ), - 55 => - array ( - ), - 56 => - array ( - ), - 62 => - array ( - ), - 64 => - array ( - ), - 65 => - array ( - ), - 67 => - array ( - ), - 69 => - array ( - ), - 71 => - array ( - ), - ), - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/ManageTokens.php' => - array ( - 17 => - array ( - ), - 18 => - array ( - ), - 19 => - array ( - ), - 20 => - array ( - ), - 25 => - array ( - ), - 26 => - array ( - ), - 27 => - array ( - ), - 28 => - array ( - ), - 30 => - array ( - ), - 31 => - array ( - ), - 32 => - array ( - ), - 33 => - array ( - ), - 38 => - array ( - ), - 39 => - array ( - ), - 40 => - array ( - ), - 41 => - array ( - ), - 46 => - array ( - ), - 47 => - array ( - ), - 48 => - array ( - ), - 49 => - array ( - ), - 51 => - array ( - ), - 52 => - array ( - ), - 53 => - array ( - ), - 54 => - array ( - ), - 55 => - array ( - ), - 66 => - array ( - ), - 67 => - array ( - ), - 68 => - array ( - ), - 74 => - array ( - ), - 79 => - array ( - ), - 80 => - array ( - ), - 81 => - array ( - ), - 83 => - array ( - ), - 84 => - array ( - ), - 85 => - array ( - ), - 87 => - array ( - ), - 88 => - array ( - ), - 89 => - array ( - ), - 95 => - array ( - ), - 100 => - array ( - ), - 101 => - array ( - ), - 102 => - array ( - ), - 104 => - array ( - ), - 105 => - array ( - ), - 106 => - array ( - ), - 108 => - array ( - ), - 109 => - array ( - ), - 110 => - array ( - ), - 116 => - array ( - ), - 121 => - array ( - ), - 122 => - array ( - ), - 123 => - array ( - ), - 128 => - array ( - ), - 129 => - array ( - ), - 130 => - array ( - ), - 132 => - array ( - ), - 133 => - array ( - ), - 134 => - array ( - ), - 136 => - array ( - ), - 137 => - array ( - ), - 138 => - array ( - ), - 139 => - array ( - ), - 141 => - array ( - ), - 142 => - array ( - ), - 143 => - array ( - ), - 145 => - array ( - ), - 146 => - array ( - ), - 147 => - array ( - ), - 148 => - array ( - ), - 152 => - array ( - ), - 154 => - array ( - ), - 164 => - array ( - ), - 165 => - array ( - ), - 166 => - array ( - ), - 167 => - array ( - ), - 169 => - array ( - ), - 170 => - array ( - ), - 171 => - array ( - ), - 172 => - array ( - ), - 174 => - array ( - ), - 175 => - array ( - ), - 188 => - array ( - ), - 189 => - array ( - ), - 190 => - array ( - ), - 191 => - array ( - ), - 192 => - array ( - ), - 193 => - array ( - ), - 194 => - array ( - ), - 199 => - array ( - ), - 200 => - array ( - ), - 201 => - array ( - ), - 202 => - array ( - ), - 203 => - array ( - ), - 205 => - array ( - ), - 220 => - array ( - ), - 225 => - array ( - ), - 226 => - array ( - ), - 227 => - array ( - ), - 229 => - array ( - ), - 231 => - array ( - ), - 236 => - array ( - ), - 241 => - array ( - ), - 242 => - array ( - ), - 243 => - array ( - ), - 248 => - array ( - ), - 249 => - array ( - ), - 250 => - array ( - ), - 251 => - array ( - ), - 253 => - array ( - ), - 255 => - array ( - ), - 262 => - array ( - ), - 267 => - array ( - ), - 272 => - array ( - ), - 277 => - array ( - ), - 278 => - array ( - ), - 279 => - array ( - ), - 281 => - array ( - ), - 283 => - array ( - ), - 285 => - array ( - ), - 287 => - array ( - ), - 289 => - array ( - ), - 290 => - array ( - ), - 291 => - array ( - ), - 293 => - array ( - ), - 295 => - array ( - ), - 297 => - array ( - ), - ), - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/RefreshToken.php' => - array ( - 21 => - array ( - ), - 23 => - array ( - ), - 24 => - array ( - ), - 28 => - array ( - ), - 30 => - array ( - ), - 31 => - array ( - ), - 32 => - array ( - ), - 33 => - array ( - ), - 34 => - array ( - ), - 35 => - array ( - ), - 36 => - array ( - ), - 37 => - array ( - ), - 38 => - array ( - ), - 39 => - array ( - ), - 40 => - array ( - ), - 41 => - array ( - ), - 42 => - array ( - ), - 43 => - array ( - ), - 44 => - array ( - ), - 45 => - array ( - ), - 48 => - array ( - ), - 50 => - array ( - ), - 51 => - array ( - ), - 52 => - array ( - ), - 53 => - array ( - ), - 55 => - array ( - ), - 56 => - array ( - ), - 58 => - array ( - ), - 59 => - array ( - ), - 60 => - array ( - ), - 62 => - array ( - ), - 63 => - array ( - ), - 65 => - array ( - ), - 67 => - array ( - ), - 69 => - array ( - ), - ), -)); -$coverage->setTests(array ( - 'UNCOVERED_FILES_FROM_WHITELIST' => - array ( - 'size' => 'unknown', - 'status' => NULL, - ), -)); - -$filter = $coverage->filter(); -$filter->setWhitelistedFiles(array ( - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/wp-graphql-jwt-authentication.php' => true, - 0 => true, - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/Auth.php' => true, - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/Login.php' => true, - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/ManageTokens.php' => true, - '/Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/RefreshToken.php' => true, -)); - -return $coverage; \ No newline at end of file diff --git a/tests/_output/coverage.xml b/tests/_output/coverage.xml deleted file mode 100644 index 06d8f30..0000000 --- a/tests/_output/coverage.xml +++ /dev/null @@ -1,508 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/_output/functional.remote.coverage.xml b/tests/_output/functional.remote.coverage.xml deleted file mode 100644 index 3b1cffa..0000000 --- a/tests/_output/functional.remote.coverage.xml +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/_output/functional.remote.coverage/.css/bootstrap.min.css b/tests/_output/functional.remote.coverage/.css/bootstrap.min.css deleted file mode 100644 index b27cb19..0000000 --- a/tests/_output/functional.remote.coverage/.css/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../.fonts/glyphicons-halflings-regular.eot);src:url(../.fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../.fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../.fonts/glyphicons-halflings-regular.woff) format('woff'),url(../.fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../.fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/tests/_output/functional.remote.coverage/.css/nv.d3.min.css b/tests/_output/functional.remote.coverage/.css/nv.d3.min.css deleted file mode 100644 index 7a6f7fe..0000000 --- a/tests/_output/functional.remote.coverage/.css/nv.d3.min.css +++ /dev/null @@ -1 +0,0 @@ -.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc} \ No newline at end of file diff --git a/tests/_output/functional.remote.coverage/.css/style.css b/tests/_output/functional.remote.coverage/.css/style.css deleted file mode 100644 index 824fb31..0000000 --- a/tests/_output/functional.remote.coverage/.css/style.css +++ /dev/null @@ -1,122 +0,0 @@ -body { - padding-top: 10px; -} - -.popover { - max-width: none; -} - -.glyphicon { - margin-right:.25em; -} - -.table-bordered>thead>tr>td { - border-bottom-width: 1px; -} - -.table tbody>tr>td, .table thead>tr>td { - padding-top: 3px; - padding-bottom: 3px; -} - -.table-condensed tbody>tr>td { - padding-top: 0; - padding-bottom: 0; -} - -.table .progress { - margin-bottom: inherit; -} - -.table-borderless th, .table-borderless td { - border: 0 !important; -} - -.table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { - background-color: #dff0d8; -} - -.table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { - background-color: #c3e3b5; -} - -.table tbody tr.covered-by-small-tests, li.covered-by-small-tests { - background-color: #99cb84; -} - -.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { - background-color: #f2dede; -} - -.table tbody td.warning, li.warning, span.warning { - background-color: #fcf8e3; -} - -.table tbody td.info { - background-color: #d9edf7; -} - -td.big { - width: 117px; -} - -td.small { -} - -td.codeLine { - font-family: monospace; - white-space: pre; -} - -td span.comment { - color: #888a85; -} - -td span.default { - color: #2e3436; -} - -td span.html { - color: #888a85; -} - -td span.keyword { - color: #2e3436; - font-weight: bold; -} - -pre span.string { - color: #2e3436; -} - -span.success, span.warning, span.danger { - margin-right: 2px; - padding-left: 10px; - padding-right: 10px; - text-align: center; -} - -#classCoverageDistribution, #classComplexity { - height: 200px; - width: 475px; -} - -#toplink { - position: fixed; - left: 5px; - bottom: 5px; - outline: 0; -} - -svg text { - font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; - font-size: 11px; - color: #666; - fill: #666; -} - -.scrollbox { - height:245px; - overflow-x:hidden; - overflow-y:scroll; -} diff --git a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.eot b/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a495..0000000 Binary files a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.svg b/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.svg deleted file mode 100644 index 94fb549..0000000 --- a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.ttf b/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc6..0000000 Binary files a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.woff b/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 9e61285..0000000 Binary files a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.woff and /dev/null differ diff --git a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.woff2 b/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b5..0000000 Binary files a/tests/_output/functional.remote.coverage/.fonts/glyphicons-halflings-regular.woff2 and /dev/null differ diff --git a/tests/_output/functional.remote.coverage/.js/bootstrap.min.js b/tests/_output/functional.remote.coverage/.js/bootstrap.min.js deleted file mode 100644 index 9bcd2fc..0000000 --- a/tests/_output/functional.remote.coverage/.js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/tests/_output/functional.remote.coverage/.js/d3.min.js b/tests/_output/functional.remote.coverage/.js/d3.min.js deleted file mode 100644 index 1664873..0000000 --- a/tests/_output/functional.remote.coverage/.js/d3.min.js +++ /dev/null @@ -1,5 +0,0 @@ -!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++ie;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++aa;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)0?0:3:xo(r[0]-e)0?2:1:xo(r[1]-t)0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ -r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)Uo?{x:s,y:xo(t-s)Uo?{x:xo(e-g)Uo?{x:h,y:xo(t-h)Uo?{x:xo(e-p)=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.yd||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.yr||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.yp){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.xu||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return ur;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++oe;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.ro;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++0;h--)o.push(u(c)*h);for(c=0;o[c]l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++oe?[NaN,NaN]:[e>0?a[e-1]:n[0],et?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}else{for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++ii){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++rr;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++uu;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], -shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.xy&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++lt;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++cs?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; -if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}(); \ No newline at end of file diff --git a/tests/_output/functional.remote.coverage/.js/file.js b/tests/_output/functional.remote.coverage/.js/file.js deleted file mode 100644 index 756cc08..0000000 --- a/tests/_output/functional.remote.coverage/.js/file.js +++ /dev/null @@ -1,61 +0,0 @@ - $(function() { - var $window = $(window) - , $top_link = $('#toplink') - , $body = $('body, html') - , offset = $('#code').offset().top - , hidePopover = function ($target) { - $target.data('popover-hover', false); - - setTimeout(function () { - if (!$target.data('popover-hover')) { - $target.popover('hide'); - } - }, 300); - }; - - $top_link.hide().click(function(event) { - event.preventDefault(); - $body.animate({scrollTop:0}, 800); - }); - - $window.scroll(function() { - if($window.scrollTop() > offset) { - $top_link.fadeIn(); - } else { - $top_link.fadeOut(); - } - }).scroll(); - - $('.popin') - .popover({trigger: 'manual'}) - .on({ - 'mouseenter.popover': function () { - var $target = $(this); - - $target.data('popover-hover', true); - - // popover already displayed - if ($target.next('.popover').length) { - return; - } - - // show the popover - $target.popover('show'); - - // register mouse events on the popover - $target.next('.popover:not(.popover-initialized)') - .on({ - 'mouseenter': function () { - $target.data('popover-hover', true); - }, - 'mouseleave': function () { - hidePopover($target); - } - }) - .addClass('popover-initialized'); - }, - 'mouseleave.popover': function () { - hidePopover($(this)); - } - }); - }); diff --git a/tests/_output/functional.remote.coverage/.js/holder.min.js b/tests/_output/functional.remote.coverage/.js/holder.min.js deleted file mode 100644 index 6bfc844..0000000 --- a/tests/_output/functional.remote.coverage/.js/holder.min.js +++ /dev/null @@ -1,12 +0,0 @@ -/*! - -Holder - client side image placeholders -Version 2.7.1+6hydf -© 2015 Ivan Malopinsky - http://imsky.co - -Site: http://holderjs.com -Issues: https://github.com/imsky/holder/issues -License: http://opensource.org/licenses/MIT - -*/ -!function(a){if(a.document){var b=a.document;b.querySelectorAll||(b.querySelectorAll=function(c){var d,e=b.createElement("style"),f=[];for(b.documentElement.firstChild.appendChild(e),b._qsa=[],e.styleSheet.cssText=c+"{x-qsa:expression(document._qsa && document._qsa.push(this))}",a.scrollBy(0,0),e.parentNode.removeChild(e);b._qsa.length;)d=b._qsa.shift(),d.style.removeAttribute("x-qsa"),f.push(d);return b._qsa=null,f}),b.querySelector||(b.querySelector=function(a){var c=b.querySelectorAll(a);return c.length?c[0]:null}),b.getElementsByClassName||(b.getElementsByClassName=function(a){return a=String(a).replace(/^|\s+/g,"."),b.querySelectorAll(a)}),Object.keys||(Object.keys=function(a){if(a!==Object(a))throw TypeError("Object.keys called on non-object");var b,c=[];for(b in a)Object.prototype.hasOwnProperty.call(a,b)&&c.push(b);return c}),function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a.atob=a.atob||function(a){a=String(a);var c,d=0,e=[],f=0,g=0;if(a=a.replace(/\s/g,""),a.length%4===0&&(a=a.replace(/=+$/,"")),a.length%4===1)throw Error("InvalidCharacterError");if(/[^+/0-9A-Za-z]/.test(a))throw Error("InvalidCharacterError");for(;d>16&255)),e.push(String.fromCharCode(f>>8&255)),e.push(String.fromCharCode(255&f)),g=0,f=0),d+=1;return 12===g?(f>>=4,e.push(String.fromCharCode(255&f))):18===g&&(f>>=2,e.push(String.fromCharCode(f>>8&255)),e.push(String.fromCharCode(255&f))),e.join("")},a.btoa=a.btoa||function(a){a=String(a);var c,d,e,f,g,h,i,j=0,k=[];if(/[^\x00-\xFF]/.test(a))throw Error("InvalidCharacterError");for(;j>2,g=(3&c)<<4|d>>4,h=(15&d)<<2|e>>6,i=63&e,j===a.length+2?(h=64,i=64):j===a.length+1&&(i=64),k.push(b.charAt(f),b.charAt(g),b.charAt(h),b.charAt(i));return k.join("")}}(a),Object.prototype.hasOwnProperty||(Object.prototype.hasOwnProperty=function(a){var b=this.__proto__||this.constructor.prototype;return a in this&&(!(a in b)||b[a]!==this[a])}),function(){if("performance"in a==!1&&(a.performance={}),Date.now=Date.now||function(){return(new Date).getTime()},"now"in a.performance==!1){var b=Date.now();performance.timing&&performance.timing.navigationStart&&(b=performance.timing.navigationStart),a.performance.now=function(){return Date.now()-b}}}(),a.requestAnimationFrame||(a.webkitRequestAnimationFrame?!function(a){a.requestAnimationFrame=function(b){return webkitRequestAnimationFrame(function(){b(a.performance.now())})},a.cancelAnimationFrame=webkitCancelAnimationFrame}(a):a.mozRequestAnimationFrame?!function(a){a.requestAnimationFrame=function(b){return mozRequestAnimationFrame(function(){b(a.performance.now())})},a.cancelAnimationFrame=mozCancelAnimationFrame}(a):!function(a){a.requestAnimationFrame=function(b){return a.setTimeout(b,1e3/60)},a.cancelAnimationFrame=a.clearTimeout}(a))}}(this),function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):"object"==typeof exports?exports.Holder=b():a.Holder=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){(function(b){function d(a,b,c,d){var f=e(c.substr(c.lastIndexOf(a.domain)),a);f&&h({mode:null,el:d,flags:f,engineSettings:b})}function e(a,b){var c={theme:B(J.settings.themes.gray,null),stylesheets:b.stylesheets,instanceOptions:b};return a.match(/([\d]+p?)x([\d]+p?)(?:\?|$)/)?f(a,c):g(a,c)}function f(a,b){var c=a.split("?"),d=c[0].split("/");b.holderURL=a;var e=d[1],f=e.match(/([\d]+p?)x([\d]+p?)/);if(!f)return!1;if(b.fluid=-1!==e.indexOf("p"),b.dimensions={width:f[1].replace("p","%"),height:f[2].replace("p","%")},2===c.length){var g=A.parse(c[1]);if(g.bg&&(b.theme.background=(-1===g.bg.indexOf("#")?"#":"")+g.bg),g.fg&&(b.theme.foreground=(-1===g.fg.indexOf("#")?"#":"")+g.fg),g.theme&&b.instanceOptions.themes.hasOwnProperty(g.theme)&&(b.theme=B(b.instanceOptions.themes[g.theme],null)),g.text&&(b.text=g.text),g.textmode&&(b.textmode=g.textmode),g.size&&(b.size=g.size),g.font&&(b.font=g.font),g.align&&(b.align=g.align),b.nowrap=z.truthy(g.nowrap),b.auto=z.truthy(g.auto),z.truthy(g.random)){J.vars.cache.themeKeys=J.vars.cache.themeKeys||Object.keys(b.instanceOptions.themes);var h=J.vars.cache.themeKeys[0|Math.random()*J.vars.cache.themeKeys.length];b.theme=B(b.instanceOptions.themes[h],null)}}return b}function g(a,b){var c=!1,d=String.fromCharCode(11),e=a.replace(/([^\\])\//g,"$1"+d).split(d),f=/%[0-9a-f]{2}/gi,g=b.instanceOptions;b.holderURL=[];for(var h=e.length,i=0;h>i;i++){var j=e[i];if(j.match(f))try{j=decodeURIComponent(j)}catch(k){j=e[i]}var l=!1;if(J.flags.dimensions.match(j))c=!0,b.dimensions=J.flags.dimensions.output(j),l=!0;else if(J.flags.fluid.match(j))c=!0,b.dimensions=J.flags.fluid.output(j),b.fluid=!0,l=!0;else if(J.flags.textmode.match(j))b.textmode=J.flags.textmode.output(j),l=!0;else if(J.flags.colors.match(j)){var m=J.flags.colors.output(j);b.theme=B(b.theme,m),l=!0}else if(g.themes[j])g.themes.hasOwnProperty(j)&&(b.theme=B(g.themes[j],null)),l=!0;else if(J.flags.font.match(j))b.font=J.flags.font.output(j),l=!0;else if(J.flags.auto.match(j))b.auto=!0,l=!0;else if(J.flags.text.match(j))b.text=J.flags.text.output(j),l=!0;else if(J.flags.size.match(j))b.size=J.flags.size.output(j),l=!0;else if(J.flags.random.match(j)){null==J.vars.cache.themeKeys&&(J.vars.cache.themeKeys=Object.keys(g.themes));var n=J.vars.cache.themeKeys[0|Math.random()*J.vars.cache.themeKeys.length];b.theme=B(g.themes[n],null),l=!0}l&&b.holderURL.push(j)}return b.holderURL.unshift(g.domain),b.holderURL=b.holderURL.join("/"),c?b:!1}function h(a){var b=a.mode,c=a.el,d=a.flags,e=a.engineSettings,f=d.dimensions,g=d.theme,h=f.width+"x"+f.height;if(b=null==b?d.fluid?"fluid":"image":b,null!=d.text&&(g.text=d.text,"object"===c.nodeName.toLowerCase())){for(var j=g.text.split("\\n"),k=0;k1){var n,o=0,p=0,q=0;j=new e.Group("line"+q),("left"===a.align||"right"===a.align)&&(m=a.width*(1-2*(1-J.setup.lineWrapRatio)));for(var r=0;r=m||t===!0)&&(b(g,j,o,g.properties.leading),g.add(j),o=0,p+=g.properties.leading,q+=1,j=new e.Group("line"+q),j.y=p),t!==!0&&(i.moveTo(o,0),o+=h.spaceWidth+s.width,j.add(i))}if(b(g,j,o,g.properties.leading),g.add(j),"left"===a.align)g.moveTo(a.width-l,null,null);else if("right"===a.align){for(n in g.children)j=g.children[n],j.moveTo(a.width-j.width,null,null);g.moveTo(0-(a.width-l),null,null)}else{for(n in g.children)j=g.children[n],j.moveTo((g.width-j.width)/2,null,null);g.moveTo((a.width-g.width)/2,null,null)}g.moveTo(null,(a.height-g.height)/2,null),(a.height-g.height)/2<0&&g.moveTo(null,0,null)}else i=new e.Text(a.text),j=new e.Group("line0"),j.add(i),g.add(j),"left"===a.align?g.moveTo(a.width-l,null,null):"right"===a.align?g.moveTo(0-(a.width-l),null,null):g.moveTo((a.width-h.boundingBox.width)/2,null,null),g.moveTo(null,(a.height-h.boundingBox.height)/2,null);return d}function k(a,b,c){var d=parseInt(a,10),e=parseInt(b,10),f=Math.max(d,e),g=Math.min(d,e),h=.8*Math.min(g,f*J.defaults.scale);return Math.round(Math.max(c,h))}function l(a){var b;b=null==a||null==a.nodeType?J.vars.resizableImages:[a];for(var c=0,d=b.length;d>c;c++){var e=b[c];if(e.holderData){var f=e.holderData.flags,g=D(e);if(g){if(!e.holderData.resizeUpdate)continue;if(f.fluid&&f.auto){var h=e.holderData.fluidConfig;switch(h.mode){case"width":g.height=g.width/h.ratio;break;case"height":g.width=g.height*h.ratio}}var j={mode:"image",holderSettings:{dimensions:g,theme:f.theme,flags:f},el:e,engineSettings:e.holderData.engineSettings};"exact"==f.textmode&&(f.exactDimensions=g,j.holderSettings.dimensions=f.dimensions),i(j)}else p(e)}}}function m(a){if(a.holderData){var b=D(a);if(b){var c=a.holderData.flags,d={fluidHeight:"%"==c.dimensions.height.slice(-1),fluidWidth:"%"==c.dimensions.width.slice(-1),mode:null,initialDimensions:b};d.fluidWidth&&!d.fluidHeight?(d.mode="width",d.ratio=d.initialDimensions.width/parseFloat(c.dimensions.height)):!d.fluidWidth&&d.fluidHeight&&(d.mode="height",d.ratio=parseFloat(c.dimensions.width)/d.initialDimensions.height),a.holderData.fluidConfig=d}else p(a)}}function n(){for(var a,c=[],d=Object.keys(J.vars.invisibleImages),e=0,f=d.length;f>e;e++)a=J.vars.invisibleImages[d[e]],D(a)&&"img"==a.nodeName.toLowerCase()&&(c.push(a),delete J.vars.invisibleImages[d[e]]);c.length&&I.run({images:c}),b.requestAnimationFrame(n)}function o(){J.vars.visibilityCheckStarted||(b.requestAnimationFrame(n),J.vars.visibilityCheckStarted=!0)}function p(a){a.holderData.invisibleId||(J.vars.invisibleId+=1,J.vars.invisibleImages["i"+J.vars.invisibleId]=a,a.holderData.invisibleId=J.vars.invisibleId)}function q(a,b){return null==b?document.createElement(a):document.createElementNS(b,a)}function r(a,b){for(var c in b)a.setAttribute(c,b[c])}function s(a,b,c){var d,e;null==a?(a=q("svg",E),d=q("defs",E),e=q("style",E),r(e,{type:"text/css"}),d.appendChild(e),a.appendChild(d)):e=a.querySelector("style"),a.webkitMatchesSelector&&a.setAttribute("xmlns",E);for(var f=0;f=0;h--){var i=g.createProcessingInstruction("xml-stylesheet",'href="'+f[h]+'" rel="stylesheet"');g.insertBefore(i,g.firstChild)}g.removeChild(g.documentElement),e=d.serializeToString(g)}var j=d.serializeToString(a);return j=j.replace(/\&(\#[0-9]{2,}\;)/g,"&$1"),e+j}}function u(){return b.DOMParser?(new DOMParser).parseFromString("","application/xml"):void 0}function v(a){J.vars.debounceTimer||a.call(this),J.vars.debounceTimer&&b.clearTimeout(J.vars.debounceTimer),J.vars.debounceTimer=b.setTimeout(function(){J.vars.debounceTimer=null,a.call(this)},J.setup.debounce)}function w(){v(function(){l(null)})}var x=c(1),y=c(2),z=c(3),A=c(4),B=z.extend,C=z.getNodeArray,D=z.dimensionCheck,E="http://www.w3.org/2000/svg",F=8,G="2.7.1",H="\nCreated with Holder.js "+G+".\nLearn more at http://holderjs.com\n(c) 2012-2015 Ivan Malopinsky - http://imsky.co\n",I={version:G,addTheme:function(a,b){return null!=a&&null!=b&&(J.settings.themes[a]=b),delete J.vars.cache.themeKeys,this},addImage:function(a,b){var c=document.querySelectorAll(b);if(c.length)for(var d=0,e=c.length;e>d;d++){var f=q("img"),g={};g[J.vars.dataAttr]=a,r(f,g),c[d].appendChild(f)}return this},setResizeUpdate:function(a,b){a.holderData&&(a.holderData.resizeUpdate=!!b,a.holderData.resizeUpdate&&l(a))},run:function(a){a=a||{};var c={},f=B(J.settings,a);J.vars.preempted=!0,J.vars.dataAttr=f.dataAttr||J.vars.dataAttr,c.renderer=f.renderer?f.renderer:J.setup.renderer,-1===J.setup.renderers.join(",").indexOf(c.renderer)&&(c.renderer=J.setup.supportsSVG?"svg":J.setup.supportsCanvas?"canvas":"html");var g=C(f.images),i=C(f.bgnodes),j=C(f.stylenodes),k=C(f.objects);c.stylesheets=[],c.svgXMLStylesheet=!0,c.noFontFallback=f.noFontFallback?f.noFontFallback:!1;for(var l=0;l1){c.nodeValue="";for(var u=0;u=0?b:1)}function f(a){v?e(a):w.push(a)}null==document.readyState&&document.addEventListener&&(document.addEventListener("DOMContentLoaded",function y(){document.removeEventListener("DOMContentLoaded",y,!1),document.readyState="complete"},!1),document.readyState="loading");var g=a.document,h=g.documentElement,i="load",j=!1,k="on"+i,l="complete",m="readyState",n="attachEvent",o="detachEvent",p="addEventListener",q="DOMContentLoaded",r="onreadystatechange",s="removeEventListener",t=p in g,u=j,v=j,w=[];if(g[m]===l)e(b);else if(t)g[p](q,c,j),a[p](i,c,j);else{g[n](r,c),a[n](k,c);try{u=null==a.frameElement&&h}catch(x){}u&&u.doScroll&&!function z(){if(!v){try{u.doScroll("left")}catch(a){return e(z,50)}d(),b()}}()}return f.version="1.4.0",f.isReady=function(){return v},f}a.exports="undefined"!=typeof window&&b(window)},function(a,b,c){var d=c(5),e=function(a){function b(a,b){for(var c in b)a[c]=b[c];return a}var c=1,e=d.defclass({constructor:function(a){c++,this.parent=null,this.children={},this.id=c,this.name="n"+c,null!=a&&(this.name=a),this.x=0,this.y=0,this.z=0,this.width=0,this.height=0},resize:function(a,b){null!=a&&(this.width=a),null!=b&&(this.height=b)},moveTo:function(a,b,c){this.x=null!=a?a:this.x,this.y=null!=b?b:this.y,this.z=null!=c?c:this.z},add:function(a){var b=a.name;if(null!=this.children[b])throw"SceneGraph: child with that name already exists: "+b;this.children[b]=a,a.parent=this}}),f=d(e,function(b){this.constructor=function(){b.constructor.call(this,"root"),this.properties=a}}),g=d(e,function(a){function c(c,d){if(a.constructor.call(this,c),this.properties={fill:"#000"},null!=d)b(this.properties,d);else if(null!=c&&"string"!=typeof c)throw"SceneGraph: invalid node name"}this.Group=d.extend(this,{constructor:c,type:"group"}),this.Rect=d.extend(this,{constructor:c,type:"rect"}),this.Text=d.extend(this,{constructor:function(a){c.call(this),this.properties.text=a},type:"text"})}),h=new f;return this.Shape=g,this.root=h,this};a.exports=e},function(a,b){(function(a){b.extend=function(a,b){var c={};for(var d in a)a.hasOwnProperty(d)&&(c[d]=a[d]);if(null!=b)for(var e in b)b.hasOwnProperty(e)&&(c[e]=b[e]);return c},b.cssProps=function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c+":"+a[c]);return b.join(";")},b.encodeHtmlEntity=function(a){for(var b=[],c=0,d=a.length-1;d>=0;d--)c=a.charCodeAt(d),b.unshift(c>128?["&#",c,";"].join(""):a[d]);return b.join("")},b.getNodeArray=function(b){var c=null;return"string"==typeof b?c=document.querySelectorAll(b):a.NodeList&&b instanceof a.NodeList?c=b:a.Node&&b instanceof a.Node?c=[b]:a.HTMLCollection&&b instanceof a.HTMLCollection?c=b:b instanceof Array?c=b:null===b&&(c=[]),c},b.imageExists=function(a,b){var c=new Image;c.onerror=function(){b.call(this,!1)},c.onload=function(){b.call(this,!0)},c.src=a},b.decodeHtmlEntity=function(a){return a.replace(/&#(\d+);/g,function(a,b){return String.fromCharCode(b)})},b.dimensionCheck=function(a){var b={height:a.clientHeight,width:a.clientWidth};return b.height&&b.width?b:!1},b.truthy=function(a){return"string"==typeof a?"true"===a||"yes"===a||"1"===a||"on"===a||"✓"===a:!!a}}).call(b,function(){return this}())},function(a,b,c){var d=encodeURIComponent,e=decodeURIComponent,f=c(6),g=c(7),h=/(\w+)\[(\d+)\]/,i=/\w+\.\w+/;b.parse=function(a){if("string"!=typeof a)return{};if(a=f(a),""===a)return{};"?"===a.charAt(0)&&(a=a.slice(1));for(var b={},c=a.split("&"),d=0;d
",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), -a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), -void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" - - - - - - - diff --git a/tests/_output/functional.remote.coverage/index.html b/tests/_output/functional.remote.coverage/index.html deleted file mode 100644 index 96348a7..0000000 --- a/tests/_output/functional.remote.coverage/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Code Coverage for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication - - - - - - -
-
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 83.87% covered (warning) -
-
-
83.87%
234 / 279
-
- 61.36% covered (warning) -
-
-
61.36%
27 / 44
-
- 20.00% covered (danger) -
-
-
20.00%
1 / 5
src
-
- 82.99% covered (warning) -
-
-
82.99%
200 / 241
-
- 59.46% covered (warning) -
-
-
59.46%
22 / 37
-
- 25.00% covered (danger) -
-
-
25.00%
1 / 4
wp-graphql-jwt-authentication.php
-
- 89.47% covered (warning) -
-
-
89.47%
34 / 38
-
- 71.43% covered (warning) -
-
-
71.43%
5 / 7
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
- -
- - - - - diff --git a/tests/_output/functional.remote.coverage/src/Auth.php.html b/tests/_output/functional.remote.coverage/src/Auth.php.html deleted file mode 100644 index 394aa7b..0000000 --- a/tests/_output/functional.remote.coverage/src/Auth.php.html +++ /dev/null @@ -1,1194 +0,0 @@ - - - - - Code Coverage for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/Auth.php - - - - - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 57.89% covered (warning) -
-
-
57.89%
11 / 19
CRAP
-
- 76.99% covered (warning) -
-
-
76.99%
87 / 113
WPGraphQL\JWT_Authentication\Auth
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 61.90% covered (warning) -
-
-
61.90%
13 / 21
140.70
-
- 76.99% covered (warning) -
-
-
76.99%
87 / 113
 get_secret_key
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
 login_and_get_token
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
4.13
-
- 80.00% covered (warning) -
-
-
80.00%
8 / 10
 get_token_issued
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
 get_token_expiration
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
 get_signed_token
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
5.01
-
- 92.31% covered (success) -
-
-
92.31%
12 / 13
 get_user_jwt_secret
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
7.39
-
- 80.00% covered (warning) -
-
-
80.00%
8 / 10
 issue_new_user_secret
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
5 / 5
 is_jwt_secret_revoked
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
 get_token
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
 get_refresh_token
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
n/a
0 / 0
 anonymousFunction:305#1277
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
6 / 6
 is_refresh_token
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
6
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
 authenticate_user
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
5.02
-
- 60.00% covered (warning) -
-
-
60.00%
3 / 5
 filter_determine_current_user
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
4
-
- 100.00% covered (success) -
-
-
100.00%
5 / 5
 revoke_user_secret
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
20
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 7
 unrevoke_user_secret
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 6
 set_status
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
n/a
0 / 0
 anonymousFunction:485#2020
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
 validate_token
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
12.24
-
- 78.26% covered (warning) -
-
-
78.26%
18 / 23
 get_auth_header
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
5
-
- 100.00% covered (success) -
-
-
100.00%
4 / 4
 get_refresh_header
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<?php
namespace WPGraphQL\JWT_Authentication;
use Firebase\JWT\JWT;
use GraphQL\Error\UserError;
use WPGraphQL\Data\DataSource;
class Auth {
    protected static $issued;
    protected static $expiration;
    protected static $is_refresh_token = false;
    /**
     * This returns the secret key, using the defined constant if defined, and passing it through a filter to
     * allow for the config to be able to be set via another method other than a defined constant, such as an
     * admin UI that allows the key to be updated/changed/revoked at any time without touching server files
     *
     * @return mixed|null|string
     * @since 0.0.1
     */
    public static function get_secret_key() {
        // Use the defined secret key, if it exists
        $secret_key = defined( 'GRAPHQL_JWT_AUTH_SECRET_KEY' ) && ! empty( GRAPHQL_JWT_AUTH_SECRET_KEY ) ? GRAPHQL_JWT_AUTH_SECRET_KEY : 'graphql-jwt-auth';
        return apply_filters( 'graphql_jwt_auth_secret_key', $secret_key );
    }
    /**
     * Get the user and password in the request body and generate a JWT
     *
     * @param string $username
     * @param string $password
     *
     * @return mixed
     * @throws \Exception
     * @since 0.0.1
     */
    public static function login_and_get_token( $username, $password ) {
        /**
         * First thing, check the secret key if not exist return a error
         */
        if ( empty( self::get_secret_key() ) ) {
            throw new UserError( __( 'JWT Auth is not configured correctly. Please contact a site administrator.', 'wp-graphql-jwt-authentication' ) );
        }
        /**
         * Authenticate the user and get the Authenticated user object in response
         */
        $user = self::authenticate_user( $username, $password );
        /**
         * Set the current user to the authenticated user
         */
        if ( empty( $user->data->ID ) ) {
            throw new UserError( __( 'The user could not be found', 'wp-graphql-jwt-authentication' ) );
        }
        /**
         * Set the current user as the authenticated user
         */
        wp_set_current_user( $user->data->ID );
        /**
         * The token is signed, now create the object with basic user data to send to the client
         */
        $response = [
            'authToken'    => self::get_signed_token( $user ),
            'refreshToken' => self::get_refresh_token( $user ),
            'user'         => DataSource::resolve_user( $user->data->ID ),
        ];
        /**
         * Let the user modify the data before send it back
         */
        return ! empty( $response ) ? $response : [];
    }
    /**
     * Get the issued time for the token
     *
     * @return int
     */
    public static function get_token_issued() {
        if ( ! isset( self::$issued ) ) {
            self::$issued = time();
        }
        return self::$issued;
    }
    /**
     * Returns the expiration for the token
     *
     * @return mixed|string|null
     */
    public static function get_token_expiration() {
        if ( ! isset( self::$expiration ) ) {
            /**
             * Set the expiration time, default is 300 seconds.
             */
            $expiration = self::get_token_issued() + 300;
            /**
             * Determine the expiration value. Default is 7 days, but is filterable to be configured as needed
             *
             * @param string $expiration The timestamp for when the token should expire
             */
            self::$expiration = apply_filters( 'graphql_jwt_auth_expire', $expiration );
        }
        return ! empty( self::$expiration ) ? self::$expiration : null;
    }
    /**
     * @param $user
     *
     * @return null|string
     */
    protected static function get_signed_token( \WP_User $user, $cap_check = true ) {
        /**
         * Only allow the currently signed in user access to a JWT token
         */
        if ( true === $cap_check && wp_get_current_user()->ID !== $user->ID || 0 === $user->ID ) {
            return new \WP_Error( 'graphql-jwt-no-permissions', __( 'Only the user requesting a token can get a token issued for them', 'wp-graphql-jwt-authentication' ) );
        }
        /**
         * Determine the "not before" value for use in the token
         *
         * @param string   $issued The timestamp of the authentication, used in the token
         * @param \WP_User $user   The authenticated user
         */
        $not_before = apply_filters( 'graphql_jwt_auth_not_before', self::get_token_issued(), $user );
        /**
         * Configure the token array, which will be encoded
         */
        $token = [
            'iss'  => get_bloginfo( 'url' ),
            'iat'  => self::get_token_issued(),
            'nbf'  => $not_before,
            'exp'  => self::get_token_expiration(),
            'data' => [
                'user' => [
                    'id' => $user->data->ID,
                ],
            ],
        ];
        /**
         * Filter the token, allowing for individual systems to configure the token as needed
         *
         * @param array    $token The token array that will be encoded
         * @param \WP_User $token The authenticated user
         */
        $token = apply_filters( 'graphql_jwt_auth_token_before_sign', $token, $user );
        /**
         * Encode the token
         */
        JWT::$leeway = 60;
        $token       = JWT::encode( $token, self::get_secret_key() );
        /**
         * Filter the token before returning it, allowing for individual systems to override what's returned.
         *
         * For example, if the user should not be granted a token for whatever reason, a filter could have the token return null.
         *
         * @param string $token   The signed JWT token that will be returned
         * @param int    $user_id The User the JWT is associated with
         */
        $token = apply_filters( 'graphql_jwt_auth_signed_token', $token, $user->ID );
        /**
         * Return the token
         */
        return ! empty( $token ) ? $token : null;
    }
    /**
     * Given a User ID, returns the user's JWT secret
     *
     * @param int $user_id
     *
     * @return mixed|string
     */
    public static function get_user_jwt_secret( $user_id ) {
        /**
         * If the secret has been revoked, throw an error
         */
        if ( true === Auth::is_jwt_secret_revoked( $user_id ) ) {
            return new \WP_Error( 'graphql-jwt-revoked-secret', __( 'The JWT Auth secret cannot be returned', 'wp-graphql-jwt-authentication' ) );
        }
        /**
         * Filter the capability that is tied to editing/viewing user JWT Auth info
         *
         * @param     string 'edit_users'
         * @param int $user_id
         */
        $capability = apply_filters( 'graphql_jwt_auth_edit_users_capability', 'edit_users', $user_id );
        /**
         * If the request is not from the current_user and the current_user doesn't have the proper capabilities, don't return the secret
         */
        $is_current_user = ( $user_id === wp_get_current_user()->ID ) ? true : false;
        if ( ! $is_current_user && ! current_user_can( $capability ) ) {
            return new \WP_Error( 'graphql-jwt-improper-capabilities', __( 'The JWT Auth secret for this user cannot be returned', 'wp-graphql-jwt-authentication' ) );
        }
        /**
         * Get the stored secret
         */
        $secret = get_user_meta( $user_id, 'graphql_jwt_auth_secret', true );
        /**
         * If there is no stored secret, or it's not a string
         */
        if ( empty( $secret ) || ! is_string( $secret ) ) {
            Auth::issue_new_user_secret( $user_id );
        }
        /**
         * Return the $secret
         *
         * @param string $secret  The GraphQL JWT Auth Secret associated with the user
         * @param int    $user_id The ID of the user the secret is associated with
         */
        return apply_filters( 'graphql_jwt_auth_user_secret', $secret, $user_id );
    }
    /**
     * Given a User ID, issue a new JWT Auth Secret
     *
     * @param int $user_id The ID of the user the secret is being issued for
     *
     * @return string $secret The JWT User secret for the user.
     */
    public static function issue_new_user_secret( $user_id ) {
        /**
         * Get the current user secret
         */
        $secret = null;
        /**
         * If the JWT Secret is not revoked for the user, generate a new one
         */
        if ( ! Auth::is_jwt_secret_revoked( $user_id ) ) {
            /**
             * Generate a new one and store it
             */
            $secret = uniqid( 'graphql_jwt_' );
            update_user_meta( $user_id, 'graphql_jwt_auth_secret', $secret );
        }
        return ! is_wp_error( $secret ) ? $secret : null;
    }
    /**
     * Given a User, returns whether their JWT secret has been revoked or not.
     *
     * @param int $user_id
     *
     * @return bool
     */
    public static function is_jwt_secret_revoked( $user_id ) {
        $revoked = (bool) get_user_meta( $user_id, 'graphql_jwt_auth_secret_revoked', true );
        return isset( $revoked ) && true === $revoked ? true : false;
    }
    /**
     * Public method for getting an Auth token for a given user
     *
     * @param \WP_USer $user The user to get the token for
     *
     * @return null|string
     */
    public static function get_token( $user, $cap_check = true ) {
        return self::get_signed_token( $user, $cap_check );
    }
    public static function get_refresh_token( $user, $cap_check = true ) {
        /**
         * Filter the token signature for refresh tokens, adding the user_secret to the signature and making the
         * expiration long lived so that the token can be used for a long time without the client having to store a new
         * one.
         */
        add_filter( 'graphql_jwt_auth_token_before_sign', function( $token, \WP_User $user ) {
            $secret = Auth::get_user_jwt_secret( $user->ID );
            if ( ! empty( $secret ) && ! is_wp_error( $secret ) ) {
                /**
                 * Set the expiration date as a year from now to make the refresh token long lived, allowing the
                 * token to be valid without changing as long as it has not been revoked or otherwise invalidated,
                 * such as a refreshed user secret.
                 */
                $token['exp']                         = apply_filters( 'graphql_jwt_auth_refresh_token_expiration', ( self::get_token_issued() + ( DAY_IN_SECONDS * 365 ) ) );
                $token['data']['user']['user_secret'] = $secret;
            }
            return $token;
        }, 10, 2 );
        return self::get_signed_token( $user, $cap_check );
    }
    public static function is_refresh_token() {
        return true === self::$is_refresh_token ? true : false;
    }
    /**
     * Takes a username and password and authenticates the user and returns the authenticated user object
     *
     * @param string $username The username for the user to login
     * @param string $password The password for the user to login
     *
     * @return null|\WP_Error|\WP_User
     */
    protected static function authenticate_user( $username, $password ) {
        /**
         * Try to authenticate the user with the passed credentials
         */
        $user = wp_authenticate( sanitize_user( $username ), trim( $password ) );
        /**
         * If the authentication fails return a error
         */
        if ( is_wp_error( $user ) ) {
            $error_code = ! empty( $user->get_error_code() ) ? $user->get_error_code() : 'invalid login';
            throw new UserError( esc_html( $error_code ) );
        }
        return ! empty( $user ) ? $user : null;
    }
    /**
     * This is our Middleware to try to authenticate the user according to the
     * token send.
     *
     * @param (int|bool) $user Logged User ID
     *
     * @return mixed|false|\WP_User
     */
    public static function filter_determine_current_user( $user ) {
        /**
         * Validate the token, which will check the Headers to see if Authentication headers were sent
         *
         * @since 0.0.1
         */
        $token = Auth::validate_token();
        /**
         * If no token was generated, return the existing value for the $user
         */
        if ( empty( $token ) ) {
            /**
             * Return the user that was passed in to the filter
             */
            return $user;
            /**
             * If there is a token
             */
        } else {
            /**
             * Get the current user from the token
             */
            $user = ! empty( $token ) && ! empty( $token->data->user->id ) ? $token->data->user->id : $user;
        }
        /**
         * Everything is ok, return the user ID stored in the token
         */
        return $user;
    }
    /**
     * Given a user ID, if the ID is for a valid user and the current user has proper capabilities, this revokes
     * the JWT Secret from the user.
     *
     * @param int $user_id
     *
     * @return mixed|boolean|\WP_Error
     */
    public static function revoke_user_secret( int $user_id ) {
        /**
         * Filter the capability that is tied to editing/viewing user JWT Auth info
         *
         * @param     string 'edit_users'
         * @param int $user_id
         */
        $capability = apply_filters( 'graphql_jwt_auth_edit_users_capability', 'edit_users', $user_id );
        /**
         * If the current user can edit users, or the current user is the user being edited
         */
        if (
            0 !== get_user_by( 'id', $user_id )->ID &&
            (
                current_user_can( $capability ) ||
                $user_id === wp_get_current_user()->ID
            )
        ) {
            /**
             * Set the user meta as true, marking the secret as revoked
             */
            update_user_meta( $user_id, 'graphql_jwt_auth_secret_revoked', 1 );
            return true;
        } else {
            return new \WP_Error( 'graphql-jwt-auth-cannot-revoke-secret', __( 'The JWT Auth Secret cannot be revoked for this user', 'wp-graphql-jwt-authentication' ) );
        }
    }
    /**
     * Given a user ID, if the ID is for a valid user and the current user has proper capabilities, this unrevokes
     * the JWT Secret from the user.
     *
     * @param int $user_id
     *
     * @return mixed|boolean|\WP_Error
     */
    public static function unrevoke_user_secret( int $user_id ) {
        /**
         * Filter the capability that is tied to editing/viewing user JWT Auth info
         *
         * @param     string 'edit_users'
         * @param int $user_id
         */
        $capability = apply_filters( 'graphql_jwt_auth_edit_users_capability', 'edit_users', $user_id );
        /**
         * If the user_id is a valid user, and the current user can edit_users
         */
        if ( 0 !== get_user_by( 'id', $user_id )->ID && current_user_can( $capability ) ) {
            /**
             * Issue a new user secret, invalidating any that may have previously been in place, and mark the
             * revoked meta key as false, showing that the secret has not been revoked
             */
            Auth::issue_new_user_secret( $user_id );
            update_user_meta( $user_id, 'graphql_jwt_auth_secret_revoked', 0 );
            return true;
        } else {
            return new \WP_Error( 'graphql-jwt-auth-cannot-unrevoke-secret', __( 'The JWT Auth Secret cannot be unrevoked for this user', 'wp-graphql-jwt-authentication' ) );
        }
    }
    protected static function set_status( $status_code ) {
        add_filter( 'graphql_response_status_code', function() use ( $status_code ) {
            return $status_code;
        });
    }
    /**
     * Main validation function, this function try to get the Authentication
     * headers and decoded.
     *
     * @param string $token The encoded JWT Token
     *
     * @throws \Exception
     * @return mixed|boolean|string
     */
    public static function validate_token( $token = null, $refresh = false ) {
        self::$is_refresh_token = ( true === $refresh ) ? true : false;
        /**
         * If a token isn't passed to the method, check the Authorization Headers to see if a token was
         * passed in the headers
         *
         * @since 0.0.1
         */
        if ( empty( $token ) ) {
            /**
             * Get the Auth header
             */
            $auth_header = self::get_auth_header();
            /**
             * If there's no $auth, return an error
             *
             * @since 0.0.1
             */
            if ( empty( $auth_header ) ) {
                return false;
            } else {
                /**
                 * The HTTP_AUTHORIZATION is present verify the format
                 * if the format is wrong return the user.
                 */
                list( $token ) = sscanf( $auth_header, 'Bearer %s' );
            }
        }
        /**
         * If there's no secret key, throw an error as there needs to be a secret key for Auth to work properly
         */
        if ( ! self::get_secret_key() ) {
            throw new \Exception( __( 'JWT is not configured properly', 'wp-graphql-jwt-authentication' ) );
        }
        /**
         * Try to decode the token
         */
        try {
            /**
             * Decode the Token
             */
            JWT::$leeway = 60;
            $secret = self::get_secret_key();
            $token = ! empty( $token ) ? JWT::decode( $token, $secret, [ 'HS256' ] ) : null;
            /**
             * The Token is decoded now validate the iss
             */
            if ( get_bloginfo( 'url' ) !== $token->iss ) {
                throw new \Exception( __( 'The iss do not match with this server', 'wp-graphql-jwt-authentication' ) );
            }
            /**
             * So far so good, validate the user id in the token
             */
            if ( ! isset( $token->data->user->id ) ) {
                throw new \Exception( __( 'User ID not found in the token', 'wp-graphql-jwt-authentication' ) );
            }
            /**
             * If there is a user_secret in the token (refresh tokens) make sure it matches what
             */
            if ( isset( $token->data->user->user_secret ) ) {
                if ( Auth::is_jwt_secret_revoked( $token->data->user->id ) ) {
                    throw new \Exception( __( 'The User Secret does not match or has been revoked for this user', 'wp-graphql-jwt-authentication' ) );
                }
            }
            /**
             * If any exceptions are caught
             */
        } catch ( \Exception $error ) {
            self::set_status( 403 );
            return new \WP_Error( 'invalid_token', __( 'The JWT Token is invalid', 'wp-graphql-jwt-authentication' ) );
        }
        self::$is_refresh_token = false;
        return $token;
    }
    /**
     * Get the value of the Authorization header from the $_SERVER super global
     *
     * @return mixed|string
     */
    public static function get_auth_header() {
        /**
         * Looking for the HTTP_AUTHORIZATION header, if not present just
         * return the user.
         */
        $auth_header = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? $_SERVER['HTTP_AUTHORIZATION'] : false;
        /**
         * Double check for different auth header string (server dependent)
         */
        $redirect_auth_header = isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : false;
        /**
         * If the $auth header is set, use it. Otherwise attempt to use the $redirect_auth header
         */
        $auth_header = isset( $auth_header ) ? $auth_header : ( isset( $redirect_auth_header ) ? $redirect_auth_header : null );
        /**
         * Return the auth header, pass through a filter
         *
         * @param string $auth_header The header used to authenticate a user's HTTP request
         */
        return apply_filters( 'graphql_jwt_auth_get_auth_header', $auth_header );
    }
    public static function get_refresh_header() {
        /**
         * Check to see if the incoming request has a "Refresh-Authorization" header
         */
        $refresh_header = isset( $_SERVER['HTTP_REFRESH_AUTHORIZATION'] ) ? $_SERVER['HTTP_REFRESH_AUTHORIZATION'] : false;
        return apply_filters( 'graphql_jwt_auth_get_refresh_header', $refresh_header );
    }
}
- -
- - - - - - diff --git a/tests/_output/functional.remote.coverage/src/Login.php.html b/tests/_output/functional.remote.coverage/src/Login.php.html deleted file mode 100644 index d5c0e1a..0000000 --- a/tests/_output/functional.remote.coverage/src/Login.php.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - Code Coverage for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/Login.php - - - - - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
CRAP
-
- 100.00% covered (success) -
-
-
100.00%
19 / 19
WPGraphQL\JWT_Authentication\Login
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
5
-
- 100.00% covered (success) -
-
-
100.00%
19 / 19
 root_mutation_fields
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
 mutation
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
14 / 14
 anonymousFunction:57#354
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<?php
namespace WPGraphQL\JWT_Authentication;
use GraphQLRelay\Relay;
use WPGraphQL\Types;
class Login {
    private static $mutation;
    /**
     * Takes an array of fields from the RootMutation and returns the fields
     * with the "login" mutation field added
     *
     * @param array $fields The fields in the RootMutation of the Schema
     *
     * @return array $fields
     */
    public static function root_mutation_fields( $fields ) {
        $fields['login'] = self::mutation();
        return $fields;
    }
    protected static function mutation() {
        if ( empty( self::$mutation ) ) {
            self::$mutation = Relay::mutationWithClientMutationId([
                'name' => 'Login',
                'isPrivate' => false,
                'description' => __( 'Login a user. Request for an authToken and User details in response', 'wp-graphql-jwt-authentication' ),
                'inputFields' => [
                    'username' => [
                        'type' => Types::non_null( Types::string() ),
                        'description' => __( 'The username used for login. Typically a unique or email address depending on specific configuration', 'wp-graphql-jwt-authentication' ),
                    ],
                    'password' => [
                        'type' => Types::non_null( Types::string() ),
                        'description' => __( 'The plain-text password for the user logging in.', 'wp-graphql-jwt-authentication' ),
                    ],
                ],
                'outputFields' => [
                    'authToken' => [
                        'type' => Types::string(),
                        'description' => __( 'JWT Token that can be used in future requests for Authentication', 'wp-graphql-jwt-authentication' ),
                    ],
                    'refreshToken' => [
                        'type' => Types::string(),
                        'description' => __( 'A JWT token that can be used in future requests to get a refreshed jwtAuthToken. If the refresh token used in a request is revoked or otherwise invalid, a valid Auth token will NOT be issued in the response headers.', 'wp-graphql-jwt-authentication' ),
                    ],
                    'user' => [
                        'type' => Types::user(),
                        'description' => __( 'The user that was logged in', 'wp-graphql-jwt-authentication' ),
                    ],
                ],
                'mutateAndGetPayload' => function( $input ) {
                    /**
                     * Login the user in and get an authToken and user in response
                     */
                    return Auth::login_and_get_token( sanitize_user( $input['username'] ), trim( $input['password'] ) );
                },
            ]);
        }
        return ( ! empty( self::$mutation ) ) ? self::$mutation : null;
    }
}
- -
- - - - - - diff --git a/tests/_output/functional.remote.coverage/src/ManageTokens.php.html b/tests/_output/functional.remote.coverage/src/ManageTokens.php.html deleted file mode 100644 index bb3b9d7..0000000 --- a/tests/_output/functional.remote.coverage/src/ManageTokens.php.html +++ /dev/null @@ -1,680 +0,0 @@ - - - - - Code Coverage for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/ManageTokens.php - - - - - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 50.00% covered (danger) -
-
-
50.00%
6 / 12
CRAP
-
- 84.44% covered (warning) -
-
-
84.44%
76 / 90
WPGraphQL\JWT_Authentication\ManageTokens
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 50.00% covered (danger) -
-
-
50.00%
6 / 12
59.41
-
- 84.44% covered (warning) -
-
-
84.44%
76 / 90
 init
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
15 / 15
 add_user_fields
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
11
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
 anonymousFunction:69#230
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
4.13
-
- 80.00% covered (warning) -
-
-
80.00%
4 / 5
 anonymousFunction:90#371
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
4.13
-
- 80.00% covered (warning) -
-
-
80.00%
4 / 5
 anonymousFunction:111#512
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
3.07
-
- 80.00% covered (warning) -
-
-
80.00%
4 / 5
 anonymousFunction:135#647
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
 anonymousFunction:144#741
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
2
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
 add_user_mutation_input_fields
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
 update_jwt_fields_during_mutation
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
42
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 9
 prevent_token_from_returning_if_revoked
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2.06
-
- 75.00% covered (warning) -
-
-
75.00%
3 / 4
 use_custom_user_expiration
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
5.07
-
- 85.71% covered (warning) -
-
-
85.71%
6 / 7
 add_tokens_to_graphql_response_headers
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
9
-
- 100.00% covered (success) -
-
-
100.00%
11 / 11
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<?php
namespace WPGraphQL\JWT_Authentication;
use GraphQL\Error\UserError;
use WPGraphQL\Types;
class ManageTokens {
    /**
     * Initialize the funcionality for managing tokens
     */
    public static function init() {
        /**
         * Filter the User type to have a jwtUserSecret and jwtAuthToken field
         */
        add_filter( 'graphql_user_fields', [
            '\WPGraphQL\JWT_Authentication\ManageTokens',
            'add_user_fields'
        ] );
        /**
         * Add fields to the input for user mutations
         */
        add_filter( 'graphql_user_mutation_input_fields', [
            '\WPGraphQL\JWT_Authentication\ManageTokens',
            'add_user_mutation_input_fields'
        ] );
        add_action( 'graphql_user_object_mutation_update_additional_data', [
            '\WPGraphQL\JWT_Authentication\ManageTokens',
            'update_jwt_fields_during_mutation'
        ], 10, 3 );
        /**
         * Filter the signed token, preventing it from returning if the user has had their JWT Secret revoked
         */
        add_filter( 'graphql_jwt_auth_signed_token', [
            '\WPGraphQL\JWT_Authentication\ManageTokens',
            'prevent_token_from_returning_if_revoked'
        ], 10, 2 );
        /**
         * Filter the expiration to use the user's expiration if a custom expiration has been set.
         */
        add_filter( 'graphql_jwt_auth_expire', [
            '\WPGraphQL\JWT_Authentication\ManageTokens',
            'use_custom_user_expiration'
        ] );
        add_filter( 'graphql_response_headers_to_send', [
            '\WPGraphQL\JWT_Authentication\ManageTokens',
            'add_tokens_to_graphql_response_headers'
        ] );
    }
    /**
     * Filters the User type in the GraphQL Schema to provide fields for querying for user's jwtAuthToken and jwtUserSecret
     *
     * @param array $fields The fields for the User type in the GraphQL Schema
     *
     * @return array $fields
     */
    public static function add_user_fields( $fields ) {
        $fields['jwtAuthToken'] = [
            'type' => Types::string(),
            'description' => __( 'A JWT token that can be used in future requests for authentication/authorization', 'wp-graphql-jwt-authentication' ),
            'resolve' => function( \WP_User $user ) {
                /**
                 * Get the token for the user
                 */
                $token = Auth::get_token( $user );
                /**
                 * If the token cannot be returned, throw an error
                 */
                if ( empty( $token ) || is_wp_error( $token ) ) {
                    throw new UserError( __( 'The JWT token could not be returned', 'wp-graphql-jwt-authentication' ) );
                }
                return ! empty( $token ) ? $token : null;
            },
        ];
        $fields['jwtRefreshToken'] = [
            'type' => Types::string(),
            'description' => __( 'A JWT token that can be used in future requests to get a refreshed jwtAuthToken. If the refresh token used in a request is revoked or otherwise invalid, a valid Auth token will NOT be issued in the response headers.', 'wp-graphql-jwt-authentication' ),
            'resolve' => function( \WP_User $user ) {
                /**
                 * Get the token for the user
                 */
                $token = Auth::get_refresh_token( $user );
                /**
                 * If the token cannot be returned, throw an error
                 */
                if ( empty( $token ) || is_wp_error( $token ) ) {
                    throw new UserError( __( 'The JWT token could not be returned', 'wp-graphql-jwt-authentication' ) );
                }
                return ! empty( $token ) ? $token : null;
            },
        ];
        $fields['jwtUserSecret'] = [
            'type' => Types::string(),
            'description' => __( 'A unique secret tied to the users JWT token that can be revoked or refreshed. Revoking the secret prevents JWT tokens from being issued to the user. Refreshing the token invalidates previously issued tokens, but allows new tokens to be issued.', 'wp-graphql' ),
            'resolve' => function( \WP_User $user ) {
                /**
                 * Get the user's JWT Secret
                 */
                $secret = Auth::get_user_jwt_secret( $user->ID );
                /**
                 * If the secret cannot be returned, throw an error
                 */
                if ( is_wp_error( $secret ) ) {
                    throw new UserError( __( 'The user secret could not be returned', 'wp-graphql-jwt-authentication' ) );
                }
                /**
                 * Return the secret
                 */
                return ! empty( $secret ) ? $secret : null;
            }
        ];
        $fields['jwtAuthExpiration'] = [
            'type' => Types::string(),
            'description' => __( 'The expiration for the JWT Token for the user. If not set custom for the user, it will use the default sitewide expiration setting', 'wp-graphql-jwt-authentication' ),
            'resolve' => function( \WP_User $user ) {
                $expiration = Auth::get_token_expiration();
                return ! empty( $expiration ) ? $expiration : null;
            }
        ];
        $fields['isJwtAuthSecretRevoked'] = [
            'type' => Types::non_null( Types::boolean() ),
            'description' => __( 'Whether the JWT User secret has been revoked. If the secret has been revoked, auth tokens will not be issued until an admin, or user with proper capabilities re-issues a secret for the user.', 'wp-graphql-jwt-authentication' ),
            'resolve' => function( \WP_User $user ) {
                $revoked = Auth::is_jwt_secret_revoked( $user->ID );
                return true == $revoked ? true : false;
            }
        ];
        return $fields;
    }
    /**
     * Given an array of fields, this returns an array with the new fields added
     * @param array $fields The input fields for user mutations
     *
     * @return array
     */
    public static function add_user_mutation_input_fields( array $fields ) {
        $fields['revokeJwtUserSecret'] = [
            'type' => Types::boolean(),
            'description' => __( 'If true, this will revoke the users JWT secret. If false, this will unrevoke the JWT secret AND issue a new one. To revoke, the user must have proper capabilities to edit users JWT secrets.', 'wp-graphql-jwt-authentication' ),
        ];
        $fields['refreshJwtUserSecret'] = [
            'type' => Types::boolean(),
            'description' => __( 'If true, this will refresh the users JWT secret.' ),
        ];
        return $fields;
    }
    /**
     * @param int $user_id The ID of the user being mutated
     * @param array $input The input args of the GraphQL mutation request
     * @param string $mutation_name The name of the mutation
     */
    public static function update_jwt_fields_during_mutation( $user_id, array $input, $mutation_name ) {
        /**
         * If there was input to revokeJwtUserSecret, check the value for true or false, and
         * revoke or unRevoke the token accordingly
         */
        if ( isset( $input['revokeJwtUserSecret'] ) ) {
            if ( true === $input['revokeJwtUserSecret'] ) {
                Auth::revoke_user_secret( $user_id );
            } elseif ( false === $input['revokeJwtUserSecret'] ) {
                Auth::unrevoke_user_secret( $user_id );
            }
        }
        /**
         * If refreshJwtUserSecret is true.
         */
        if ( isset( $input['refreshJwtUserSecret'] ) ) {
            if ( true === $input['refreshJwtUserSecret'] ) {
                Auth::issue_new_user_secret( $user_id );
            }
        }
    }
    /**
     * This filters the token to prevent it from being issued if it has been revoked.
     *
     * @param string $token
     * @param int $user_id
     *
     * @return string $token
     */
    public static function prevent_token_from_returning_if_revoked( $token, $user_id  ) {
        /**
         * Check to see if the user's auth secret has been revoked.
         */
        $revoked = Auth::is_jwt_secret_revoked( $user_id );
        /**
         * If the token has been revoked, prevent it from being returned
         */
        if ( true === $revoked ) {
            throw new UserError( __( 'The JWT token cannot be issued for this user', 'wp-graphql-jwt-authentication' ) );
        }
        return $token;
    }
    public static function use_custom_user_expiration( $expiration ) {
        $user = wp_get_current_user();
        /**
         * If there is no current user set or the current user's secret has been revoked, return null
         */
        if ( 0 === $user->ID || Auth::is_jwt_secret_revoked( $user->ID ) ) {
            return null;
        }
        /**
         * If the user has custom expiration configured, use it
         */
        $user_expiration = get_user_meta( $user->ID, 'graphql_jwt_custom_expiration', true );
        if ( ! empty( $user_expiration ) && is_string( $user_expiration ) ) {
            $expiration = $user_expiration;
        }
        return $expiration;
    }
    public static function add_tokens_to_graphql_response_headers( $headers ) {
        /**
         * If there's a Refresh-Authorization token in the request headers, validate it
         */
        $validate_refresh_header = Auth::validate_token( Auth::get_refresh_header(), true );
        /**
         * If the refresh token in the request headers is valid, return a JWT Auth token that can be used for future requests
         */
        if ( ! is_wp_error( $validate_refresh_header ) && ! empty( $validate_refresh_header->data->user->id ) ) {
            /**
             * Get an auth token and refresh token to return
             */
            $auth_token = Auth::get_token( new \WP_User( $validate_refresh_header->data->user->id ), false );
            /**
             * If the tokens can be generated (not revoked, etc), return them
             */
            if ( ! empty( $auth_token ) && ! is_wp_error( $auth_token ) ) {
                $headers['X-JWT-Auth'] = $auth_token;
            }
        }
        $validate_auth_header = Auth::validate_token( null, false );
        if ( ! is_wp_error( $validate_auth_header ) && ! empty( $validate_auth_header->data->user->id ) ) {
            $refresh_token = Auth::get_refresh_token( new \WP_User( $validate_auth_header->data->user->id ), false );
            if ( ! empty( $refresh_token ) && ! is_wp_error( $refresh_token ) ) {
                $headers['X-JWT-Refresh'] = $refresh_token;
            }
        }
        return $headers;
    }
}
- -
- - - - - - diff --git a/tests/_output/functional.remote.coverage/src/RefreshToken.php.html b/tests/_output/functional.remote.coverage/src/RefreshToken.php.html deleted file mode 100644 index be62528..0000000 --- a/tests/_output/functional.remote.coverage/src/RefreshToken.php.html +++ /dev/null @@ -1,263 +0,0 @@ - - - - - Code Coverage for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src/RefreshToken.php - - - - - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
CRAP
-
- 94.74% covered (success) -
-
-
94.74%
18 / 19
WPGraphQL\JWT_Authentication\RefreshToken
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
13.02
-
- 94.74% covered (success) -
-
-
94.74%
18 / 19
 root_mutation_fields
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
2 / 2
 mutation
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
7
-
- 100.00% covered (success) -
-
-
100.00%
8 / 8
 anonymousFunction:46#251
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
5.05
-
- 87.50% covered (warning) -
-
-
87.50%
7 / 8
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<?php
namespace WPGraphQL\JWT_Authentication;
use GraphQL\Error\UserError;
use GraphQLRelay\Relay;
use WPGraphQL\Types;
class RefreshToken {
    private static $mutation;
    /**
     * Takes an array of fields from the RootMutation and returns the fields
     * with the "login" mutation field added
     *
     * @param array $fields The fields in the RootMutation of the Schema
     *
     * @return array $fields
     */
    public static function root_mutation_fields( $fields ) {
        $fields['refreshJwtAuthToken'] = self::mutation();
        return $fields;
    }
    public static function mutation() {
        if ( empty( self::$mutation ) ) {
            self::$mutation = Relay::mutationWithClientMutationId([
                'name' => 'RefreshJwtAuthToken',
                'isPrivate' => false,
                'description' => __( 'Use a valid JWT Refresh token to retrieve a new JWT Auth Token', 'wp-graphql-jwt-authentication' ),
                'inputFields' => [
                    'jwtRefreshToken' => [
                        'type' => Types::non_null( Types::string() ),
                        'description' => __( 'A valid, previously issued JWT refresh token. If valid a new Auth token will be provided. If invalid, expired, revoked or otherwise invalid, a new AuthToken will not be provided.', 'wp-graphql-jwt-authentication' ),
                    ],
                ],
                'outputFields' => [
                    'authToken' => [
                        'type' => Types::string(),
                        'description' => __( 'JWT Token that can be used in future requests for Authentication', 'wp-graphql-jwt-authentication' ),
                    ],
                ],
                'mutateAndGetPayload' => function( $input ) {
                    $refresh_token = ! empty( $input['jwtRefreshToken'] ) ? Auth::validate_token( $input['jwtRefreshToken'] ) : null;
                    $id = isset( $refresh_token->data->user->id ) || 0 === $refresh_token->data->user->id ? absint( $refresh_token->data->user->id ) : 0;
                    if ( empty( $id ) ) {
                        throw new UserError( __( 'The provided refresh token is invalid', 'wp-graphql-jwt-authentication' ) );
                    }
                    $user = new \WP_User( $id );
                    $auth_token = Auth::get_token( $user, false );
                    return [
                        'authToken' => $auth_token,
                    ];
                },
            ]);
        }
        return ( ! empty( self::$mutation ) ) ? self::$mutation : null;
    }
}
- -
- - - - - - diff --git a/tests/_output/functional.remote.coverage/src/dashboard.html b/tests/_output/functional.remote.coverage/src/dashboard.html deleted file mode 100644 index 3d4cd16..0000000 --- a/tests/_output/functional.remote.coverage/src/dashboard.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - Dashboard for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src - - - - - - - -
- -
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

- -
-
-

Project Risks

- -
-
-
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
- - -
- - - - - - - - diff --git a/tests/_output/functional.remote.coverage/src/index.html b/tests/_output/functional.remote.coverage/src/index.html deleted file mode 100644 index ca6cf28..0000000 --- a/tests/_output/functional.remote.coverage/src/index.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - Code Coverage for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/src - - - - - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
-
- 82.99% covered (warning) -
-
-
82.99%
200 / 241
-
- 59.46% covered (warning) -
-
-
59.46%
22 / 37
-
- 25.00% covered (danger) -
-
-
25.00%
1 / 4
Auth.php
-
- 76.99% covered (warning) -
-
-
76.99%
87 / 113
-
- 57.89% covered (warning) -
-
-
57.89%
11 / 19
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
Login.php
-
- 100.00% covered (success) -
-
-
100.00%
19 / 19
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
ManageTokens.php
-
- 84.44% covered (warning) -
-
-
84.44%
76 / 90
-
- 50.00% covered (danger) -
-
-
50.00%
6 / 12
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
RefreshToken.php
-
- 94.74% covered (success) -
-
-
94.74%
18 / 19
-
- 66.67% covered (warning) -
-
-
66.67%
2 / 3
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
- -
- - - - - diff --git a/tests/_output/functional.remote.coverage/wp-graphql-jwt-authentication.php.html b/tests/_output/functional.remote.coverage/wp-graphql-jwt-authentication.php.html deleted file mode 100644 index bd897aa..0000000 --- a/tests/_output/functional.remote.coverage/wp-graphql-jwt-authentication.php.html +++ /dev/null @@ -1,473 +0,0 @@ - - - - - Code Coverage for /Users/jasonbahl/Sites/wordpress/wp-graphql-jwt-authentication/wp-graphql-jwt-authentication.php - - - - - - -
-
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 71.43% covered (warning) -
-
-
71.43%
5 / 7
CRAP
-
- 89.47% covered (warning) -
-
-
89.47%
34 / 38
init
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
WPGraphQL\JWT_Authentication\JWT_Authentication
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
-
- 66.67% covered (warning) -
-
-
66.67%
4 / 6
15.31
-
- 88.89% covered (warning) -
-
-
88.89%
32 / 36
 instance
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
7 / 7
 __clone
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
 __wakeup
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 1
2
-
- 0.00% covered (danger) -
-
-
0.00%
0 / 2
 setup_constants
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
6
-
- 100.00% covered (success) -
-
-
100.00%
11 / 11
 includes
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
3
-
- 100.00% covered (success) -
-
-
100.00%
3 / 3
 init
-
- 100.00% covered (success) -
-
-
100.00%
1 / 1
1
-
- 100.00% covered (success) -
-
-
100.00%
11 / 11
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<?php
/**
 * Plugin Name: WPGraphQL JWT Authentication
 * Plugin URI: https://www.wpgraphql.com
 * Description: JWT Authentication for WPGraphQL
 * Author: WPGraphQL, Jason Bahl
 * Author URI: https://www.wpgraphql.com
 * Text Domain: wp-graphql-jwt-authentication-jwt-authentication
 * Domain Path: /languages
 * Version: 0.3.0
 * Requires at least: 4.7.0
 * Tested up to: 4.8.3
 * Requires PHP: 5.5
 * License: GPL-3
 * License URI: https://www.gnu.org/licenses/gpl-3.0.html
 *
 * @package         WPGraphQL_JWT_Authentication
 */
namespace WPGraphQL\JWT_Authentication;
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
    die;
}
if ( file_exists( __DIR__ . '/c3.php' ) ) {
    require_once( 'c3.php' );
}
if ( ! class_exists( '\WPGraphQL\JWT_Authentication' ) ) :
    final class JWT_Authentication {
        /**
         * Stores the instance of the JWT_Authentication class
         *
         * @var JWT_Authentication The one true JWT_Authentication
         * @since  0.0.1
         * @access private
         */
        private static $instance;
        /**
         * The instance of the JWT_Authentication object
         *
         * @return object|JWT_Authentication - The one true JWT_Authentication
         * @since  0.0.1
         * @access public
         */
        public static function instance() {
            if ( ! isset( self::$instance ) && ! ( self::$instance instanceof JWT_Authentication ) ) {
                self::$instance = new JWT_Authentication;
                self::$instance->setup_constants();
                self::$instance->includes();
            }
            self::$instance->init();
            /**
             * Fire off init action
             *
             * @param JWT_Authentication $instance The instance of the Init_JWT_Authentication class
             */
            do_action( 'graphql_jwt_authentication_init', self::$instance );
            /**
             * Return the Init_JWT_Authentication Instance
             */
            return self::$instance;
        }
        /**
         * Throw error on object clone.
         * The whole idea of the singleton design pattern is that there is a single object
         * therefore, we don't want the object to be cloned.
         *
         * @since  0.0.1
         * @access public
         * @return void
         */
        public function __clone() {
            // Cloning instances of the class is forbidden.
            _doing_it_wrong( __FUNCTION__, esc_html__( 'The Init_JWT_Authentication class should not be cloned.', 'wp-graphql-jwt-authentication' ), '0.0.1' );
        }
        /**
         * Disable unserializing of the class.
         *
         * @since  0.0.1
         * @access protected
         * @return void
         */
        public function __wakeup() {
            // De-serializing instances of the class is forbidden.
            _doing_it_wrong( __FUNCTION__, esc_html__( 'De-serializing instances of the WPGraphQL class is not allowed', 'wp-graphql-jwt-authentication' ), '0.0.1' );
        }
        /**
         * Setup plugin constants.
         *
         * @access private
         * @since  0.0.1
         * @return void
         */
        private function setup_constants() {
            // Plugin version.
            if ( ! defined( 'WPGRAPHQL_JWT_AUTHENTICATION_VERSION' ) ) {
                define( 'WPGRAPHQL_JWT_AUTHENTICATION_VERSION', '0.3.0' );
            }
            // Plugin Folder Path.
            if ( ! defined( 'WPGRAPHQL_JWT_AUTHENTICATION_PLUGIN_DIR' ) ) {
                define( 'WPGRAPHQL_JWT_AUTHENTICATION_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
            }
            // Plugin Folder URL.
            if ( ! defined( 'WPGRAPHQL_JWT_AUTHENTICATION_PLUGIN_URL' ) ) {
                define( 'WPGRAPHQL_JWT_AUTHENTICATION_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
            }
            // Plugin Root File.
            if ( ! defined( 'WPGRAPHQL_JWT_AUTHENTICATION_PLUGIN_FILE' ) ) {
                define( 'WPGRAPHQL_JWT_AUTHENTICATION_PLUGIN_FILE', __FILE__ );
            }
            // Whether to autoload the files or not
            if ( ! defined( 'WPGRAPHQL_JWT_AUTHENTICATION_AUTOLOAD' ) ) {
                define( 'WPGRAPHQL_JWT_AUTHENTICATION_AUTOLOAD', true );
            }
        }
        /**
         * Include required files.
         * Uses composer's autoload
         *
         * @access private
         * @since  0.0.1
         * @return void
         */
        private function includes() {
            // Autoload Required Classes
            if ( defined( 'WPGRAPHQL_JWT_AUTHENTICATION_AUTOLOAD' ) && true == WPGRAPHQL_JWT_AUTHENTICATION_AUTOLOAD ) {
                require_once( WPGRAPHQL_JWT_AUTHENTICATION_PLUGIN_DIR . 'vendor/autoload.php' );
            }
        }
        /**
         * Initialize the plugin
         */
        private static function init() {
            /**
             * Initialize the GraphQL fields for managing tokens
             */
            ManageTokens::init();
            /**
             * Filter the rootMutation fields
             */
            add_filter( 'graphql_rootMutation_fields', [
                '\WPGraphQL\JWT_Authentication\Login',
                'root_mutation_fields'
            ], 10, 1 );
            add_filter( 'graphql_rootMutation_fields', [
                '\WPGraphQL\JWT_Authentication\RefreshToken',
                'root_mutation_fields'
            ], 10, 1 );
            /**
             * Filter how WordPress determines the current user
             */
            add_filter( 'determine_current_user', [
                '\WPGraphQL\JWT_Authentication\Auth',
                'filter_determine_current_user'
            ], 10 );
        }
    }
endif;
function init() {
    return JWT_Authentication::instance();
}
add_action( 'graphql_init', '\WPGraphQL\JWT_Authentication\init' );
- -
- - - - - - diff --git a/tests/functional.suite.dist.yml b/tests/functional.suite.dist.yml index 97b6d0c..f856bac 100644 --- a/tests/functional.suite.dist.yml +++ b/tests/functional.suite.dist.yml @@ -5,10 +5,12 @@ actor: FunctionalTester modules: - enabled: - - \Helper\Functional - - REST - - WPDb - - WPBrowser - - Asserts - - WPLoader + enabled: + - Asserts + - REST + - WPBrowser + - WPDb + - WPLoader + config: + WPDb: + cleanup: false diff --git a/tests/wpunit.suite.dist.yml b/tests/wpunit.suite.dist.yml index 436e3a3..32632f7 100644 --- a/tests/wpunit.suite.dist.yml +++ b/tests/wpunit.suite.dist.yml @@ -1,9 +1,26 @@ # Codeception Test Suite Configuration # # Suite for unit or integration tests that require WordPress functions and classes. - actor: WpunitTester modules: - enabled: - - WPLoader - - \Helper\Wpunit + enabled: + - WPLoader + disabled: + - WPDb + - WPBrowser + config: + WPDb: + cleanup: false + WPLoader: + plugins: + - wp-graphql-acf/tests/_bootstrap/bootstrap.php + - wp-graphql/wp-graphql.php + - advanced-custom-fields-pro/acf.php + - wp-graphql-acf/wp-graphql-acf.php + activatePlugins: + - wp-graphql-acf/tests/_bootstrap/bootstrap.php + - wp-graphql/wp-graphql.php + - advanced-custom-fields-pro/acf.php + - wp-graphql-acf/wp-graphql-acf.php + configFile: + - 'tests/_data/config.php' diff --git a/tests/wpunit/CommentLocationTest.php b/tests/wpunit/CommentLocationTest.php new file mode 100644 index 0000000..254097c --- /dev/null +++ b/tests/wpunit/CommentLocationTest.php @@ -0,0 +1,166 @@ +group_key = __CLASS__; + WPGraphQL::clear_schema(); + $this->register_acf_field_group(); + + $this->post_id = $this->factory()->post->create( [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_title' => 'Test', + 'post_content' => 'test', + ] ); + + $this->tag_id = $this->factory()->term->create( [ + 'taxonomy' => 'post_tag', + ] ); + + $this->comment_id = $this->factory()->comment->create([ + 'comment_content' => 'test comment', + 'comment_author' => 'Test Author', + 'comment_approved' => true, + 'comment_post_ID' => $this->post_id, + ]); + + $this->test_image = dirname( __FILE__, 2 ) . '/_data/images/test.png'; + + } + + public function tearDown(): void { + acf_remove_local_field_group( $this->group_key ); + wp_delete_post( $this->post_id, true ); + WPGraphQL::clear_schema(); + parent::tearDown(); // TODO: Change the autogenerated stub + } + + public function register_acf_field_group( $config = [] ) { + + $defaults = [ + 'key' => $this->group_key, + 'title' => 'Comment Fields', + 'fields' => [], + 'location' => [ + [ + [ + 'param' => 'comment', + 'operator' => '==', + 'value' => 'all', + ], + ], + ], + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => '', + 'active' => true, + 'description' => '', + 'show_in_graphql' => 1, + 'graphql_field_name' => 'commentTestFields', + 'graphql_types' => [ 'Comment' ] + ]; + + acf_add_local_field_group( array_merge( $defaults, $config ) ); + + + } + + public function register_acf_field( $config = [] ) { + + $defaults = [ + 'parent' => $this->group_key, + 'key' => 'field_5d7812fd123', + 'label' => 'Text', + 'name' => 'text', + 'type' => 'text', + 'instructions' => '', + 'required' => 0, + 'conditional_logic' => 0, + 'wrapper' => array( + 'width' => '', + 'class' => '', + 'id' => '', + ), + 'show_in_graphql' => 1, + 'default_value' => '', + 'placeholder' => '', + 'prepend' => '', + 'append' => '', + 'maxlength' => '', + ]; + + acf_add_local_field( array_merge( $defaults, $config ) ); + } + + /** + * @throws Exception + */ + public function testBasicQuery() { + $query = '{ comments { nodes { id } } }'; + $actual = graphql( [ 'query' => $query ] ); + $this->assertArrayNotHasKey( 'errors', $actual ); + } + + public function testAcfTextField() { + + // There was a bug in some versions of WPGraphQL where databaseId wasn't exposed on comments + // this is a polyfill as we're not really interested in testing comments, just the ACF fields on comments + add_filter( 'graphql_allowed_fields_on_restricted_type', function( $fields, $model_name ) { + + if ( 'CommentObject' === $model_name && ! isset( $fields['databaseId' ] ) ) { + $fields[] = 'databaseId'; + } + + return $fields; + }, 10, 2 ); + + $this->register_acf_field([ + 'name' => 'comment_text_field_test', + 'type' => 'text', + ]); + + $expected_text = 'Some Text'; + + update_field( 'comment_text_field_test', $expected_text, 'comment_' . $this->comment_id ); + $field = get_field( 'comment_text_field_test', 'comment_' . $this->comment_id, true ); + codecept_debug( [ 'field_value', $field, 'comment_' . $this->comment_id ] ); + + $query = ' + query getComment( $id: ID! ) { + comment( id: $id ) { + __typename + commentTestFields { + fieldGroupName + commentTextFieldTest + } + } + } + '; + + $actual = graphql([ + 'query' => $query, + 'variables' => [ + 'id' => \GraphQLRelay\Relay::toGlobalId( 'comment', $this->comment_id ), + ], + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + $this->assertSame( $expected_text, $actual['data']['comment']['commentTestFields']['commentTextFieldTest'] ); + + } + +} diff --git a/tests/wpunit/ExplicitOptionsTest.php b/tests/wpunit/ExplicitOptionsTest.php new file mode 100644 index 0000000..4ddfc78 --- /dev/null +++ b/tests/wpunit/ExplicitOptionsTest.php @@ -0,0 +1,251 @@ +group_key = __CLASS__; + WPGraphQL::clear_schema(); + parent::setUp(); + } + + public function tearDown(): void { + acf_remove_local_field_group( $this->group_key ); + WPGraphQL::clear_schema(); + parent::tearDown(); + } + + public function register_acf_field_group( $config = [] ) { + + $defaults = [ + 'key' => $this->group_key, + 'title' => 'Post Object Fields', + 'fields' => [], + 'location' => [ + [ + [ + 'param' => 'post_type', + 'operator' => '==', + 'value' => 'post', + ], + ], + ], + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => '', + 'active' => true, + 'description' => '', + 'show_in_graphql' => 1, + 'graphql_field_name' => 'postFields', + 'graphql_types' => [ 'Post' ] + ]; + + acf_add_local_field_group( array_merge( $defaults, $config ) ); + + } + + public function register_acf_field( $config = [] ) { + + $defaults = [ + 'parent' => 'group_key', + 'key' => 'field_5d7812fd000a4', + 'label' => 'Text', + 'name' => 'text', + 'type' => 'text', + 'instructions' => '', + 'required' => 0, + 'conditional_logic' => 0, + 'wrapper' => array( + 'width' => '', + 'class' => '', + 'id' => '', + ), + 'show_in_graphql' => 1, + 'default_value' => '', + 'placeholder' => '', + 'prepend' => '', + 'append' => '', + 'maxlength' => '', + ]; + + acf_add_local_field( array_merge( $defaults, $config ) ); + } + + /** + * Test Explicit Options + * Register an acf group field and enable it for Post and CPT, but disable for Page + * Check if the acf field is queriable for each post types + */ + public function testExplicitOptions() { + + $cpt_name = 'acf_cpt'; + // register a custom post type. + register_post_type( + $cpt_name, + [ + 'show_in_graphql' => true, + 'graphql_single_name' => 'acfCpt', + 'graphql_plural_name' => 'acfCpts', + ] + ); + + $group_key = 'group_for_page_and_cpt'; + $group_graphql_name = 'pageCptFields'; + + // register a field group for Post and custom post type $cpt_name. + $this->register_acf_field_group( + [ + 'key' => $group_key, + 'graphql_field_name' => $group_graphql_name, + 'graphql_types' => [ 'Post', 'acfCpt' ], + ] + ); + + $this->register_acf_field( + [ + 'parent' => $group_key, + 'type' => 'text', + 'name' => 'acf_text_field', + ] + ); + + // create a test post. + $post_id = $this->factory()->post->create( + [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_title' => 'Test Post', + 'post_content' => 'test post', + ] + ); + + $expected_text_1 = 'test value1'; + update_field( 'acf_text_field', $expected_text_1, $post_id ); + + // create a test page. + $page_id = $this->factory()->post->create( + [ + 'post_type' => 'page', + 'post_status' => 'publish', + 'post_title' => 'Test Page', + 'post_content' => 'test page', + ] + ); + + $expected_text_2 = 'test value2'; + update_field( 'acf_text_field', $expected_text_2, $page_id ); + + // create a custom post type post. + $cpt_id = $this->factory()->post->create( + [ + 'post_type' => $cpt_name, + 'post_status' => 'publish', + 'post_title' => 'Test Post', + 'post_content' => 'test post', + ] + ); + + $expected_text_3 = 'test value2'; + update_field( 'acf_text_field', $expected_text_3, $cpt_id ); + + // post assert validation. + $query = sprintf( + ' + query getPostById( $postId: Int ) { + postBy( postId: $postId ) { + id + %s { + fieldGroupName + %s + } + } + } + ', + $group_graphql_name, + 'acfTextField' + ); + + $actual = graphql( + [ + 'query' => $query, + 'variables' => [ + 'postId' => $post_id, + ], + ] + ); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + $this->assertSame( $expected_text_1, $actual['data']['postBy'][ $group_graphql_name ][ 'acfTextField' ] ); + + // page assert validation. it must return errors + $query = sprintf( + ' + query getPageById( $pageId: Int ) { + pageBy( pageId: $pageId ) { + id + %s { + fieldGroupName + %s + } + } + } + ', + $group_graphql_name, + 'acfTextField' + ); + + $actual = graphql( + [ + 'query' => $query, + 'variables' => [ + 'pageId' => $page_id, + ], + ] + ); + + codecept_debug( $actual ); + + $this->assertArrayHasKey( 'errors', $actual ); + $this->assertSame( 'Cannot query field "pageCptFields" on type "Page".', $actual['errors'][0]['message'] ); + + // custom post type assert validation + $query = sprintf( + ' + query getAcfCptById( $acfCptId: Int ) { + acfCptBy( acfCptId: $acfCptId ) { + id + %s { + fieldGroupName + %s + } + } + } + ', + $group_graphql_name, + 'acfTextField' + ); + + $actual = graphql( + [ + 'query' => $query, + 'variables' => [ + 'acfCptId' => $cpt_id, + ], + ] + ); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + $this->assertSame( $expected_text_3, $actual['data']['acfCptBy'][ $group_graphql_name ][ 'acfTextField' ] ); + + acf_remove_local_field_group( $group_key ); + + } + +} diff --git a/tests/wpunit/LocationRulesTest.php b/tests/wpunit/LocationRulesTest.php new file mode 100644 index 0000000..0578cde --- /dev/null +++ b/tests/wpunit/LocationRulesTest.php @@ -0,0 +1,674 @@ +group_key = __CLASS__; + WPGraphQL::clear_schema(); + + $this->post_id = $this->factory()->post->create( [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_title' => 'Test', + 'post_content' => 'test', + ] ); + + $this->tag_id = $this->factory()->term->create( [ + 'taxonomy' => 'post_tag', + ] ); + + $this->test_image = dirname( __FILE__, 2 ) . '/_data/images/test.png'; + + } + + public function tearDown(): void { + wp_delete_post( $this->post_id, true ); + WPGraphQL::clear_schema(); + parent::tearDown(); // TODO: Change the autogenerated stub + } + + public function register_acf_field_group( $config = [] ) { + + $defaults = [ + 'key' => $this->group_key, + 'title' => 'Test Fields', + 'fields' => [], + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => '', + 'active' => true, + 'description' => '', + 'show_in_graphql' => 1, + 'graphql_field_name' => 'testFields', + ]; + + $field_group = array_merge( $defaults, $config ); + + codecept_debug( $field_group ); + + acf_add_local_field_group( $field_group ); + + + } + + public function testFieldGroupAssignedToPostTypeWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'location' => [ + [ + [ + 'param' => 'post_type', + 'operator' => '==', + 'value' => 'post', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'postFieldsTest', + ]); + + + $query = ' + { + posts { + nodes { + id + title + postFieldsTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + acf_remove_local_field_group( $this->group_key ); + + } + + public function testFieldGroupAssignedToTagWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'tagFieldsTest', + 'location' => [ + [ + [ + 'param' => 'taxonomy', + 'operator' => '==', + 'value' => 'post_tag', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'tagFieldsTest', + ]); + + + $query = ' + { + tags { + nodes { + id + name + tagFieldsTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'tagFieldsTest' ); + + } + + public function testFieldGroupAssignedToCategoryWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'categoryFieldTest', + 'location' => [ + [ + [ + 'param' => 'taxonomy', + 'operator' => '==', + 'value' => 'category', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'categoryFieldTest', + ]); + + + $query = ' + { + categories { + nodes { + id + name + categoryFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'tagFieldsTest' ); + + } + + public function testFieldGroupAssignedToCommentsWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'commentFieldTest', + 'location' => [ + [ + [ + 'param' => 'comment', + 'operator' => '==', + 'value' => 'all', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'commentFieldTest', + ]); + + + $query = ' + { + comments { + nodes { + id + commentFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'commentFieldTest' ); + + } + + public function testFieldGroupAssignedToMenusWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'menuFieldTest', + 'location' => [ + [ + [ + 'param' => 'nav_menu', + 'operator' => '==', + 'value' => 'all', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'menuFieldTest', + ]); + + + $query = ' + { + menus { + nodes { + id + menuFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + } + + public function testFieldGroupAssignedToMenuItemsWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'menuItemFieldTest', + 'location' => [ + [ + [ + 'param' => 'nav_menu_item', + 'operator' => '==', + 'value' => 'all', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'menuItemFieldTest', + ]); + + + $query = ' + { + menuItems { + nodes { + id + menuItemFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual, 'If this fails, see https://github.com/wp-graphql/wp-graphql/issues/1844' ); + + acf_remove_local_field_group( 'menuItemFieldTest' ); + + } + + public function testFieldGroupAssignedToMediaWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'mediaItemFieldTest', + 'location' => [ + [ + [ + 'param' => 'attachment', + 'operator' => '==', + 'value' => 'all', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'mediaItemFieldTest', + ]); + + + $query = ' + { + mediaItems { + nodes { + id + mediaItemFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'mediaItemFieldTest' ); + + } + + public function testFieldGroupAssignedToIndividualPostWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'singlePostFieldTest', + 'location' => [ + [ + [ + 'param' => 'post', + 'operator' => '==', + 'value' => $this->post_id, + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'singlePostFieldTest', + ]); + + + $query = ' + { + posts { + nodes { + id + singlePostFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + $query = ' + { + comments { + nodes { + id + singlePostFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'singlePostFieldTest' ); + + } + + public function testFieldGroupAssignedToUserEditWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'userEditFieldTest', + 'location' => [ + [ + [ + 'param' => 'user_form', + 'operator' => '==', + 'value' => 'edit', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'userEditFieldTest', + ]); + + + $query = ' + { + users { + nodes { + id + userEditFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + $query = ' + { + comments { + nodes { + id + userEditFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'userEditFieldTest' ); + + } + + public function testFieldGroupAssignedToUserRegisterWithoutGraphqlTypesFieldShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'userRegisterFieldTest', + 'location' => [ + [ + [ + 'param' => 'user_form', + 'operator' => '==', + 'value' => 'register', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'userRegisterFieldTest', + ]); + + + $query = ' + { + users { + nodes { + id + userRegisterFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + $query = ' + { + comments { + nodes { + id + userRegisterFieldTest { + fieldGroupName + } + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'userRegisterFieldTest' ); + + } + + public function testFieldGroupAssignedToAcfOptionsPageShowsInSchema() { + + /** + * Register a field group to a specific post type + */ + $this->register_acf_field_group([ + 'key' => 'settingsFieldsTest', + 'location' => [ + [ + [ + 'param' => 'options_page', + 'operator' => '==', + 'value' => 'theme-general-settings', + ], + ], + [ + [ + 'param' => 'options_page', + 'operator' => '==', + 'value' => 'theme-footer-settings', + ], + ], + ], + 'show_in_graphql' => 1, + 'graphql_field_name' => 'settingsFieldsTest', + ]); + + acf_add_options_page(array( + 'page_title' => 'Theme General Settings', + 'menu_title' => 'Theme Settings', + 'menu_slug' => 'theme-general-settings', + 'capability' => 'edit_posts', + 'redirect' => false, + 'show_in_graphql' => true, + 'graphql_field_name' => 'ThemeGeneralSettings', + )); + + acf_add_options_sub_page(array( + 'page_title' => 'Theme Header Settings', + 'menu_title' => 'Header', + 'parent_slug' => 'theme-general-settings', + 'menu_slug' => 'theme-header-settings', + 'show_in_graphql' => true, + 'graphql_field_name' => 'ThemeHeaderSettings', + )); + + acf_add_options_sub_page(array( + 'page_title' => 'Theme Footer Settings', + 'menu_title' => 'Footer', + 'parent_slug' => 'theme-general-settings', + 'menu_slug' => 'theme-footer-settings', + 'show_in_graphql' => true, + 'graphql_field_name' => 'ThemeFooterSettings', + )); + + + $query = ' + { + themeGeneralSettings { + settingsFieldsTest { + __typename + } + } + themeFooterSettings { + settingsFieldsTest { + __typename + } + } + } + '; + + $actual = graphql([ + 'query' => $query + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + + acf_remove_local_field_group( 'settingsFieldsTest' ); + + } + +} diff --git a/tests/wpunit/MenuItemLocationTest.php b/tests/wpunit/MenuItemLocationTest.php new file mode 100644 index 0000000..3045775 --- /dev/null +++ b/tests/wpunit/MenuItemLocationTest.php @@ -0,0 +1,178 @@ +group_key = __CLASS__; + WPGraphQL::clear_schema(); + $this->register_acf_field_group(); + + $this->post_id = $this->factory()->post->create( [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_title' => 'Test', + 'post_content' => 'test', + ] ); + + $this->tag_id = $this->factory()->term->create( [ + 'taxonomy' => 'post_tag', + ] ); + + $this->comment_id = $this->factory()->comment->create([ + 'comment_content' => 'test comment', + 'comment_author' => 'Test Author', + 'comment_approved' => true, + 'comment_post_ID' => $this->post_id, + ]); + + $location_name = 'test-location'; + add_theme_support( 'nav_menus' ); + register_nav_menu( $location_name, 'test menu...' ); + $menu_slug = 'my-test-menu'; + $menu_id = wp_create_nav_menu( $menu_slug ); + $post_id = $this->factory()->post->create(); + + $this->menu_item_id = wp_update_nav_menu_item( + $menu_id, + 0, + [ + 'menu-item-title' => 'Menu item', + 'menu-item-object' => 'post', + 'menu-item-object-id' => $post_id, + 'menu-item-status' => 'publish', + 'menu-item-type' => 'post_type', + ] + ); + set_theme_mod( 'nav_menu_locations', [ $location_name => $menu_id ] ); + + $this->test_image = dirname( __FILE__, 2 ) . '/_data/images/test.png'; + + } + + public function tearDown(): void { + acf_remove_local_field_group( $this->group_key ); + wp_delete_post( $this->post_id, true ); + WPGraphQL::clear_schema(); + parent::tearDown(); // TODO: Change the autogenerated stub + } + + public function register_acf_field_group( $config = [] ) { + + $defaults = [ + 'key' => $this->group_key, + 'title' => 'Menu Item Fields', + 'fields' => [], + 'location' => [ + [ + [ + 'param' => 'nav_menu_item', + 'operator' => '==', + 'value' => 'all', + ], + ], + ], + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => '', + 'active' => true, + 'description' => '', + 'show_in_graphql' => 1, + 'graphql_field_name' => 'menuItemFields', + 'graphql_types' => [ 'MenuItem' ] + ]; + + acf_add_local_field_group( array_merge( $defaults, $config ) ); + + + } + + public function register_acf_field( $config = [] ) { + + $defaults = [ + 'parent' => $this->group_key, + 'key' => 'field_5d7812fd123', + 'label' => 'Text', + 'name' => 'text', + 'type' => 'text', + 'instructions' => '', + 'required' => 0, + 'conditional_logic' => 0, + 'wrapper' => array( + 'width' => '', + 'class' => '', + 'id' => '', + ), + 'show_in_graphql' => 1, + 'default_value' => '', + 'placeholder' => '', + 'prepend' => '', + 'append' => '', + 'maxlength' => '', + ]; + + acf_add_local_field( array_merge( $defaults, $config ) ); + } + + /** + * @throws Exception + */ + public function testBasicQuery() { + $query = '{ menus { nodes { id } } }'; + $actual = graphql( [ 'query' => $query ] ); + $this->assertArrayNotHasKey( 'errors', $actual ); + } + + public function testAcfTextField() { + + $this->register_acf_field([ + 'name' => 'menu_item_text_test', + 'type' => 'text', + ]); + + $expected_text = 'Some Text'; + + update_field( 'menu_item_text_test', $expected_text, $this->menu_item_id ); + $field = get_field( 'menu_item_text_test', $this->menu_item_id ); + codecept_debug( [ 'field_value', $field, $this->comment_id ] ); + + $query = ' + query getMenuItem( $id: ID! ) { + menuItem( id: $id ) { + __typename + databaseId + menuItemFields { + fieldGroupName + menuItemTextTest + } + } + } + '; + + $actual = graphql([ + 'query' => $query, + 'variables' => [ + 'id' => \GraphQLRelay\Relay::toGlobalId( 'post', $this->menu_item_id ), + ], + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + $this->assertSame( $this->menu_item_id, $actual['data']['menuItem']['databaseId'] ); + $this->assertSame( $expected_text, $actual['data']['menuItem']['menuItemFields']['menuItemTextTest'] ); + + } + +} diff --git a/tests/wpunit/MenuLocationTest.php b/tests/wpunit/MenuLocationTest.php new file mode 100644 index 0000000..bdc2b26 --- /dev/null +++ b/tests/wpunit/MenuLocationTest.php @@ -0,0 +1,179 @@ +group_key = __CLASS__; + WPGraphQL::clear_schema(); + $this->register_acf_field_group(); + + $this->post_id = $this->factory()->post->create( [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_title' => 'Test', + 'post_content' => 'test', + ] ); + + $this->tag_id = $this->factory()->term->create( [ + 'taxonomy' => 'post_tag', + ] ); + + $this->comment_id = $this->factory()->comment->create([ + 'comment_content' => 'test comment', + 'comment_author' => 'Test Author', + 'comment_approved' => true, + 'comment_post_ID' => $this->post_id, + ]); + + $location_name = 'test-location'; + add_theme_support( 'nav_menus' ); + register_nav_menu( $location_name, 'test menu...' ); + $menu_slug = 'my-test-menu'; + $this->menu_id = wp_create_nav_menu( $menu_slug ); + $post_id = $this->factory()->post->create(); + + $this->menu_item_id = wp_update_nav_menu_item( + $this->menu_id, + 0, + [ + 'menu-item-title' => 'Menu item', + 'menu-item-object' => 'post', + 'menu-item-object-id' => $post_id, + 'menu-item-status' => 'publish', + 'menu-item-type' => 'post_type', + ] + ); + set_theme_mod( 'nav_menu_locations', [ $location_name => $this->menu_id ] ); + + $this->test_image = dirname( __FILE__, 2 ) . '/_data/images/test.png'; + + } + + public function tearDown(): void { + acf_remove_local_field_group( $this->group_key ); + wp_delete_post( $this->post_id, true ); + WPGraphQL::clear_schema(); + parent::tearDown(); // TODO: Change the autogenerated stub + } + + public function register_acf_field_group( $config = [] ) { + + $defaults = [ + 'key' => $this->group_key, + 'title' => 'Menu Fields', + 'fields' => [], + 'location' => [ + [ + [ + 'param' => 'nav_menu', + 'operator' => '==', + 'value' => 'all', + ], + ], + ], + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => '', + 'active' => true, + 'description' => '', + 'show_in_graphql' => 1, + 'graphql_field_name' => 'menuFields', + 'graphql_types' => [ 'Menu' ] + ]; + + acf_add_local_field_group( array_merge( $defaults, $config ) ); + + + } + + public function register_acf_field( $config = [] ) { + + $defaults = [ + 'parent' => $this->group_key, + 'key' => 'field_5d7812fd123', + 'label' => 'Text', + 'name' => 'text', + 'type' => 'text', + 'instructions' => '', + 'required' => 0, + 'conditional_logic' => 0, + 'wrapper' => array( + 'width' => '', + 'class' => '', + 'id' => '', + ), + 'show_in_graphql' => 1, + 'default_value' => '', + 'placeholder' => '', + 'prepend' => '', + 'append' => '', + 'maxlength' => '', + ]; + + acf_add_local_field( array_merge( $defaults, $config ) ); + } + + /** + * @throws Exception + */ + public function testBasicQuery() { + $query = '{ menuItems { nodes { id } } }'; + $actual = graphql( [ 'query' => $query ] ); + $this->assertArrayNotHasKey( 'errors', $actual ); + } + + public function testAcfTextField() { + + $this->register_acf_field([ + 'name' => 'menu_text_test', + 'type' => 'text', + ]); + + $expected_text = 'Some Text'; + + update_field( 'menu_text_test', $expected_text, 'nav_menu_' . $this->menu_id ); + $field = get_field( 'menu_text_test', 'nav_menu_' . $this->menu_id ); + codecept_debug( [ 'field_value', $field, 'nav_menu_' . $this->menu_id ] ); + + $query = ' + query getMenu( $id: ID! ) { + menu( id: $id ) { + __typename + databaseId + menuFields { + fieldGroupName + menuTextTest + } + } + } + '; + + $actual = graphql([ + 'query' => $query, + 'variables' => [ + 'id' => \GraphQLRelay\Relay::toGlobalId( 'term', $this->menu_id ), + ], + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + $this->assertSame( $this->menu_id, $actual['data']['menu']['databaseId'] ); + $this->assertSame( $expected_text, $actual['data']['menu']['menuFields']['menuTextTest'] ); + + } + +} diff --git a/tests/wpunit/PostObjectFieldsTest.php b/tests/wpunit/PostObjectFieldsTest.php index 2d6054f..13d8d0e 100644 --- a/tests/wpunit/PostObjectFieldsTest.php +++ b/tests/wpunit/PostObjectFieldsTest.php @@ -3,12 +3,15 @@ class PostObjectFieldsTest extends \Codeception\TestCase\WPTestCase { public $group_key; public $post_id; + public $test_image; + + public function setUp(): void { + - public function setUp() { parent::setUp(); // TODO: Change the autogenerated stub - WPGraphQL::clear_schema(); - $this->group_key = __CLASS__; + $this->group_key = __CLASS__; + WPGraphQL::clear_schema(); $this->register_acf_field_group(); $this->post_id = $this->factory()->post->create([ @@ -18,10 +21,21 @@ public function setUp() { 'post_content' => 'test', ]); + $this->test_image = dirname( __FILE__, 2 ) . '/_data/images/test.png'; + + + } - public function tearDown() { - parent::tearDown(); // TODO: Change the autogenerated stub + public function tearDown(): void { + + $this->deregister_acf_field_group(); + WPGraphQL::clear_schema(); wp_delete_post( $this->post_id, true ); + parent::tearDown(); // TODO: Change the autogenerated stub + } + + public function deregister_acf_field_group() { + acf_remove_local_field_group( $this->group_key ); } public function register_acf_field_group( $config = [] ) { @@ -49,6 +63,7 @@ public function register_acf_field_group( $config = [] ) { 'description' => '', 'show_in_graphql' => 1, 'graphql_field_name' => 'postFields', + 'graphql_types' => ['Post'] ]; acf_add_local_field_group( array_merge( $defaults, $config )); @@ -225,7 +240,7 @@ public function testQueryRangeField() { 'name' => 'range_field', ]); - $expected = absint(66 ); + $expected = floatval(66 ); update_field( 'range_field', $expected, $this->post_id ); $query = ' @@ -385,7 +400,7 @@ public function testQueryImageField() { 'name' => 'image_field', ]); - $filename = ( WPGRAPHQL_PLUGIN_DIR . '/tests/_data/images/test.png' ); + $filename = ( $this->test_image ); $img_id = $this->factory()->attachment->create_upload_object( $filename ); update_field( 'image_field', $img_id, $this->post_id ); @@ -438,7 +453,7 @@ public function testQueryFileField() { 'name' => 'file_field', ]); - $filename = ( WPGRAPHQL_PLUGIN_DIR . '/tests/_data/images/test.png' ); + $filename = ( $this->test_image ); $img_id = $this->factory()->attachment->create_upload_object( $filename ); update_field( 'file_field', $img_id, $this->post_id ); @@ -572,7 +587,7 @@ public function testQueryGalleryField() { /** * Save Image IDs as the value for the gallery field */ - $filename = ( WPGRAPHQL_PLUGIN_DIR . '/tests/_data/images/test.png' ); + $filename = ( $this->test_image ); $img_id_1 = $this->factory()->attachment->create_upload_object( $filename ); $img_id_2 = $this->factory()->attachment->create_upload_object( $filename ); $img_ids = [ $img_id_1, $img_id_2 ]; @@ -1198,13 +1213,17 @@ public function testQueryMultipleSelectFieldWithNoValueSet() { public function testQueryFieldOnCustomPostType() { register_post_type( 'acf_test', [ + 'show_ui' => true, 'show_in_graphql' => 'true', 'graphql_single_name' => 'acfTest', 'graphql_plural_name' => 'acfTests' ] ); + $group_key = uniqid(); + $this->register_acf_field_group([ - 'key' => $this->group_key . 'acf_test_group', + 'key' => $group_key . 'acf_test_group', + 'title' => 'ACF Test Fields', 'location' => [ [ [ @@ -1214,10 +1233,14 @@ public function testQueryFieldOnCustomPostType() { ], ], ], + 'graphql_field_name' => 'acfTestFields', + 'graphql_types' => [ 'acfTest' ] ]); + + $this->register_acf_field([ - 'parent' => $this->group_key . 'acf_test_group', + 'parent' => $group_key . 'acf_test_group', 'type' => 'text', 'name' => 'acf_text_field', ]); @@ -1228,18 +1251,43 @@ public function testQueryFieldOnCustomPostType() { 'post_title' => 'ACF Test', ]); + $query = ' + { + __type( name: "AcfTest_Acftestfields" ) { + name + description + fields { + name + } + } + acfTest: __type( name: "AcfTest" ) { + name + description + fields { + name + } + } + } + '; + + $debug = graphql([ + 'query' => $query, + ]); + + codecept_debug( $debug ); + $expected_text_1 = 'test value'; update_field( 'acf_text_field', $expected_text_1, $id ); $query = ' - query GET_CUSTOM_POST_TYPE_WITH_ACF_FIELD( $testId: Int! ) { - acfTestBy( acfTestId: $testId ) { + query GET_CUSTOM_POST_TYPE_WITH_ACF_FIELD( $testId: ID! ) { + acfTest( id: $testId idType: DATABASE_ID ) { __typename id title - postFields { - acfTextField + acfTestFields { + fieldGroupName } } }'; @@ -1254,9 +1302,9 @@ public function testQueryFieldOnCustomPostType() { codecept_debug( $actual ); $this->assertArrayNotHasKey( 'errors', $actual ); - $this->assertEquals( $expected_text_1, $actual['data']['acfTestBy']['postFields']['acfTextField'] ); - + $this->assertEquals( 'acfTestFields', $actual['data']['acfTest']['acfTestFields']['fieldGroupName'] ); + acf_remove_local_field_group( $group_key . 'acf_test_group' ); } /** @@ -1283,7 +1331,7 @@ public function testQueryRelationshipField() { 'post_title' => 'Test Page', ]); - $filename = ( WPGRAPHQL_PLUGIN_DIR . '/tests/_data/images/test.png' ); + $filename = ( $this->test_image ); $img_id = $this->factory()->attachment->create_upload_object( $filename ); @@ -1345,1061 +1393,4 @@ public function test_flex_field_preview() { public function test_repeater_field_preview() { // @todo: test that previewing repeater fields work } - - protected function register_fields() { - - add_action( 'init', function() { - register_post_type( 'test', [ - 'show_in_graphql' => true, - 'hierarchical' => true, - 'graphql_single_name' => 'Test', - 'graphql_plural_name' => 'Tests' - ] ); - }); - - acf_add_local_field_group(array( - 'key' => 'group_5c8c7abfe98f7', - 'title' => 'Post Fields', - 'fields' => array( - array( - 'key' => 'field_5c8c7ac6571de', - 'label' => 'Text Field', - 'name' => 'text_field', - 'type' => 'text', - 'instructions' => 'Instructions for the text field', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => 'textField', - 'default_value' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - 'maxlength' => '', - ), - array( - 'key' => 'field_5c8d7107e67a1', - 'label' => 'Text Area Field', - 'name' => 'text_area_field', - 'type' => 'textarea', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'default_value' => '', - 'placeholder' => '', - 'maxlength' => '', - 'rows' => '', - 'new_lines' => '', - ), - array( - 'key' => 'field_5c8d7d304bb3a', - 'label' => 'Number Field', - 'name' => 'number_field', - 'type' => 'number', - 'instructions' => 'Add a number', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => 'customNumberFieldName', - 'default_value' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - 'min' => '', - 'max' => '', - 'step' => '', - ), - array( - 'key' => 'field_5c8f2ab9f90aa', - 'label' => 'Range Field', - 'name' => 'range_field', - 'type' => 'range', - 'instructions' => 'Range field, dawg', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'min' => '', - 'max' => '', - 'step' => '', - 'prepend' => '', - 'append' => '', - ), - array( - 'key' => 'field_5c8f2adb8e636', - 'label' => 'Email Field', - 'name' => 'email_field', - 'type' => 'email', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - ), - array( - 'key' => 'field_5c8f2aea8e637', - 'label' => 'URL Field', - 'name' => 'url_field', - 'type' => 'url', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'placeholder' => '', - ), - array( - 'key' => 'field_5c8f2af38e638', - 'label' => 'Password Field', - 'name' => 'password_field', - 'type' => 'password', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - ), - array( - 'key' => 'field_5c8f2aff8e639', - 'label' => 'Image Field', - 'name' => 'image_field', - 'type' => 'image', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'return_format' => 'array', - 'preview_size' => 'thumbnail', - 'library' => 'all', - 'min_width' => '', - 'min_height' => '', - 'min_size' => '', - 'max_width' => '', - 'max_height' => '', - 'max_size' => '', - 'mime_types' => '', - ), - array( - 'key' => 'field_5c8f2b088e63a', - 'label' => 'File Field', - 'name' => 'file_field', - 'type' => 'file', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'return_format' => 'array', - 'library' => 'all', - 'min_size' => '', - 'max_size' => '', - 'mime_types' => '', - ), - array( - 'key' => 'field_5c8f2b178e63b', - 'label' => 'WYSIWYG Field', - 'name' => 'wysiwyg_field', - 'type' => 'wysiwyg', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'tabs' => 'all', - 'toolbar' => 'full', - 'media_upload' => 1, - 'delay' => 0, - ), - array( - 'key' => 'field_5c8f2b2d8e63c', - 'label' => 'oEmbed Field', - 'name' => 'oembed_field', - 'type' => 'oembed', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'width' => '', - 'height' => '', - ), - array( - 'key' => 'field_5c8f2b378e63d', - 'label' => 'Gallery Field', - 'name' => 'gallery_field', - 'type' => 'gallery', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'min' => '', - 'max' => '', - 'insert' => 'append', - 'library' => 'all', - 'min_width' => '', - 'min_height' => '', - 'min_size' => '', - 'max_width' => '', - 'max_height' => '', - 'max_size' => '', - 'mime_types' => '', - ), - array( - 'key' => 'field_5c8f2b418e63e', - 'label' => 'Select Field', - 'name' => 'select_field', - 'type' => 'select', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'choices' => array( - 'one' => 'One', - 'two' => 'Two', - ), - 'default_value' => array( - ), - 'allow_null' => 0, - 'multiple' => 0, - 'ui' => 0, - 'return_format' => 'value', - 'ajax' => 0, - 'placeholder' => '', - ), - array( - 'key' => 'field_selectFieldMultiple', - 'label' => 'Select Field_Multiple', - 'name' => 'select_field_multiple', - 'type' => 'select', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => 'selectMultiple', - 'choices' => array( - 'one' => 'One', - 'two' => 'Two', - ), - 'default_value' => array( - ), - 'allow_null' => 0, - 'multiple' => 1, - 'ui' => 0, - 'return_format' => 'value', - 'ajax' => 0, - 'placeholder' => '', - ), - array( - 'key' => 'field_5c8f2b518e63f', - 'label' => 'Checkbox Field', - 'name' => 'checkbox_field', - 'type' => 'checkbox', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'choices' => array( - 'one' => 'One', - 'two' => 'Two', - ), - 'allow_custom' => 0, - 'default_value' => array( - ), - 'layout' => 'vertical', - 'toggle' => 0, - 'return_format' => 'value', - 'save_custom' => 0, - ), - array( - 'key' => 'field_5c8f2b5c8e640', - 'label' => 'Radio Button Field', - 'name' => 'radio_button_field', - 'type' => 'radio', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'choices' => array( - 'one' => 'One', - 'two' => 'Two', - ), - 'allow_null' => 0, - 'other_choice' => 0, - 'default_value' => '', - 'layout' => 'vertical', - 'return_format' => 'value', - 'save_other_choice' => 0, - ), - array( - 'key' => 'field_5c8f2b6c8e641', - 'label' => 'Button Group Field', - 'name' => 'button_group_field', - 'type' => 'button_group', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'choices' => array( - 'one' => 'One', - 'two' => 'Two', - ), - 'allow_null' => 0, - 'default_value' => '', - 'layout' => 'horizontal', - 'return_format' => 'value', - ), - array( - 'key' => 'field_5c8f2b8d8e642', - 'label' => 'True False Field', - 'name' => 'true_false_field', - 'type' => 'true_false', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'message' => '', - 'default_value' => 0, - 'ui' => 0, - 'ui_on_text' => '', - 'ui_off_text' => '', - ), - array( - 'key' => 'field_5c906944b8fea', - 'label' => 'Link Field', - 'name' => 'link_field', - 'type' => 'link', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'return_format' => 'array', - ), - array( - 'key' => 'field_5c906a60cb595', - 'label' => 'Post Object Field', - 'name' => 'post_object_field', - 'type' => 'post_object', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'post_type' => [ - 'post', - 'page' - ], - 'taxonomy' => '', - 'allow_null' => 0, - 'multiple' => 0, - 'return_format' => 'id', - 'ui' => 1, - ), - array( - 'key' => 'field_5c906a7621090', - 'label' => 'Page Link Field', - 'name' => 'page_link_field', - 'type' => 'page_link', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'post_type' => [ - 'post', - ], - 'taxonomy' => '', - 'allow_null' => 0, - 'allow_archives' => 0, - 'multiple' => 0, - - ), - array( - 'key' => 'field_5c906d3dc1e0f', - 'label' => 'Relationship Field', - 'name' => 'relationship_field', - 'type' => 'relationship', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'post_type' => [ - 'post', - 'page', - 'attachment' - ], - 'taxonomy' => '', - 'filters' => array( - 0 => 'search', - 1 => 'post_type', - 2 => 'taxonomy', - ), - 'elements' => '', - 'min' => '', - 'max' => '', - 'return_format' => 'object', - ), - array( - 'key' => 'field_5c906eb5d836e', - 'label' => 'Taxonomy Field', - 'name' => 'taxonomy_field', - 'type' => 'taxonomy', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'taxonomy' => 'category', - 'field_type' => 'checkbox', - 'add_term' => 1, - 'save_terms' => 0, - 'load_terms' => 0, - 'return_format' => 'id', - 'multiple' => 0, - 'allow_null' => 0, - ), - array( - 'key' => 'field_5c907409a9b0f', - 'label' => 'User Field', - 'name' => 'user_field', - 'type' => 'user', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'role' => '', - 'allow_null' => 0, - 'multiple' => 0, - 'return_format' => 'array', - ), - array( - 'key' => 'field_5c913a1bbaef7', - 'label' => 'Google Map Field', - 'name' => 'google_map_field', - 'type' => 'google_map', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'center_lat' => '', - 'center_lng' => '', - 'zoom' => '', - 'height' => '', - ), - array( - 'key' => 'field_5c91490c792c2', - 'label' => 'Date Picker Field', - 'name' => 'date_picker_field', - 'type' => 'date_picker', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'display_format' => 'd/m/Y', - 'return_format' => 'F j, Y', - 'first_day' => 0, - ), - array( - 'key' => 'field_5c914924792c3', - 'label' => 'Date Time Picker Field', - 'name' => 'date_time_picker_field', - 'type' => 'date_time_picker', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'display_format' => 'd/m/Y g:i a', - 'return_format' => 'd/m/Y g:i a', - 'first_day' => 0, - ), - array( - 'key' => 'field_5c91493a792c4', - 'label' => 'Time Picker Field', - 'name' => 'time_picker_field', - 'type' => 'time_picker', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'display_format' => 'g:i a', - 'return_format' => 'g:i a', - ), - array( - 'key' => 'field_5c914945792c5', - 'label' => 'Color Picker Field', - 'name' => 'color_picker_field', - 'type' => 'color_picker', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - ), - array( - 'key' => 'field_5c914d2be1d78', - 'label' => 'Group Field', - 'name' => 'group_field', - 'type' => 'group', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'layout' => 'block', - 'sub_fields' => array( - array( - 'key' => 'field_5c914da8bcf6f', - 'label' => 'Text Field In Group', - 'name' => 'text_field_in_group', - 'type' => 'text', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - 'maxlength' => '', - ), - array( - 'key' => 'field_5c914e92bcf70', - 'label' => 'Text Area Field In Group', - 'name' => 'text_area_field_in_group', - 'type' => 'textarea', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'placeholder' => '', - 'maxlength' => '', - 'rows' => '', - 'new_lines' => '', - ), - ), - ), - array( - 'key' => 'field_5c91622b29f39', - 'label' => 'Repeater Field', - 'name' => 'repeater_field', - 'type' => 'repeater', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'collapsed' => '', - 'min' => 0, - 'max' => 0, - 'layout' => 'table', - 'button_label' => '', - 'sub_fields' => array( - array( - 'key' => 'field_5c91623b29f3a', - 'label' => 'Text Field in Repeater', - 'name' => 'text_field_in_repeater', - 'type' => 'text', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - 'maxlength' => '', - ), - array( - 'key' => 'field_5c916cc879e26', - 'label' => 'Image in Repeater', - 'name' => 'image_in_repeater', - 'type' => 'image', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'return_format' => 'array', - 'preview_size' => 'thumbnail', - 'library' => 'all', - 'min_width' => '', - 'min_height' => '', - 'min_size' => '', - 'max_width' => '', - 'max_height' => '', - 'max_size' => '', - 'mime_types' => '', - ), - ), - ), - array( - 'key' => 'field_5c916e316f20c', - 'label' => 'Flexible Field', - 'name' => 'flexible_field', - 'type' => 'flexible_content', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'layouts' => array( - 'layout_5c916e380a702' => array( - 'key' => 'layout_5c916e380a702', - 'name' => 'group_one', - 'label' => 'Group One', - 'display' => 'block', - 'sub_fields' => array( - array( - 'key' => 'field_5c916e666f20d', - 'label' => 'Flex Group One', - 'name' => 'flex_group_one', - 'type' => 'group', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'layout' => 'block', - 'sub_fields' => array( - array( - 'key' => 'field_5c916e9a6f20e', - 'label' => 'Text', - 'name' => 'text', - 'type' => 'text', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'default_value' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - 'maxlength' => '', - ), - ), - ), - array( - 'key' => 'field_5caebca9bad98', - 'label' => 'Repeater', - 'name' => 'repeater', - 'type' => 'repeater', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'collapsed' => '', - 'min' => 0, - 'max' => 0, - 'layout' => 'table', - 'button_label' => '', - 'sub_fields' => array( - array( - 'key' => 'field_5caebcc0bad99', - 'label' => 'Text', - 'name' => 'text', - 'type' => 'text', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'default_value' => '', - 'placeholder' => '', - 'prepend' => '', - 'append' => '', - 'maxlength' => '', - ), - ), - ), - ), - 'min' => '', - 'max' => '', - ), - 'layout_5c916f24e716d' => array( - 'key' => 'layout_5c916f24e716d', - 'name' => 'group_2', - 'label' => 'Group Two', - 'display' => 'block', - 'sub_fields' => array( - array( - 'key' => 'field_5c916f24e7170', - 'label' => 'Flex Image', - 'name' => 'flex_image', - 'type' => 'image', - 'instructions' => '', - 'required' => 0, - 'conditional_logic' => 0, - 'wrapper' => array( - 'width' => '', - 'class' => '', - 'id' => '', - ), - 'show_in_graphql' => 1, - 'graphql_field_name' => '', - 'return_format' => 'array', - 'preview_size' => 'thumbnail', - 'library' => 'all', - 'min_width' => '', - 'min_height' => '', - 'min_size' => '', - 'max_width' => '', - 'max_height' => '', - 'max_size' => '', - 'mime_types' => '', - ), - ), - 'min' => '', - 'max' => '', - ), - 'layout_5d74257693147' => array( - 'key' => 'layout_5d74257693147', - 'name' => 'group_3', - 'label' => 'Group Three', - 'display' => 'block', - 'sub_fields' => array(), - 'min' => '', - 'max' => '', - ), - ), - 'button_label' => 'Add Row', - 'min' => '', - 'max' => '', - ), - ), - 'location' => array( - array( - array( - 'param' => 'post_type', - 'operator' => '==', - 'value' => 'post', - ), - array( - 'param' => 'post_status', - 'operator' => '==', - 'value' => 'publish', - ), - ), - array( - array( - 'param' => 'post_type', - 'operator' => '==', - 'value' => 'test', - ), - array( - 'param' => 'post_status', - 'operator' => '==', - 'value' => 'publish', - ), - ), - array( - array( - 'param' => 'taxonomy', - 'operator' => '==', - 'value' => 'category', - ), - ), - array( - array( - 'param' => 'taxonomy', - 'operator' => '==', - 'value' => 'post_tag', - ), - ), - array( - array( - 'param' => 'taxonomy', - 'operator' => '==', - 'value' => 'post_format', - ), - ), - array( - array( - 'param' => 'attachment', - 'operator' => '==', - 'value' => 'all', - ), - ), - array( - array( - 'param' => 'post_type', - 'operator' => '==', - 'value' => 'page', - ), - ), - array( - array( - 'param' => 'comment', - 'operator' => '==', - 'value' => 'post', - ), - ), - array( - array( - 'param' => 'comment', - 'operator' => '==', - 'value' => 'page', - ), - ), - array( - array( - 'param' => 'comment', - 'operator' => '==', - 'value' => 'attachment', - ), - ), - array( - array( - 'param' => 'nav_menu', - 'operator' => '==', - 'value' => 'all', - ), - ), - array( - array( - 'param' => 'nav_menu_item', - 'operator' => '==', - 'value' => 'all', - ), - ), - ), - 'menu_order' => 0, - 'position' => 'normal', - 'style' => 'default', - 'label_placement' => 'top', - 'instruction_placement' => 'label', - 'hide_on_screen' => '', - 'active' => true, - 'description' => '', - 'show_in_graphql' => 1, - 'graphql_field_name' => 'postFields', - )); - - } - } diff --git a/tests/wpunit/TemplateLocationsTest.php b/tests/wpunit/TemplateLocationsTest.php new file mode 100644 index 0000000..fc5bdc7 --- /dev/null +++ b/tests/wpunit/TemplateLocationsTest.php @@ -0,0 +1,38 @@ +group_key = __CLASS__; + WPGraphQL::clear_schema(); + + $this->post_id = $this->factory()->post->create( [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_title' => 'Test', + 'post_content' => 'test', + ] ); + + } + + public function tearDown(): void { + parent::tearDown(); + } + + public function testBasicQuery() { + $query = '{ posts { nodes { id, template { __typename } } } }'; + $actual = graphql( [ 'query' => $query ] ); + codecept_debug( $actual ); + $this->assertArrayNotHasKey( 'errors', $actual ); + } + + // @todo: WPGraphQL needs to update to use get_page_templates() in order + // for testing to work properly for assigning fields to + +} diff --git a/tests/wpunit/TermLocationTest.php b/tests/wpunit/TermLocationTest.php new file mode 100644 index 0000000..3e3ca2e --- /dev/null +++ b/tests/wpunit/TermLocationTest.php @@ -0,0 +1,148 @@ +group_key = __CLASS__; + WPGraphQL::clear_schema(); + $this->register_acf_field_group(); + + $this->post_id = $this->factory()->post->create( [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_title' => 'Test', + 'post_content' => 'test', + ] ); + + $this->tag_id = $this->factory()->term->create( [ + 'taxonomy' => 'post_tag', + ] ); + + $this->test_image = dirname( __FILE__, 2 ) . '/_data/images/test.png'; + + } + + public function tearDown(): void { + acf_remove_local_field_group( $this->group_key ); + wp_delete_post( $this->post_id, true ); + WPGraphQL::clear_schema(); + parent::tearDown(); // TODO: Change the autogenerated stub + } + + public function register_acf_field_group( $config = [] ) { + + $defaults = [ + 'key' => $this->group_key, + 'title' => 'Tag Fields', + 'fields' => [], + 'location' => [ + [ + [ + 'param' => 'taxonomy', + 'operator' => '==', + 'value' => 'post_tag', + ], + ], + ], + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => '', + 'active' => true, + 'description' => '', + 'show_in_graphql' => 1, + 'graphql_field_name' => 'tagFields', + 'graphql_types' => [ 'Tag' ] + ]; + + acf_add_local_field_group( array_merge( $defaults, $config ) ); + + + } + + public function register_acf_field( $config = [] ) { + + $defaults = [ + 'parent' => $this->group_key, + 'key' => 'field_5d7812fd123', + 'label' => 'Text', + 'name' => 'text', + 'type' => 'text', + 'instructions' => '', + 'required' => 0, + 'conditional_logic' => 0, + 'wrapper' => array( + 'width' => '', + 'class' => '', + 'id' => '', + ), + 'show_in_graphql' => 1, + 'default_value' => '', + 'placeholder' => '', + 'prepend' => '', + 'append' => '', + 'maxlength' => '', + ]; + + acf_add_local_field( array_merge( $defaults, $config ) ); + } + + /** + * @throws Exception + */ + public function testBasicQuery() { + $query = '{ tags { nodes { id } } }'; + $actual = graphql( [ 'query' => $query ] ); + $this->assertArrayNotHasKey( 'errors', $actual ); + } + + public function testAcfTextField() { + + $this->register_acf_field([ + 'name' => 'tag_text_field', + 'type' => 'text', + ]); + + $expected_text = 'Some Text'; + + update_field( 'tag_text_field', $expected_text, 'post_tag_' . $this->tag_id ); + $field = get_field( 'tag_text_field', 'post_tag_' . $this->tag_id, true ); + codecept_debug( [ 'field_value', $field, 'post_tag_' . $this->tag_id ] ); + + $query = ' + query getTag( $id: ID! ) { + tag( id: $id idType: DATABASE_ID ) { + databaseId + tagFields { + fieldGroupName + tagTextField + } + } + } + '; + + $actual = graphql([ + 'query' => $query, + 'variables' => [ + 'id' => $this->tag_id, + ], + ]); + + codecept_debug( $actual ); + + $this->assertArrayNotHasKey( 'errors', $actual ); + $this->assertSame( $this->tag_id, $actual['data']['tag']['databaseId'] ); + $this->assertSame( $expected_text, $actual['data']['tag']['tagFields']['tagTextField'] ); + + } + +} diff --git a/tests/wpunit/TextFieldTest.php b/tests/wpunit/TextFieldTest.php index 6b36575..07ee0ad 100644 --- a/tests/wpunit/TextFieldTest.php +++ b/tests/wpunit/TextFieldTest.php @@ -1,11 +1,11 @@ * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { + private $vendorDir; + // PSR-4 private $prefixLengthsPsr4 = array(); private $prefixDirsPsr4 = array(); @@ -57,10 +59,17 @@ class ClassLoader private $missingClasses = array(); private $apcuPrefix; + private static $registeredLoaders = array(); + + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + } + public function getPrefixes() { if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); } return array(); @@ -300,6 +309,15 @@ public function getApcuPrefix() public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + //no-op + } elseif ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } } /** @@ -308,6 +326,10 @@ public function register($prepend = false) public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } } /** @@ -367,6 +389,16 @@ public function findFile($class) return $file; } + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + private function findFileWithExtension($class, $ext) { // PSR-4 lookup diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000..bb0f57a --- /dev/null +++ b/vendor/composer/InstalledVersions.php @@ -0,0 +1,284 @@ + + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => '1bad7d6d5214448f8d27bbcd908b24d6552667cb', + 'name' => 'wp-graphql/wp-graphql-acf', + ), + 'versions' => + array ( + 'wp-graphql/wp-graphql-acf' => + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => '1bad7d6d5214448f8d27bbcd908b24d6552667cb', + ), + ), +); +private static $canGetVendors; +private static $installedByVendor = array(); + + + + + + + +public static function getInstalledPackages() +{ +$packages = array(); +foreach (self::getInstalled() as $installed) { +$packages[] = array_keys($installed['versions']); +} + + +if (1 === \count($packages)) { +return $packages[0]; +} + +return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); +} + + + + + + + + + +public static function isInstalled($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (isset($installed['versions'][$packageName])) { +return true; +} +} + +return false; +} + + + + + + + + + + + + + + +public static function satisfies(VersionParser $parser, $packageName, $constraint) +{ +$constraint = $parser->parseConstraints($constraint); +$provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + +return $provided->matches($constraint); +} + + + + + + + + + + +public static function getVersionRanges($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +$ranges = array(); +if (isset($installed['versions'][$packageName]['pretty_version'])) { +$ranges[] = $installed['versions'][$packageName]['pretty_version']; +} +if (array_key_exists('aliases', $installed['versions'][$packageName])) { +$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); +} +if (array_key_exists('replaced', $installed['versions'][$packageName])) { +$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); +} +if (array_key_exists('provided', $installed['versions'][$packageName])) { +$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); +} + +return implode(' || ', $ranges); +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getVersion($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +if (!isset($installed['versions'][$packageName]['version'])) { +return null; +} + +return $installed['versions'][$packageName]['version']; +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getPrettyVersion($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +if (!isset($installed['versions'][$packageName]['pretty_version'])) { +return null; +} + +return $installed['versions'][$packageName]['pretty_version']; +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getReference($packageName) +{ +foreach (self::getInstalled() as $installed) { +if (!isset($installed['versions'][$packageName])) { +continue; +} + +if (!isset($installed['versions'][$packageName]['reference'])) { +return null; +} + +return $installed['versions'][$packageName]['reference']; +} + +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + + + + + +public static function getRootPackage() +{ +$installed = self::getInstalled(); + +return $installed[0]['root']; +} + + + + + + + +public static function getRawData() +{ +return self::$installed; +} + + + + + + + + + + + + + + + + + + + +public static function reload($data) +{ +self::$installed = $data; +self::$installedByVendor = array(); +} + + + + +private static function getInstalled() +{ +if (null === self::$canGetVendors) { +self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); +} + +$installed = array(); + +if (self::$canGetVendors) { + +foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { +if (isset(self::$installedByVendor[$vendorDir])) { +$installed[] = self::$installedByVendor[$vendorDir]; +} elseif (is_file($vendorDir.'/composer/installed.php')) { +$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; +} +} +} + +$installed[] = self::$installed; + +return $installed; +} +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 6acd66d..7d3d8b2 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,7 +6,9 @@ $baseDir = dirname($vendorDir); return array( + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'WPGraphQL\\ACF\\ACF' => $baseDir . '/src/class-acf.php', 'WPGraphQL\\ACF\\ACF_Settings' => $baseDir . '/src/class-acfsettings.php', 'WPGraphQL\\ACF\\Config' => $baseDir . '/src/class-config.php', + 'WPGraphQL\\ACF\\LocationRules' => $baseDir . '/src/location-rules.php', ); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 37438df..3be627f 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit0d1c0770bd52413713b6ce542771fda1 +class ComposerAutoloaderInit9e804fc6f45415f83dd03aeed86e33f0 { private static $loader; @@ -22,15 +22,17 @@ public static function getLoader() return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit0d1c0770bd52413713b6ce542771fda1', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit0d1c0770bd52413713b6ce542771fda1', 'loadClassLoader')); + require __DIR__ . '/platform_check.php'; + + spl_autoload_register(array('ComposerAutoloaderInit9e804fc6f45415f83dd03aeed86e33f0', 'loadClassLoader'), true, true); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); + spl_autoload_unregister(array('ComposerAutoloaderInit9e804fc6f45415f83dd03aeed86e33f0', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit0d1c0770bd52413713b6ce542771fda1::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit9e804fc6f45415f83dd03aeed86e33f0::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index f86815d..23d10df 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit0d1c0770bd52413713b6ce542771fda1 +class ComposerStaticInit9e804fc6f45415f83dd03aeed86e33f0 { public static $prefixLengthsPsr4 = array ( 'W' => @@ -21,17 +21,19 @@ class ComposerStaticInit0d1c0770bd52413713b6ce542771fda1 ); public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'WPGraphQL\\ACF\\ACF' => __DIR__ . '/../..' . '/src/class-acf.php', 'WPGraphQL\\ACF\\ACF_Settings' => __DIR__ . '/../..' . '/src/class-acfsettings.php', 'WPGraphQL\\ACF\\Config' => __DIR__ . '/../..' . '/src/class-config.php', + 'WPGraphQL\\ACF\\LocationRules' => __DIR__ . '/../..' . '/src/location-rules.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit0d1c0770bd52413713b6ce542771fda1::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit0d1c0770bd52413713b6ce542771fda1::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit0d1c0770bd52413713b6ce542771fda1::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit9e804fc6f45415f83dd03aeed86e33f0::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit9e804fc6f45415f83dd03aeed86e33f0::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit9e804fc6f45415f83dd03aeed86e33f0::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index fe51488..f20a6c4 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1 +1,5 @@ -[] +{ + "packages": [], + "dev": false, + "dev-package-names": [] +} diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php new file mode 100644 index 0000000..6ae1e93 --- /dev/null +++ b/vendor/composer/installed.php @@ -0,0 +1,24 @@ + + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => '1bad7d6d5214448f8d27bbcd908b24d6552667cb', + 'name' => 'wp-graphql/wp-graphql-acf', + ), + 'versions' => + array ( + 'wp-graphql/wp-graphql-acf' => + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => '1bad7d6d5214448f8d27bbcd908b24d6552667cb', + ), + ), +); diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php new file mode 100644 index 0000000..f79e574 --- /dev/null +++ b/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 70000)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.0.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/wp-graphql-acf.php b/wp-graphql-acf.php index fb3dd68..76daa7b 100644 --- a/wp-graphql-acf.php +++ b/wp-graphql-acf.php @@ -7,9 +7,9 @@ * Author URI: https://www.wpgraphql.com * Text Domain: wp-graphql-acf * Domain Path: /languages - * Version: 0.4.1 + * Version: 0.5.0 * Requires PHP: 7.0 - * GitHub Plugin URI: https://github.com/afragen/github-updater + * GitHub Plugin URI: https://github.com/wp-graphql/wp-graphql-acf * * @package WPGraphQL_ACF */ @@ -26,6 +26,7 @@ * Define constants */ const WPGRAPHQL_REQUIRED_MIN_VERSION = '0.4.0'; +const WPGRAPHQL_ACF_VERSION = '0.5.0'; /** * Initialize the plugin @@ -111,3 +112,5 @@ function can_load_plugin() { return true; } + +