diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..7382bb8eb572 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes index 51fea41744d4..05fc1f38cde9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,18 +5,35 @@ # git files .gitattributes export-ignore -# .gitignore +.gitignore export-ignore -# helper config files -.travis.yml export-ignore -phpdoc.dist.xml export-ignore - -# Misc other files -readme.rst +# admin files +.github/ export-ignore +admin/ export-ignore +contributing/ export-ignore +.editorconfig export-ignore +CODE_OF_CONDUCT.md export-ignore +CONTRIBUTING.md export-ignore -# They don't want all of our tests... -tests/codeigniter/ export-ignore -tests/travis/ export-ignore +# contributor/development files +tests/ export-ignore +utils/ export-ignore +.php-cs-fixer.dist.php export-ignore +.php-cs-fixer.no-header.php export-ignore +.php-cs-fixer.tests.php export-ignore +.php-cs-fixer.user-guide.php export-ignore +structarmed.php export-ignore +phpmetrics.json export-ignore +phpstan-baseline.php export-ignore +phpstan-bootstrap.php export-ignore +phpstan.dist.neon export-ignore +phpunit.dist.xml export-ignore +psalm-baseline.xml export-ignore +psalm.xml export-ignore +psalm_autoload.php export-ignore +rector.php export-ignore -# User Guide Source Files -user_guide_src +# source user guide +user_guide_src/ export-ignore +.nojekyll export-ignore +phpdoc.dist.xml export-ignore diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000000..3f99053424ec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,138 @@ +name: Bug report +description: Create a report to help us improve CodeIgniter +title: "Bug: " +labels: ['bug'] + +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Before you begin, **please ensure that there are no existing issues, + whether still open or closed, related to your report**. + If there is, your report will be closed promptly. + + For example, if you get the error "*Undefined property: Config\\Exceptions::$sensitiveDataInTrace*", + you can search the GitHub repository with the keyword "[$sensitiveDataInTrace](https://github.com/codeigniter4/CodeIgniter4/search?q=%24sensitiveDataInTrace&type=issues)". + + --- + + - type: dropdown + id: php-version + attributes: + label: PHP Version + description: Which PHP versions did you run your code? + multiple: true + options: + - '8.2' + - '8.3' + - '8.4' + - '8.5' + validations: + required: true + + - type: input + id: codeigniter-version + attributes: + label: CodeIgniter4 Version + description: | + e.g. 4.1.5 + If you are not using the [latest version](https://github.com/codeigniter4/CodeIgniter4/releases), please + check to see if the problem occurs with the latest version. + validations: + required: true + + - type: dropdown + id: codeigniter-installation + attributes: + label: CodeIgniter4 Installation Method + multiple: false + options: + - Composer (using `codeigniter4/appstarter`) + - Composer (as dependency to an existing project) + - Manual (zip or tar.gz) + - Git + validations: + required: true + + - type: dropdown + id: operating-systems + attributes: + label: Which operating systems have you tested for this bug? + description: You may select more than one. + multiple: true + options: + - macOS + - Windows + - Linux + validations: + required: true + + - type: dropdown + id: server + attributes: + label: Which server did you use? + options: + - apache + - cli + - cli-server (PHP built-in webserver) + - cgi-fcgi + - fpm-fcgi + - phpdbg + validations: + required: true + + - type: dropdown + id: environment + attributes: + label: Environment + description: Which CI_ENVIRONMENT setting are you using? + multiple: true + options: + - production + - development + - testing + - other (custom) + validations: + required: true + + - type: input + id: database + attributes: + label: Database + description: e.g. MySQL 5.6, MariaDB 10.2, PostgreSQL 9.6 + validations: + required: false + + - type: textarea + id: description + attributes: + label: What happened? + placeholder: Tell us what you see! + validations: + required: true + + - type: textarea + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior. + validations: + required: true + + - type: textarea + attributes: + label: Expected Output + description: What do you expect to happen instead of this filed bug? + validations: + required: true + + - type: textarea + attributes: + label: Anything else? + description: | + Links? References? Anything that will give us more context about the issue you are encountering! + + Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..d399e41dca3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,9 @@ +blank_issues_enabled: false +contact_links: + - name: CodeIgniter Forum + url: https://forum.codeigniter.com/forum-30.html + about: Please ask your support questions and/or feature requests in the forums. Thanks! + + - name: CodeIgniter Slack channel + url: https://codeigniterchat.slack.com + about: Engage with other members of the community in our Slack channel. diff --git a/.github/ISSUE_TEMPLATE/planned-work.md b/.github/ISSUE_TEMPLATE/planned-work.md new file mode 100644 index 000000000000..3da11a7dbc58 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/planned-work.md @@ -0,0 +1,10 @@ +--- +name: Planned work +about: Maintainers' space. DO NOT use this for your bug reports! +title: 'Dev: ' +labels: dev +assignees: '' + +--- + +Maintainers will create "issues" for planned work, so it can be tracked. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..38f7db70cc60 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ + +**Description** +Explain what you have changed, and why. + +**Checklist:** +- [ ] Securely signed commits +- [ ] Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication) +- [ ] Unit testing, with >80% coverage +- [ ] User guide updated +- [ ] Conforms to style guide diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..c6188a21b9e6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 + +updates: + - package-ecosystem: 'composer' + directory: '/' + schedule: + interval: 'daily' + open-pull-requests-limit: 10 + groups: + composer-dependencies: + patterns: + - '*' + + - package-ecosystem: 'github-actions' + directories: + - '/' + - '/admin/framework' + - '/admin/starter' + - '/admin/userguide' + schedule: + interval: 'daily' + groups: + github-actions: + patterns: + - '*' + group-by: dependency-name diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000000..69cb0c51e123 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,23 @@ +# https://github.com/actions/labeler?tab=readme-ov-file#usage + +# Add the `4.8` label to PRs that target the `4.8` branch. +'4.8': # @todo change value whenever the next minor version is changed +- base-branch: '4.8' + +# Add the `github_actions` label to PRs that change any file in the `.github/workflows/` directory. +'github_actions': +- changed-files: + - any-glob-to-any-file: + - '.github/workflows/*' + +# Add the `documentation` label to PRs for documentation only. +'documentation': +- changed-files: + - any-glob-to-all-files: + - 'user_guide_src/source/**' + +# Add the `testing` label to PRs that changes tests only. +'testing': +- changed-files: + - any-glob-to-all-files: + - 'tests/**' diff --git a/.github/mergeable.yml b/.github/mergeable.yml new file mode 100644 index 000000000000..2e58526188aa --- /dev/null +++ b/.github/mergeable.yml @@ -0,0 +1,69 @@ +# https://mergeable.readthedocs.io/en/latest/configuration.html + +version: 2 +mergeable: + - when: issues.opened, issues.reopened + validate: + - do: or + validate: + - do: and + validate: + - do: description + must_include: + regex: '### PHP Version' + - do: description + must_include: + regex: '### CodeIgniter4 Version' + - do: author + must_include: + regex: ^kenjis|lonnieezell|MGatner|michalsn|paulbalandan|samsonasik|ddevsr$ + fail: + - do: comment + payload: + body: | + Hi there, @@author! :wave: + + It looks like you opened an issue without following the bug report template: + + * Bug report ([open an issue](https://github.com/codeigniter4/CodeIgniter4/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=Bug%3A+)) + * For feature request or support question, please use the [forums](https://forum.codeigniter.com/forum-30.html). + + The current issue will be closed. This is a precaution to save maintainers' time, I hope you'll understand. + + Sincerely, the mergeable bot 🤖 + - do: close + + - when: pull_request.opened, pull_request.ready_for_review + filter: + - do: payload + pull_request: + author_association: + must_include: + regex: ^NONE|FIRST_TIME_CONTRIBUTOR|FIRST_TIMER$ + validate: [] + pass: + - do: comment + payload: + body: | + Hi there, @@author! :wave: + + Thank you for sending this PR! + + We expect the following in all Pull Requests (PRs). + - PRs must be sent to the [appropriate branch](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#branching) + - All git commits must be [GPG-signed](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#signing) + - Must follow our [style guide](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#php-style) + - Be [commented](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#comments) in the PHP source file + - Be documented in the [user guide](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#user-guide) + - Be [unit tested](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#unit-testing) + - Pass all checks in GitHub Actions + + > [!IMPORTANT] + > We expect all code changes or bug-fixes to be accompanied by one or more tests added to our test suite to prove the code works. + + If pull requests do not comply with the above, they will likely be closed. Since we are a team of volunteers, we don't have any more time to work + on the framework than you do. Please make it as painless for your contributions to be included as possible. + + See https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md + + Sincerely, the mergeable bot 🤖 diff --git a/.github/prlint.json b/.github/prlint.json new file mode 100644 index 000000000000..bb352e554b71 --- /dev/null +++ b/.github/prlint.json @@ -0,0 +1,8 @@ +{ + "title": [ + { + "pattern": "^(\\[\\d+\\.\\d+\\]\\s{1})?(feat|fix|chore|docs|perf|refactor|style|test|config|revert)(\\([\\-.@:`a-zA-Z0-9]+\\))?!?:\\s{1}\\S.+\\S|Prep for \\d\\.\\d\\.\\d release|\\d\\.\\d\\.\\d (Ready|Merge) code$", + "message": "PR title must include the type (feat, fix, chore, docs, perf, refactor, style, test, config, revert) of the commit per Conventional Commits specification. See https://www.conventionalcommits.org/en/v1.0.0/ for the discussion." + } + ] +} diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000000..09ead7ca89d2 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,24 @@ +changelog: + exclude: + authors: + - dependabot + - dependabot[bot] + categories: + - title: Breaking Changes + labels: + - 'breaking change' + - title: Fixed Bugs + labels: + - bug + - title: New Features + labels: + - 'new feature' + - title: Enhancements + labels: + - enhancement + - title: Refactoring + labels: + - refactor + - title: Others (Only for checking. Remove this category) + labels: + - "*" diff --git a/.github/scripts/deploy-appstarter b/.github/scripts/deploy-appstarter new file mode 100644 index 000000000000..86d7bad2241a --- /dev/null +++ b/.github/scripts/deploy-appstarter @@ -0,0 +1,35 @@ +#!/bin/bash + +## Deploy codeigniter4/appstarter + +# Setup variables +SOURCE=$1 +TARGET=$2 +RELEASE=$3 + +echo "Preparing for version $3" +echo "Merging files from $1 to $2" + +# Prepare the source +cd $SOURCE +git checkout master + +# Prepare the target +cd $TARGET +git checkout master +rm -rf * + +# Copy common files +releasable='app public writable env LICENSE spark preload.php' +for fff in $releasable; +do + cp -Rf ${SOURCE}/$fff ./ +done + +# Copy repo-specific files +cp -Rf ${SOURCE}/admin/starter/. ./ + +# Commit the changes +git add . +git commit -m "Release ${RELEASE}" +bash ${SOURCE}/.github/scripts/secure-git-push https://github.com/codeigniter4/appstarter.git HEAD:master diff --git a/.github/scripts/deploy-framework b/.github/scripts/deploy-framework new file mode 100644 index 000000000000..ea18cadf1607 --- /dev/null +++ b/.github/scripts/deploy-framework @@ -0,0 +1,37 @@ +#!/bin/bash + +## Deploy codeigniter4/framework + +# Setup variables +SOURCE=$1 +TARGET=$2 +RELEASE=$3 + +echo "Preparing for version $3" +echo "Merging files from $1 to $2" + +# Prepare the source +cd $SOURCE +git checkout master + +# Prepare the target +cd $TARGET +git checkout master +rm -rf * + +# Copy common files +releasable='app public writable env LICENSE spark system preload.php' +for fff in $releasable; +do + cp -Rf ${SOURCE}/$fff ./ +done + +# Copy repo-specific files +cp -Rf ${SOURCE}/admin/framework/. ./ +# Copy tests files +cp -Rf ${SOURCE}/admin/starter/tests/. ./tests/ + +# Commit the changes +git add . +git commit -m "Release ${RELEASE}" +bash ${SOURCE}/.github/scripts/secure-git-push https://github.com/codeigniter4/framework.git HEAD:master diff --git a/.github/scripts/deploy-userguide b/.github/scripts/deploy-userguide new file mode 100755 index 000000000000..e664ac1b7ff5 --- /dev/null +++ b/.github/scripts/deploy-userguide @@ -0,0 +1,61 @@ +#!/bin/bash + +## Deploy codeigniter4/userguide + +# Setup variables +SOURCE=$1 +TARGET=$2 +RELEASE=$3 + +# Check if RELEASE is empty +if [ -z "$RELEASE" ]; then + echo "Error: \$RELEASE parameter is empty." + exit 1 +fi + +VERSION=$(echo "$RELEASE" | cut -c 2-) + +# Check if VERSION is empty +if [ -z "$VERSION" ]; then + echo "Error: Failed to extract \$VERSION from \$RELEASE parameter '$RELEASE'." + exit 1 +fi + +# Check if VERSION matches the format X.Y.Z +if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: VERSION '$VERSION' does not match the expected format X.Y.Z." + exit 1 +fi + +echo "Preparing for version $3" +echo "Merging files from $1 to $2" + +# Prepare the source +cd $SOURCE +git checkout master +cd user_guide_src +make html +make epub + +# Prepare the target +cd $TARGET +git checkout master +rm -rf docs + +# Copy common files +cp -Rf ${SOURCE}/LICENSE ./ + +# Copy repo-specific files +cp -Rf ${SOURCE}/admin/userguide/. ./ + +# Copy files +cp -Rf ${SOURCE}/user_guide_src/build/html ./docs +cp -Rf ${SOURCE}/user_guide_src/build/epub/CodeIgniter.epub ./CodeIgniter${VERSION}.epub + +# Ensure underscore prefixed files are published +touch ${TARGET}/docs/.nojekyll + +# Commit the changes +git add . +git commit -m "Release ${RELEASE}" +bash ${SOURCE}/.github/scripts/secure-git-push https://github.com/codeigniter4/userguide.git HEAD:master diff --git a/.github/scripts/random-tests-config.txt b/.github/scripts/random-tests-config.txt new file mode 100644 index 000000000000..5bf0fce66733 --- /dev/null +++ b/.github/scripts/random-tests-config.txt @@ -0,0 +1,49 @@ +# CodeIgniter4 Components Verified for Random Test Execution +# This file lists components that have been verified to pass all tests +# when run in random order (--order-by=random) +# +# Format: One component directory name per line (matching tests/system//) +# Comments start with # +# Uncomment components as they are verified to work with random execution +# +# Reference: https://github.com/codeigniter4/CodeIgniter4/issues/9968 + +API +AutoReview +Autoloader +Cache +CLI +Commands +Config +Cookie +# DataCaster +# DataConverter +# Database +# Debug +Email +# Encryption +# Entity +Events +Files +Filters +Format +HTTP +# Helpers +Honeypot +HotReloader +# I18n +# Images +Language +Log +# Models +Pager +Publisher +RESTful +# Router +Security +# Session +Test +Throttle +Typography +# Validation +View diff --git a/.github/scripts/run-random-tests.sh b/.github/scripts/run-random-tests.sh new file mode 100755 index 000000000000..fe321bef0492 --- /dev/null +++ b/.github/scripts/run-random-tests.sh @@ -0,0 +1,917 @@ +#!/usr/bin/env bash + +################################################################################ +# CodeIgniter4 - Random Test Execution Verification +# +# Verifies that tests for each component pass when run in random order. +# Reads a list of components from a config file and tests each with parallel +# execution while respecting a configurable concurrency limit. +# +# Usage: ./run-random-tests.sh [options] +# Options: +# -q, --quiet Suppress debug output +# -c, --component COMPONENT Test single COMPONENT (overrides config file) +# -n, --max-jobs MAX_JOBS Limit concurrent test jobs (auto-detect if omitted) +# -r, --repeat REPEAT Repeat full component run REPEAT times +# -t, --timeout TIMEOUT Per-component TIMEOUT in seconds (0 disables, default: 300) +# -h, --help Show this help message +# +# Examples: +# ./run-random-tests.sh --repeat 10 +# ./run-random-tests.sh --component Database --repeat 5 +# ./run-random-tests.sh --repeat 10 --max-jobs 4 --quiet +################################################################################ + +set -u +export LC_NUMERIC=C +trap 'kill "${bg_pids[@]:-}" 2>/dev/null; wait 2>/dev/null' EXIT INT TERM + +################################################################################ +# CONFIGURATION & INITIALIZATION +################################################################################ + +# Color codes for terminal output +readonly RED='\033[0;31m' +readonly BOLD_RED='\033[1;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[0;33m' +readonly BOLD_YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly BOLD='\033[1m' +readonly RESET='\033[0m' + +# Script paths +readonly script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly project_root="$( cd "$script_dir/../.." && pwd )" +readonly config_file="$script_dir/random-tests-config.txt" +readonly results_dir="$project_root/build/random-tests" + +# Runtime variables +quiet="" +component="" +max_jobs="" +repeat_count=1 +timeout_seconds=300 +first_result=true +declare -a bg_pids=() + +# Counters +completed=0 +passed=0 +failed=0 +skipped=0 +total=0 + +# Component state tracking +declare -a displayed_components=() +declare -a failed_components=() +declare -a skipped_components=() + +################################################################################ +# UTILITY FUNCTIONS +################################################################################ + +is_quiet() { + [[ "$quiet" == "--quiet" || "$quiet" == "-q" ]] +} + +should_show_spinner() { + if ! is_quiet; then + return 1 + fi + + if [[ -n "${GITHUB_ACTIONS:-}" ]]; then + return 1 + fi + + return 0 +} + +show_spinner() { + local spinner_marker="$results_dir/run_random_tests_$$.spinner" + touch "$spinner_marker" + + local spinner_chars=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') + local spinner_index=0 + + echo -ne "\033[?25l" >&2 + + ( + while [[ -f "$spinner_marker" ]]; do + echo -ne "\033[2K\r${BLUE}${spinner_chars[$((spinner_index % 10))]} Running tests in parallel...${RESET}" >&2 + ((spinner_index++)) + sleep 0.1 + done + echo -ne "\033[2K\r" >&2 + ) & + + echo "$!" > "${spinner_marker}.pid" +} + +stop_spinner() { + local spinner_marker="$results_dir/run_random_tests_$$.spinner" + + if [[ ! -f "$spinner_marker" ]]; then + echo -ne "\033[?25h" >&2 + return + fi + + rm -f "$spinner_marker" + echo -ne "\033[2K\r" >&2 + + if [[ -f "${spinner_marker}.pid" ]]; then + kill "$(cat "${spinner_marker}.pid")" 2>/dev/null || true + rm -f "${spinner_marker}.pid" + fi + + wait 2>/dev/null + echo -ne "\033[?25h" >&2 +} + +print_header() { + echo -e "${BLUE}==============================================================================${RESET}" + echo -e "${BLUE}$1${RESET}" + echo -e "${BLUE}==============================================================================${RESET}" +} + +print_success() { + echo -e "${GREEN}✓ $1${RESET}" +} + +print_error() { + echo -e "${RED}✗ $1${RESET}" +} + +print_warning() { + echo -e "${YELLOW}⚠ $1${RESET}" +} + +print_debug() { + if ! is_quiet; then + echo -e "${BLUE}🔧 $1${RESET}" + fi +} + +inflect() { + local count=$1 + local singular=$2 + local plural=${3:-${singular}s} + + if [[ "$count" -eq 1 ]]; then + echo "$singular" + return + fi + + echo "$plural" +} + +generate_phpunit_random_seed() { + local seed=$(date +%s) + + if [[ ! "$seed" =~ ^[0-9]+$ ]]; then + echo 1 + return + fi + + echo $((seed + (RANDOM % 1000))) +} + +extract_test_order() { + local events_file="$1" + + if [[ ! -f "$events_file" ]]; then + return + fi + + while IFS= read -r line; do + if [[ "$line" =~ ^Test\ Prepared\ \((.*)\)$ ]]; then + echo "${BASH_REMATCH[1]}" + fi + done < "$events_file" +} + +get_failed_test_predecessor() { + local events_file="$1" + + if [[ ! -f "$events_file" ]]; then + return + fi + + local failed_test="" + while IFS= read -r line; do + if [[ "$line" =~ ^Test\ Failed\ \((.*)\)$ ]] \ + || [[ "$line" =~ ^Test\ Errored\ \((.*)\)$ ]] \ + || [[ "$line" =~ ^Test\ Considered\ Risky\ \((.*)\)$ ]] \ + || [[ "$line" =~ ^Test\ Triggered\ Warning\ \((.*)\)$ ]] \ + || [[ "$line" =~ ^Test\ Triggered\ PHP\ Warning\ \((.*)\)$ ]] \ + || [[ "$line" =~ ^Test\ Triggered\ PHP\ Notice\ \((.*)\)$ ]] \ + || [[ "$line" =~ ^Test\ Triggered\ PHP\ Deprecation\ \((.*)\)$ ]] \ + || [[ "$line" =~ ^Test\ Triggered\ PHP\ Unit\ Deprecation\ \((.*)\)$ ]]; then + failed_test="${BASH_REMATCH[1]}" + break + fi + done < "$events_file" + + if [[ -z "$failed_test" ]]; then + return + fi + + local previous_test="" + while IFS= read -r line; do + if [[ "$line" =~ ^Test\ Prepared\ \((.*)\)$ ]]; then + local current_test="${BASH_REMATCH[1]}" + if [[ "$current_test" == "$failed_test" ]]; then + echo "$failed_test|$previous_test" + return + fi + previous_test="$current_test" + fi + done < "$events_file" + + echo "$failed_test|" +} + +print_result() { + local type=$1 completed=$2 total=$3 component=$4 elapsed_str=$5 + local padded=$(printf "%${#total}d" "$completed") + local color symbol + + case "$type" in + success) color=$GREEN; symbol="✓" ;; + failure) color=$RED; symbol="✗" ;; + warning) color=$YELLOW; symbol="⚠" ;; + esac + + echo -e "${BOLD_YELLOW}[${padded}/${total}]${RESET} ${color}${symbol}${RESET} Component: ${BLUE}$component${RESET} ${YELLOW}($elapsed_str)${RESET}" +} + +format_elapsed_time() { + local elapsed=$1 + + if ! [[ "$elapsed" =~ ^[0-9]+$ ]]; then + echo "N/A" + return + fi + + if [[ $elapsed -ge 60000 ]]; then + printf "%dm %.2fs" "$((elapsed / 60000))" "$(echo "scale=2; ($elapsed % 60000) / 1000" | bc)" + elif [[ $elapsed -ge 1000 ]]; then + printf "%.2fs" "$(echo "scale=2; $elapsed / 1000" | bc)" + else + echo "${elapsed}ms" + fi +} + +################################################################################ +# CONFIGURATION & VALIDATION +################################################################################ + +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + show_help + exit 0 + ;; + -q|--quiet) + quiet="--quiet" + shift + ;; + -c|--component) + if [[ $# -lt 2 ]]; then + print_error "Missing value for --component" + exit 1 + fi + + component="$2" + shift 2 + ;; + -n|--max-jobs) + if [[ $# -lt 2 ]]; then + print_error "Missing value for --max-jobs" + exit 1 + fi + + if [[ ! "$2" =~ ^[1-9][0-9]*$ ]]; then + print_error "--max-jobs must be a positive integer" + exit 1 + fi + + max_jobs="$2" + shift 2 + ;; + -r|--repeat) + if [[ $# -lt 2 ]]; then + print_error "Missing value for --repeat" + exit 1 + fi + + if [[ ! "$2" =~ ^[1-9][0-9]*$ ]]; then + print_error "--repeat must be a positive integer" + exit 1 + fi + + repeat_count="$2" + shift 2 + ;; + -t|--timeout) + if [[ $# -lt 2 ]]; then + print_error "Missing value for --timeout" + exit 1 + fi + + if [[ ! "$2" =~ ^[0-9]+$ ]]; then + print_error "--timeout must be a non-negative integer" + exit 1 + fi + + timeout_seconds="$2" + shift 2 + ;; + *) + print_error "Unknown option '$1'" + exit 1 + ;; + esac + done +} + +show_help() { + echo "CodeIgniter4 - Random Test Execution Verification" + echo "" + echo -e "${YELLOW}Usage:${RESET}" + echo -e " ${GREEN}$0${RESET} [options]" + echo "" + echo -e "${YELLOW}Options:${RESET}" + echo -e " ${GREEN}-q, --quiet${RESET} Suppress debug output" + echo -e " ${GREEN}-c, --component COMPONENT${RESET} Test single ${GREEN}COMPONENT${RESET} (overrides config file)" + echo -e " ${GREEN}-n, --max-jobs MAX_JOBS${RESET} Limit concurrent test jobs (auto-detect if omitted)" + echo -e " ${GREEN}-r, --repeat REPEAT${RESET} Repeat full component run ${GREEN}REPEAT${RESET} times" + echo -e " ${GREEN}-t, --timeout TIMEOUT${RESET} Per-component ${GREEN}TIMEOUT${RESET} in seconds (0 disables, default: 300)" + echo -e " ${GREEN}-h, --help${RESET} Show this help message" +} + +auto_detect_max_jobs() { + if command -v nproc &>/dev/null; then + nproc + return + fi + + if command -v sysctl &>/dev/null; then + sysctl -n hw.ncpu 2>/dev/null + return + fi + + echo 4 +} + +check_required_tools() { + local -a missing_tools=() + local -a required_tools=(date find sort sed grep bc) + local tool + + for tool in "${required_tools[@]}"; do + if ! command -v "$tool" >/dev/null 2>&1; then + missing_tools+=("$tool") + fi + done + + if [[ $timeout_seconds -gt 0 ]]; then + if ! command -v timeout >/dev/null 2>&1; then + if ! command -v gtimeout >/dev/null 2>&1; then + if ! command -v pgrep >/dev/null 2>&1; then + missing_tools+=("pgrep") + fi + + if ! command -v pkill >/dev/null 2>&1; then + missing_tools+=("pkill") + fi + fi + fi + fi + + if [[ ! -x "$project_root/vendor/bin/phpunit" ]]; then + print_error "PHPUnit executable not found or not executable: $project_root/vendor/bin/phpunit" + echo "Run composer install before running this script." + exit 1 + fi + + if [[ ${#missing_tools[@]} -gt 0 ]]; then + print_error "Missing required command(s): ${missing_tools[*]}" + echo "Install the missing tools and re-run this script." + exit 1 + fi +} + +verify_config() { + print_debug "Verifying configuration file: $config_file" + + if [[ ! -f "$config_file" ]]; then + print_error "Configuration file not found: $config_file" + echo "Please create $config_file with a list of components, one per line." + exit 1 + fi +} + +validate_component_name() { + local component="$1" + + # Only allow alphanumeric characters, hyphens, and underscores + # Reject path traversal attempts and command injection characters + if [[ ! "$component" =~ ^[a-zA-Z0-9_-]+$ ]]; then + return 1 + fi + + return 0 +} + +read_components() { + declare -a components=() + + while IFS= read -r line; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + + if [[ -z "$line" || "$line" =~ ^# ]]; then + continue + fi + + # Validate component name for security + if ! validate_component_name "$line"; then + print_warning "Skipping invalid/unsafe component name: $line" + continue + fi + + components+=("$line") + done < "$config_file" + + echo "${components[@]}" +} + +################################################################################ +# TEST EXECUTION +################################################################################ + +run_component_tests() { + local component=$1 + + # Security: Validate component name before use + if ! validate_component_name "$component"; then + print_error "Security: Invalid component name rejected: $component" + return 1 + fi + + local test_dir="tests/system/$component" + local start_time=$(date +%s%N) + + print_debug "Running tests for: $component" + + if [[ ! -d "$test_dir" ]]; then + local elapsed=$((($(date +%s%N) - $start_time) / 1000000)) + { + echo "Exit code: 2" + echo "Elapsed time: $elapsed" + } > "$results_dir/random_test_result_${elapsed}_${component}.txt" + return + fi + + local output_file="$results_dir/random_test_output_${component}_$$.log" + local events_file="$results_dir/random_test_events_${component}_$$.log" + local exit_code=0 + local attempt=1 + local -r max_attempts=2 + local random_seed + local -a phpunit_args + + # Retry loop: the Composer classmap autoloader occasionally fails to load + # CodeIgniter\CodeIgniter under parallel CI load — a transient infra race, + # not a real test failure. Retry once on that signature with a fresh random + # seed; a second miss is reported as genuine failure. + while true; do + random_seed=$(generate_phpunit_random_seed) + + # Security: Use array to avoid eval and prevent command injection + phpunit_args=( + "vendor/bin/phpunit" + "$test_dir" + "--colors=never" + "--no-coverage" + "--do-not-cache-result" + "--order-by=random" + "--random-order-seed=${random_seed}" + "--log-events-text" + "$events_file" + ) + + if [[ $timeout_seconds -gt 0 ]] && command -v timeout >/dev/null 2>&1; then + (cd "$project_root" && timeout --kill-after=2s "${timeout_seconds}s" "${phpunit_args[@]}") > "$output_file" 2>&1 + exit_code=$? + elif [[ $timeout_seconds -gt 0 ]] && command -v gtimeout >/dev/null 2>&1; then + (cd "$project_root" && gtimeout --kill-after=2s "${timeout_seconds}s" "${phpunit_args[@]}") > "$output_file" 2>&1 + exit_code=$? + else + local timeout_marker="$output_file.timeout" + (cd "$project_root" && "${phpunit_args[@]}") > "$output_file" 2>&1 & + local test_pid=$! + + if [[ $timeout_seconds -gt 0 ]]; then + # Watchdog: monitors test process and kills it after timeout + # Uses 1-second sleep intervals to respond quickly when test finishes early + ( + local elapsed=0 + while [[ $elapsed -lt $timeout_seconds ]]; do + sleep 1 + elapsed=$((elapsed + 1)) + kill -0 "$test_pid" 2>/dev/null || exit 0 + done + + if kill -0 "$test_pid" 2>/dev/null; then + touch "$timeout_marker" + local pids_to_kill=$(pgrep -P "$test_pid" 2>/dev/null) + + kill -TERM "$test_pid" 2>/dev/null || true + if [[ -n "$pids_to_kill" ]]; then + echo "$pids_to_kill" | xargs kill -TERM 2>/dev/null || true + fi + + sleep 2 + + if kill -0 "$test_pid" 2>/dev/null; then + kill -KILL "$test_pid" 2>/dev/null || true + if [[ -n "$pids_to_kill" ]]; then + echo "$pids_to_kill" | xargs kill -KILL 2>/dev/null || true + fi + # Security: Quote and escape test_dir for safe pattern matching + pkill -KILL -f "phpunit.*${test_dir//\//\\/}" 2>/dev/null || true + fi + fi + ) & + disown $! 2>/dev/null || true + fi + + wait "$test_pid" 2>/dev/null + exit_code=$? + + if [[ -f "$timeout_marker" ]]; then + exit_code=124 + rm -f "$timeout_marker" + elif [[ $exit_code -eq 143 || $exit_code -eq 137 ]]; then + exit_code=124 + fi + fi + + # Success, exhausted attempts, or a non-infra failure: stop retrying. + if [[ $exit_code -eq 0 ]] || [[ $attempt -ge $max_attempts ]]; then + break + fi + + # Only retry on the known transient autoload race signatures. + # Matching on error messages (not line numbers) so the pattern survives + # unrelated edits to MockCodeIgniter/CIUnitTestCase. + if ! grep -qE 'Failed to open stream: No such file or directory|Class "CodeIgniter.CodeIgniter" not found' "$output_file" 2>/dev/null; then + break + fi + + print_debug "Transient autoload failure detected in $component; retrying (attempt $((attempt + 1))/${max_attempts})" + ((attempt++)) + rm -f "$events_file" + done + + local elapsed=$((($(date +%s%N) - $start_time) / 1000000)) + local result_file="$results_dir/random_test_result_${elapsed}_${component}.txt" + local order_file="$results_dir/random_test_order_${elapsed}_${component}.txt" + + if [[ -f "$events_file" ]]; then + extract_test_order "$events_file" > "$order_file" + else + echo "Execution order unavailable (events file not created)." > "$order_file" + fi + + if [[ $exit_code -eq 0 ]]; then + { + echo "Exit code: 0" + echo "Elapsed time: $elapsed" + } > "$result_file" + rm -f "$output_file" "$events_file" "$order_file" + else + local output="" + if [[ -f "$output_file" ]]; then + output=$(cat "$output_file") + fi + + if [[ $exit_code -eq 124 ]]; then + output+=$'\n\nTest timed out after '"${timeout_seconds}s" + fi + + local predecessor_info=$'\nExecution order file: '"${order_file}" + if [[ $exit_code -eq 124 ]]; then + predecessor_info+=$'\nFailed test: (timeout before PHPUnit emitted failure event)' + if [[ -f "$events_file" ]]; then + local last_prepared_test=$(extract_test_order "$events_file" | tail -n 1) + if [[ -n "$last_prepared_test" ]]; then + predecessor_info+=$'\nLast prepared test before timeout: '"${last_prepared_test}" + else + predecessor_info+=$'\nLast prepared test before timeout: (unavailable)' + fi + else + predecessor_info+=$'\nLast prepared test before timeout: (events file unavailable)' + fi + predecessor_info+=$'\nPrevious test: (unavailable due to timeout)' + else + if [[ -f "$events_file" ]]; then + local predecessor_result=$(get_failed_test_predecessor "$events_file") + if [[ -n "$predecessor_result" ]]; then + local previous_test=${predecessor_result#*|} + predecessor_info+=$'\nFailed test: '"${predecessor_result%%|*}" + if [[ -n "$previous_test" ]]; then + predecessor_info+=$'\nPrevious test: '"${previous_test}" + else + predecessor_info+=$'\nPrevious test: (none - failed test ran first)' + fi + else + predecessor_info+=$'\nFailed test: (not detected from PHPUnit events log)' + predecessor_info+=$'\nPrevious test: (unavailable)' + fi + else + predecessor_info+=$'\nFailed test: (events file unavailable)' + predecessor_info+=$'\nPrevious test: (unavailable)' + fi + fi + + { + echo "> ${phpunit_args[@]:0:7}" + echo "" + echo "$output" + echo "$predecessor_info" + echo "" + echo "Exit code: 1" + echo "Elapsed time: $elapsed" + } > "$result_file" + rm -f "$output_file" "$events_file" + fi +} + +cleanup_finished_pids() { + local -a active=() + for pid in "${bg_pids[@]:-}"; do + if kill -0 "$pid" 2>/dev/null; then + active+=("$pid") + fi + done + bg_pids=("${active[@]:-}") +} + +spawn_limited_job() { + local component=$1 + + cleanup_finished_pids + + while [[ ${#bg_pids[@]} -ge $max_jobs ]]; do + sleep 0.05 + cleanup_finished_pids + done + + run_component_tests "$component" & + bg_pids+=($!) +} + +process_result() { + local component=$1 + local elapsed=$2 + local result_file="$results_dir/random_test_result_${elapsed}_${component}.txt" + + if [[ ! -f "$result_file" ]]; then + return 1 + fi + + ((completed++)) + + if [[ "$first_result" == true ]] && ! is_quiet; then + echo "" + first_result=false + fi + + local status=$(grep "^Exit code:" "$result_file" | sed 's/Exit code: //') + local elapsed_str=$(format_elapsed_time "$elapsed") + + case "$status" in + 0) + ((passed++)) + print_result "success" "$completed" "$total" "$component" "$elapsed_str" + rm -f "$result_file" + ;; + 2) + ((skipped++)) + skipped_components+=("$component") + print_result "warning" "$completed" "$total" "$component" "$elapsed_str" + rm -f "$result_file" + ;; + *) + ((failed++)) + failed_components+=("$component") + print_result "failure" "$completed" "$total" "$component" "$elapsed_str" + ;; + esac + + displayed_components+=("$component") + + return 0 +} + +get_completed_components() { + # Returns unprocessed test results sorted by elapsed time (fastest first) + # Filename format: random_test_result_${elapsed}_${component}.txt + # Output format: "component|elapsed" (one per line) + + # Extract and sort files by elapsed time + local -a entries=() + + while IFS= read -r file_path; do + # Remove prefix: random_test_result_ + local temp=$(basename "$file_path") + temp=${temp#random_test_result_} + + # Extract elapsed time (everything before first underscore after number) + local elapsed=${temp%%_*} + + # Validate elapsed is numeric + if [[ ! "$elapsed" =~ ^[0-9]+$ ]]; then + continue + fi + + # Extract component (everything after elapsed and underscore, before .txt) + local listed_component=${temp#${elapsed}_} + listed_component=${listed_component%.txt} + + entries+=("$elapsed|$listed_component") + done < <(find "$results_dir" -maxdepth 1 -type f -name "random_test_result_*.txt" 2>/dev/null) + + # Sort entries by elapsed time numerically + printf '%s\n' "${entries[@]}" | sort -t'|' -k1,1n | + while IFS='|' read -r elapsed listed_component; do + if [[ ! " ${displayed_components[*]:-} " =~ " ${listed_component} " ]]; then + echo "$listed_component|$elapsed" + fi + done +} + + +print_summary() { + local run_number=$1 + local pass_percent=0.00 + local fail_percent=0.00 + local skip_percent=0.00 + + if [[ $total -gt 0 ]]; then + pass_percent=$(printf "%.2f" "$(echo "scale=2; $passed * 100 / $total" | bc)") + fail_percent=$(printf "%.2f" "$(echo "scale=2; $failed * 100 / $total" | bc)") + skip_percent=$(printf "%.2f" "$(echo "scale=2; $skipped * 100 / $total" | bc)") + fi + + echo "" + print_header "Test Execution Summary" + printf "%-20s %b\n" "Total $(inflect "$total" "Component" "Components"):" "${BLUE}$total${RESET}" + printf "%-20s %b\n" "Passed:" "${GREEN}$passed${RESET} (${GREEN}${pass_percent}%${RESET})" + printf "%-20s %b\n" "Failed:" "${RED}$failed${RESET} (${RED}${fail_percent}%${RESET})" + printf "%-20s %b\n" "Skipped:" "${YELLOW}$skipped${RESET} (${YELLOW}${skip_percent}%${RESET})" + printf "%-20s %b\n" "Completed Runs:" "${BLUE}$run_number${RESET} / ${BLUE}$repeat_count${RESET}" + + if [[ $failed -gt 0 ]]; then + echo -e "\n${BOLD_RED}Failed $(inflect "$failed" "Component" "Components"):${RESET}" + while IFS= read -r failed_component; do + local result_file=$(find "$results_dir" -name "random_test_result_*_${failed_component}.txt" 2>/dev/null | head -n 1) + + if [[ -z "$result_file" ]]; then + result_file="$results_dir/random_test_result_*_${failed_component}.txt" + fi + + echo -e " ${RED}✗${RESET} ${BOLD}$failed_component${RESET} ($result_file)" + done < <(printf '%s\n' "${failed_components[@]}" | sort) + fi + + if [[ $skipped -gt 0 ]]; then + echo -e "\n${BOLD_YELLOW}Skipped $(inflect "$skipped" "Component" "Components"):${RESET}" + while IFS= read -r skipped_component; do + echo -e " ${YELLOW}⚠${RESET} ${BOLD}$skipped_component${RESET} (no such directory: ${BOLD}tests/system/$skipped_component${RESET})" + done < <(printf '%s\n' "${skipped_components[@]}" | sort) + fi +} + +################################################################################ +# MAIN SCRIPT +################################################################################ + +main() { + cd "$project_root" || exit 1 + + parse_arguments "$@" + check_required_tools + + if [[ -z "$max_jobs" ]]; then + max_jobs=$(auto_detect_max_jobs) + fi + + print_header "CodeIgniter4 - Random Test Execution Verification" + echo "" + + declare -a components_array + + if [[ -n "$component" ]]; then + # Single component specified via command line + if ! validate_component_name "$component"; then + print_error "Invalid component name: $component" + echo " Component name must contain only: alphanumeric, hyphens, underscores, forward slashes" + echo " Cannot contain: spaces, dots, consecutive slashes, or start with slash" + exit 1 + fi + print_debug "Testing single component specified via command line: $component\n" + components_array=("$component") + else + # Read components from config file + verify_config + print_success "Configuration file: $config_file\n" + components_array=($(read_components)) + fi + + total=${#components_array[@]} + + if [[ $total -eq 0 ]]; then + if [[ -n "$component" ]]; then + print_error "Component not found or inaccessible: $component" + else + print_warning "No components configured in $config_file" + echo "Please add component names to the configuration file, one per line." + fi + exit 0 + fi + + print_debug "Found $total $(inflect "$total" "component") to test" + print_debug "Max concurrent jobs: $max_jobs" + print_debug "Per-component timeout: ${timeout_seconds}s" + print_debug "Total runs: $repeat_count\n" + + local run=1 + while [[ $run -le $repeat_count ]]; do + completed=0 + passed=0 + failed=0 + skipped=0 + displayed_components=() + failed_components=() + skipped_components=() + bg_pids=() + first_result=true + + if [[ $run -gt 1 ]]; then + echo "" + fi + + if [[ $repeat_count -gt 1 ]]; then + print_header "Run $run/$repeat_count" + echo "" + fi + + print_debug "Setting up results directory: $results_dir\n" + mkdir -p "$results_dir" + rm -f "$results_dir"/* + + if ! should_show_spinner; then + echo -e "${BLUE}Running tests in parallel...${RESET}\n" + else + show_spinner + fi + + for next_component in "${components_array[@]}"; do + spawn_limited_job "$next_component" + done + + for finished_pid in "${bg_pids[@]:-}"; do + wait "$finished_pid" 2>/dev/null || true + done + + if should_show_spinner; then + stop_spinner + fi + + while IFS='|' read -r next_component next_elapsed; do + process_result "$next_component" "$next_elapsed" || true + done < <(get_completed_components) + + print_summary "$run" + + if [[ $failed -gt 0 || $skipped -gt 0 ]]; then + exit 1 + fi + + ((run++)) + done + + echo "" + if [[ -n "$component" ]]; then + print_success "Component '$component' passed random execution tests!" + else + print_success "All components passed random execution tests!" + fi +} + +main "$@" diff --git a/.github/scripts/secure-git-push b/.github/scripts/secure-git-push new file mode 100755 index 000000000000..217581f0a806 --- /dev/null +++ b/.github/scripts/secure-git-push @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [[ $# -ne 2 ]]; then + echo "Usage: secure-git-push " >&2 + exit 1 +fi + +if [[ -z "${PUSH_TOKEN:-}" ]]; then + echo "PUSH_TOKEN is required" >&2 + exit 1 +fi + +REMOTE_URL="$1" +REFSPEC="$2" +AUTH_HEADER="$(printf 'x-access-token:%s' "${PUSH_TOKEN}" | base64 | tr -d '\n')" + +echo "::add-mask::${AUTH_HEADER}" +git -c http.https://github.com/.extraheader="AUTHORIZATION: basic ${AUTH_HEADER}" push "${REMOTE_URL}" "${REFSPEC}" + +unset AUTH_HEADER PUSH_TOKEN diff --git a/.github/scripts/validate-version b/.github/scripts/validate-version new file mode 100644 index 000000000000..411900dedb75 --- /dev/null +++ b/.github/scripts/validate-version @@ -0,0 +1,21 @@ +#!/bin/bash +set -o pipefail + +if [[ -z $1 ]]; then + echo "validate-version requires a version identifier" + exit 1 +fi + +FILES=("system/CodeIgniter.php" "user_guide_src/source/conf.py") +LENGTH="${#FILES[@]}" + +for FILE in "${FILES[@]}"; do + COUNT="$((COUNT + $(grep -c "$FILE" -e "'$1'")))" +done + +if [[ $COUNT -ne $LENGTH ]]; then + echo "CodeIgniter version is not updated to v"$1"" + exit 1 +fi + +echo "CodeIgniter version is updated to v"$1"" diff --git a/.github/skills/README.md b/.github/skills/README.md new file mode 100644 index 000000000000..1c641b9e335a --- /dev/null +++ b/.github/skills/README.md @@ -0,0 +1,25 @@ +# Skills + +This directory contains workspace agent skills for maintainers. + +## Available Skills +- `forum-announcement`: Create CodeIgniter4 release forum announcements in myBB format. + +## Structure +- `.github/skills//SKILL.md`: Skill metadata and workflow. +- `.github/skills//references/`: Optional supporting docs loaded on demand. +- `.github/skills//assets/`: Optional templates and reusable files. +- `.github/skills//scripts/`: Optional executables for automation. + +## Usage +1. Open Copilot Chat in this workspace. +2. Invoke `/`. +3. Provide inputs, for example: +``` +/forum-announcement 4.7.0 +``` + +## Maintainer Notes +- Keep `name` in `SKILL.md` identical to its folder name. +- Keep the `description` keyword-rich so the skill is discoverable. +- Use references only when needed; avoid duplicating guidance between `SKILL.md` and `references/`. diff --git a/.github/skills/forum-announcement/SKILL.md b/.github/skills/forum-announcement/SKILL.md new file mode 100644 index 000000000000..e9e7c731a1a3 --- /dev/null +++ b/.github/skills/forum-announcement/SKILL.md @@ -0,0 +1,112 @@ +--- +name: forum-announcement +description: 'Create CodeIgniter4 release forum announcements in myBB format. Use when preparing release posts, dual-version announcements (maintenance + major), changelog summaries, contributor acknowledgements, upgrade guidance, and final forum-ready text files.' +argument-hint: 'Target release version(s), e.g. "4.6.5 and 4.7.0" or "4.7.1"' +user-invocable: true +--- + +# Forum Announcement + +Create a forum-ready CodeIgniter4 release announcement using myBB markup. + +## When to Use +- Publish a new CodeIgniter4 release announcement on the forum. +- Convert changelog and RST details into a structured, readable post. +- Produce a dual-release announcement with maintenance-first ordering. + +## Inputs +- One version (`4.x.y`) or two versions (`maintenance`, then `major`). + +## Output Naming +- `v{VERSION}` is a placeholder and must be replaced by an actual version string. +- Single release example: `v4.7.1-announcement.txt`. +- Dual release example (`4.6.5` + `4.7.0`): use the major version filename `v4.7.0-announcement.txt`. +- Save the file at repository root. + +## Procedure +1. Read `CHANGELOG.md` for release summary and PR references. +2. Read `user_guide_src/source/changelogs/v{VERSION}.rst` for each target version. +3. Determine release mode: + - Single release: one `4.x.y` version. + - Dual release: maintenance + major; maintenance is presented first in the post. +4. Draft the post with this structure (remove sections that do not apply): + - Title. + - Intro paragraphs. + - Release links. + - Which version to use (dual release only). + - Maintenance release section (dual release only). + - Highlights and new features. + - Notable enhancements. + - Security and quality. + - Breaking changes. + - Other notable changes. + - Contributor thanks. + - Upgrade links, issue reporting link, closing. + - AI-assistance disclosure. +5. Apply myBB formatting and escaping rules in this file. +6. Save final content as `v{VERSION}-announcement.txt` at repository root, replacing `{VERSION}` with the actual version. + +## Required Announcement Template +```text +[size=x-large][b]CodeIgniter {VERSION} Released![/b][/size] + +Introduction paragraph(s) + +[url=RELEASE_URL]GitHub Release[/url] +[url=CHANGELOG_URL]Changelog[/url] + +[hr] + +[size=large][b]Highlights & New Features[/b][/size] + +[hr] + +[size=large][b]Notable Enhancements[/b][/size] + +[hr] + +[size=large][b]Breaking Changes[/b][/size] + +[hr] + +[size=large][b]Thanks to Our Contributors[/b][/size] + +[hr] + +[url=UPGRADE_GUIDE_URL]Upgrade Guide[/url] +[url=ISSUES_URL]Report Issues[/url] + +[hr] + +[size=small][i]Note: This announcement was created with the assistance of GitHub Copilot (GPT-5.3-Codex).[/i][/size] +``` + +For dual releases, adapt the title and body to include both versions, with maintenance version first, plus a dedicated "Which Version Should I Use?" section. + +## myBB Rules +- Use `[b]`, `[i]`, `[size=x-large]`, `[size=large]`, `[size=small]`, `[url=...]`, `[list]`, `[list=1]`, `[*]`, and `[hr]`. +- Use double backticks for inline code-like text when needed. +- Escape colon patterns that may trigger myBB emoticons by replacing `:` with `:` in code-like snippets. + +## Content Quality Rules +- Tone is professional, friendly, and community-facing. +- Do not use emojis. +- Verify details against RST changelog entries before finalizing. +- Explain breaking changes with old behavior versus new behavior. +- Include a "Removed Deprecated Items" subsection when applicable. +- For maintenance bug-fix summaries, prefer ordered lists with concise before/after statements. +- Thank contributors by GitHub username. +- Include upgrade links and issue reporting links. + +## Version Guidance Rules +- Dual releases must list maintenance before major. +- The "Which Version Should I Use?" section is required for dual releases. +- In version guidance, present the lower-PHP-support option first. +- For single releases, omit the "Which Version Should I Use?" section. + +## Self-Check +- Maintenance version is presented before major version. +- PHP-version guidance and upgrade links are included. +- Breaking changes are clearly explained. +- Final AI-assistance disclosure is present and should be adjusted to the actual AI model used if different from the template. +- Output filename is concrete and does not include braces. diff --git a/.github/workflows/deploy-apidocs.yml b/.github/workflows/deploy-apidocs.yml new file mode 100644 index 000000000000..8eb59179a946 --- /dev/null +++ b/.github/workflows/deploy-apidocs.yml @@ -0,0 +1,83 @@ +# When changes are pushed to the develop branch, +# build the current version of the API documentation +# with phpDocumentor and deploy it to codeigniter4/api. +name: Deploy API Documentation + +on: + push: + branches: + - 'develop' + paths: + - '.github/scripts/secure-git-push' + - '.github/workflows/deploy-apidocs.yml' + - 'system/**' + +permissions: + contents: read + +jobs: + build: + name: Deploy to api + permissions: + contents: write + if: github.repository == 'codeigniter4/CodeIgniter4' + runs-on: ubuntu-24.04 + + steps: + - name: Setup credentials + run: | + git config --global user.email "action@github.com" + git config --global user.name "${GITHUB_ACTOR}" + + - name: Checkout source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: source + persist-credentials: false + + - name: Checkout target + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: codeigniter4/api + token: ${{ secrets.ACCESS_TOKEN }} + path: api + persist-credentials: false + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: '8.2' + tools: phive + coverage: none + + - name: Download latest phpDocumentor + working-directory: source + run: phive --no-progress install --trust-gpg-keys 6DA3ACC4991FFAE5 phpDocumentor + + - name: Prepare API repo + working-directory: api + run: | + git reset --hard master + rm -rfv docs + mkdir --parents --verbose docs + + - name: Build API in source repo + working-directory: source + run: | + php tools/phpDocumentor --ansi --verbose + cp -R ${GITHUB_WORKSPACE}/source/api/build/* ${GITHUB_WORKSPACE}/api/docs + + - name: Deploy to API repo + working-directory: api + env: + PUSH_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: | + git add . + + if ! git diff-index --quiet HEAD; then + git commit -m "Updated API for commit ${GITHUB_SHA}" + bash "${GITHUB_WORKSPACE}/source/.github/scripts/secure-git-push" https://github.com/codeigniter4/api.git HEAD:master + echo "API documentation deployed successfully." + else + echo "No changes to deploy." + fi diff --git a/.github/workflows/deploy-distributables.yml b/.github/workflows/deploy-distributables.yml new file mode 100644 index 000000000000..1eeb86f772cb --- /dev/null +++ b/.github/workflows/deploy-distributables.yml @@ -0,0 +1,207 @@ +# When a new release is created, deploy relevant +# files to each of the generated repos. +name: Deploy Distributable Repos + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + check-version: + name: Check for updated version + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 # fetch all tags + persist-credentials: false + + - name: Get latest version + id: version + run: | + echo 'LATEST_VERSION<> $GITHUB_OUTPUT + echo $(git describe --tags --abbrev=0) | sed "s/v//" >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT + + - name: Search for updated version + if: ${{ steps.version.outputs.LATEST_VERSION }} + run: | + chmod +x ${GITHUB_WORKSPACE}/.github/scripts/validate-version + ${GITHUB_WORKSPACE}/.github/scripts/validate-version ${{ steps.version.outputs.LATEST_VERSION }} + + framework: + name: Deploy to framework + permissions: + # Allow actions/github-script to create release + contents: write + if: github.repository == 'codeigniter4/CodeIgniter4' + runs-on: ubuntu-24.04 + needs: check-version + + steps: + - name: Identify + run: | + git config --global user.email "action@github.com" + git config --global user.name "${GITHUB_ACTOR}" + + - name: Checkout source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: source + persist-credentials: false + + - name: Checkout target + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: codeigniter4/framework + token: ${{ secrets.ACCESS_TOKEN }} + path: framework + persist-credentials: false + + - name: Chmod + run: chmod +x ./source/.github/scripts/deploy-framework + + - name: Deploy + env: + PUSH_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: ./source/.github/scripts/deploy-framework ${GITHUB_WORKSPACE}/source ${GITHUB_WORKSPACE}/framework ${GITHUB_REF##*/} + + - name: Release + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{secrets.ACCESS_TOKEN}} + script: | + const release = await github.rest.repos.getLatestRelease({ + owner: context.repo.owner, + repo: context.repo.repo + }) + github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: 'framework', + tag_name: release.data.tag_name, + name: release.data.name, + body: release.data.body + }) + + appstarter: + name: Deploy to appstarter + permissions: + # Allow actions/github-script to create release + contents: write + if: github.repository == 'codeigniter4/CodeIgniter4' + runs-on: ubuntu-24.04 + needs: check-version + + steps: + - name: Identify + run: | + git config --global user.email "action@github.com" + git config --global user.name "${GITHUB_ACTOR}" + + - name: Checkout source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: source + persist-credentials: false + + - name: Checkout target + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: codeigniter4/appstarter + token: ${{ secrets.ACCESS_TOKEN }} + path: appstarter + persist-credentials: false + + - name: Chmod + run: chmod +x ./source/.github/scripts/deploy-appstarter + + - name: Deploy + env: + PUSH_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: ./source/.github/scripts/deploy-appstarter ${GITHUB_WORKSPACE}/source ${GITHUB_WORKSPACE}/appstarter ${GITHUB_REF##*/} + + - name: Release + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{secrets.ACCESS_TOKEN}} + script: | + const release = await github.rest.repos.getLatestRelease({ + owner: context.repo.owner, + repo: context.repo.repo + }) + github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: 'appstarter', + tag_name: release.data.tag_name, + name: release.data.name, + body: release.data.body + }) + + userguide: + name: Deploy to userguide + permissions: + # Allow actions/github-script to create release + contents: write + if: github.repository == 'codeigniter4/CodeIgniter4' + runs-on: ubuntu-24.04 + needs: check-version + + steps: + - name: Identify + run: | + git config --global user.email "action@github.com" + git config --global user.name "${GITHUB_ACTOR}" + + - name: Checkout source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: source + persist-credentials: false + + - name: Checkout target + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: codeigniter4/userguide + token: ${{ secrets.ACCESS_TOKEN }} + path: userguide + persist-credentials: false + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + + - name: Install Sphinx + run: | + python -m pip install --upgrade pip + pip install -r ./source/user_guide_src/requirements.txt + + - name: Chmod + run: chmod +x ./source/.github/scripts/deploy-userguide + + - name: Deploy + env: + PUSH_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: ./source/.github/scripts/deploy-userguide ${GITHUB_WORKSPACE}/source ${GITHUB_WORKSPACE}/userguide ${GITHUB_REF##*/} + + - name: Release + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{secrets.ACCESS_TOKEN}} + script: | + const release = await github.rest.repos.getLatestRelease({ + owner: context.repo.owner, + repo: context.repo.repo + }) + github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: 'userguide', + tag_name: release.data.tag_name, + name: release.data.name, + body: release.data.body + }) diff --git a/.github/workflows/deploy-userguide-latest.yml b/.github/workflows/deploy-userguide-latest.yml new file mode 100644 index 000000000000..c943254325d6 --- /dev/null +++ b/.github/workflows/deploy-userguide-latest.yml @@ -0,0 +1,83 @@ +# When changes are pushed to the develop branch, +# build the current version of the User Guide +# with Sphinx and deploy it to the gh-pages branch. +# +# @todo Consolidate checkouts +name: Deploy User Guide (latest) + +on: + push: + branches: + - 'develop' + paths: + - 'user_guide_src/**' + +permissions: + contents: read + +jobs: + build: + name: Deploy to gh-pages + permissions: + # Allow push to branch gh-pages + contents: write + if: (github.repository == 'codeigniter4/CodeIgniter4') + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: '8.2' + coverage: none + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + + - name: Install Sphinx + run: | + python -m pip install --upgrade pip + pip install -r user_guide_src/requirements.txt + + # Build the latest User Guide + - name: Build Docs with Sphinx + run: make html + working-directory: user_guide_src + + - name: Add "Edit this page" links + run: | + cd user_guide_src + # Fix permissions + sudo chown -R runner:docker build/html/ + php add-edit-this-page build/html/ + + # Create an artifact of the html output + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: HTML Documentation + path: user_guide_src/build/html/ + + # Commit changes to the gh-pages branch + - name: Commit changes + run: | + git clone https://github.com/codeigniter4/CodeIgniter4.git --branch gh-pages --single-branch gh-pages + cp -r user_guide_src/build/html/* gh-pages/ + cd gh-pages + git config --local user.email "action@github.com" + git config --local user.name "${GITHUB_ACTOR}" + git add . + # Ignore failures due to lack of changes + git commit -m "Update User Guide" -a || true + + - name: Push changes + working-directory: gh-pages + env: + PUSH_TOKEN: ${{ secrets.ACCESS_TOKEN }} + run: bash ${GITHUB_WORKSPACE}/.github/scripts/secure-git-push https://github.com/codeigniter4/CodeIgniter4.git HEAD:gh-pages diff --git a/.github/workflows/label-add-conflict-all-pr.yml b/.github/workflows/label-add-conflict-all-pr.yml new file mode 100644 index 000000000000..f9adde8207de --- /dev/null +++ b/.github/workflows/label-add-conflict-all-pr.yml @@ -0,0 +1,51 @@ +name: Auto Label "stale" for All PRs + +on: + push: + branches: + - develop + - '4.*' + +jobs: + build: + name: Check Conflicts + + permissions: + contents: read + pull-requests: write + + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Get PR List + id: PR-list + run: echo "pr_list=$(gh pr list -L 100 --json mergeable,url,labels,author)" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: 'Add label "stale" and comment' + env: + PR_LIST: ${{ steps.PR-list.outputs.pr_list }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + + IFS=$'\n' # Set Internal Field Separator to newline to handle array elements + + # Iterate through the PRs in PR_LIST + for pr in $(echo "$PR_LIST" | jq -c '.[]'); do + mergeable=$(echo "$pr" | jq -r '.mergeable') + author=$(echo "$pr" | jq -r '.author.login') + labels=$(echo "$pr" | jq -c '.labels[].name' | tr -d '[]"') + url=$(echo "$pr" | jq -r '.url') + + # CONFLICTING and no 'stale' label + if [ "$mergeable" == "CONFLICTING" ] && [[ ! "$labels" == *"stale"* ]]; then + # Add "stale" label + gh pr edit $url --add-label "stale" + + # Add a comment + gh pr comment $url --body ":wave: Hi, @$author!

We detected conflicts in your PR against the base branch :speak_no_evil:
You may want to sync :arrows_counterclockwise: your branch with upstream!

Ref: [Syncing Your Branch](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/workflow.md#updating-your-branch)" + fi + done diff --git a/.github/workflows/label-pr.yml b/.github/workflows/label-pr.yml new file mode 100644 index 000000000000..bb84e2ad3504 --- /dev/null +++ b/.github/workflows/label-pr.yml @@ -0,0 +1,47 @@ +name: Add Labels to PRs + +# NOTE: When updating this workflow, you should first change the event to `pull_request` to test the changes +# in a PR, and then change it back to `pull_request_target` before merging. +# @see https://github.com/actions/labeler?tab=readme-ov-file#updating-major-version-of-the-labeler +on: + - pull_request_target + +jobs: + add-labels: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Verify PR source for workflow file changes + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const prFiles = await github.paginate(github.rest.pulls.listFiles.endpoint.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + })); + const workflowFileChanged = prFiles.some(file => file.filename === '.github/workflows/label-pr.yml'); + + if (workflowFileChanged) { + if (context.payload.pull_request.head.repo.full_name !== 'codeigniter4/CodeIgniter4') { + throw new Error('Changes to label-pr.yml are not allowed from forks.'); + } + + console.log('Workflow file changed, but PR is from the main repository. Proceeding with label addition.'); + return; + } + + console.log('No changes to workflow file detected, proceeding with label addition.'); + + - name: Add labels + uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0 + with: + sync-labels: true # Remove labels when matching files are reverted diff --git a/.github/workflows/label-signing.yml b/.github/workflows/label-signing.yml new file mode 100644 index 000000000000..609e84a0a23e --- /dev/null +++ b/.github/workflows/label-signing.yml @@ -0,0 +1,32 @@ +name: Check Signed PR +on: + pull_request: + branches: + - 'develop' + - '4.*' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + +jobs: + build: + name: Check Signed Commit + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Check signed commits in PR + uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1.2.0 + with: + comment: | + You must GPG-sign your work, certifying that you either wrote the work or otherwise have the right to pass it on to an open-source project. See Developer's Certificate of Origin. See [signing][1]. + + **Note that all your commits must be signed.** If you have an unsigned commit, you can sign the previous commits by referring to [gpg-signing-old-commits][2]. + [1]: https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md#signing + [2]: https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/workflow.md#gpg-signing-old-commits diff --git a/.github/workflows/reusable-coveralls.yml b/.github/workflows/reusable-coveralls.yml new file mode 100644 index 000000000000..cf9c02b22e11 --- /dev/null +++ b/.github/workflows/reusable-coveralls.yml @@ -0,0 +1,76 @@ +name: Reusable Coveralls Upload + +on: + workflow_call: + inputs: + php-version: + description: The PHP version the workflow should run + type: string + required: true + +permissions: + contents: read + +jobs: + coveralls: + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ inputs.php-version }} + tools: composer + coverage: xdebug + + - name: Download coverage files + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: build/cov + + - name: Display structure of downloaded files + run: ls -R + working-directory: build/cov + + - name: Get composer cache directory + id: composer-cache + run: | + echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ github.job }}-php-${{ inputs.php-version }}-${{ hashFiles('**/composer.*') }} + restore-keys: | + ${{ github.job }}-php-${{ inputs.php-version }}- + ${{ github.job }}- + + - name: Cache PHPUnit's static analysis cache + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: build/.phpunit.cache/code-coverage + key: phpunit-code-coverage-${{ hashFiles('**/phpunit.*') }} + restore-keys: | + phpunit-code-coverage- + + - name: Install dependencies + run: composer update --ansi + + - name: Merge coverage files + run: | + jq '.autoload."psr-4" += {"Config\\": "app/Config/"}' composer.json > temp.json && mv temp.json composer.json + composer dump-autoload + vendor/bin/phpcov merge --clover build/logs/clover.xml build/cov + + - name: Upload coverage to Coveralls + run: | + composer global require php-coveralls/php-coveralls + php-coveralls --verbose --exclude-no-stmt --ansi + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/reusable-phpunit-test.yml b/.github/workflows/reusable-phpunit-test.yml new file mode 100644 index 000000000000..b216a71493bb --- /dev/null +++ b/.github/workflows/reusable-phpunit-test.yml @@ -0,0 +1,241 @@ +name: Reusable PHPUnit Test + +on: + workflow_call: + inputs: + job-name: + description: Name of the job to appear in GitHub UI + type: string + required: true + php-version: + description: The PHP version the workflow should run + type: string + required: true + job-id: + description: Job ID to be used as part of cache key and artifact name + type: string + required: false + db-platform: + description: The database platform to be tested + type: string + required: false + mysql-version: + description: Version of the mysql Docker image + type: string + required: false + group-name: + description: The @group to test + type: string + required: false + enable-artifact-upload: + description: Whether artifact uploading of coverage results should be enabled + type: boolean + required: false + enable-coverage: + description: Whether coverage should be enabled + type: boolean + required: false + enable-profiling: + description: Whether slow tests should be profiled + type: boolean + required: false + extra-extensions: + description: Additional PHP extensions that are needed to be enabled + type: string + required: false + extra-ini-options: + description: Additional PHP configuration directives that should be appended to the php.ini + type: string + required: false + extra-composer-options: + description: Additional Composer options that should be appended to the `composer update` call + type: string + required: false + extra-phpunit-options: + description: Additional PHPUnit options that should be appended to the `vendor/bin/phpunit` call + type: string + required: false + +permissions: + contents: read + +env: + NLS_LANG: 'AMERICAN_AMERICA.UTF8' + NLS_DATE_FORMAT: 'YYYY-MM-DD HH24:MI:SS' + NLS_TIMESTAMP_FORMAT: 'YYYY-MM-DD HH24:MI:SS' + NLS_TIMESTAMP_TZ_FORMAT: 'YYYY-MM-DD HH24:MI:SS' + +jobs: + tests: + name: ${{ inputs.job-name }} + runs-on: ubuntu-24.04 + + # Service containers cannot be extracted to caller workflows yet + services: + mysql: + image: ${{ inputs.db-platform == 'MySQLi' && format('mysql:{0}', inputs.mysql-version || '8.0') || '' }} + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: test + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + postgres: + image: ${{ inputs.db-platform == 'Postgre' && 'postgres' || '' }} + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test + ports: + - 5432:5432 + options: >- + --health-cmd=pg_isready + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + mssql: + image: ${{ inputs.db-platform == 'SQLSRV' && 'mcr.microsoft.com/mssql/server:2025-CU3-ubuntu-24.04' || '' }} + env: + MSSQL_SA_PASSWORD: 1Secure*Password1 + ACCEPT_EULA: Y + MSSQL_PID: Developer + ports: + - 1433:1433 + options: >- + --health-cmd="/opt/mssql-tools18/bin/sqlcmd -C -S 127.0.0.1 -U sa -P 1Secure*Password1 -Q 'SELECT @@VERSION'" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + oracle: + image: ${{ inputs.db-platform == 'OCI8' && 'gvenzl/oracle-free:latest' || '' }} + env: + ORACLE_RANDOM_PASSWORD: true + APP_USER: ORACLE + APP_USER_PASSWORD: ORACLE + ports: + - 1521:1521 + options: >- + --health-cmd healthcheck.sh + --health-interval 20s + --health-timeout 10s + --health-retries 10 + + redis: + image: redis + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + memcached: + image: memcached:1.6-alpine + ports: + - 11211:11211 + + steps: + - name: Install mssql-tools on runner + if: ${{ inputs.db-platform == 'SQLSRV' }} + run: | + source /etc/os-release + curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo gpg --dearmor --batch --yes -o /usr/share/keyrings/microsoft-prod.gpg + echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/ubuntu/${VERSION_ID}/prod ${UBUNTU_CODENAME} main" | sudo tee /etc/apt/sources.list.d/mssql-release.list + sudo apt-get update + sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 mssql-tools18 unixodbc-dev + + # Make sqlcmd available to subsequent steps + echo "/opt/mssql-tools18/bin" >> $GITHUB_PATH + + - name: Create database for MSSQL Server + if: ${{ inputs.db-platform == 'SQLSRV' }} + run: | + sqlcmd -S 127.0.0.1 \ + -U sa -P 1Secure*Password1 \ + -N -C \ + -Q "CREATE DATABASE test COLLATE Latin1_General_100_CS_AS_SC_UTF8" + + - name: Install latest ImageMagick + if: ${{ contains(inputs.extra-extensions, 'imagick') }} + run: | + sudo apt-get update + sudo apt-get install -y imagemagick libmagickwand-dev ghostscript poppler-data libjbig2dec0:amd64 libopenjp2-7:amd64 + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ inputs.php-version }} + tools: composer + extensions: gd, ${{ inputs.extra-extensions }} + ini-values: ${{ inputs.extra-ini-options }} + coverage: ${{ env.COVERAGE_DRIVER }} + env: + COVERAGE_DRIVER: ${{ inputs.enable-coverage && 'xdebug' || 'none' }} + + - name: Setup global environment variables + id: setup-env + run: | + echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + echo "ARTIFACT_NAME=${{ inputs.job-id || github.job }}-php-${{ inputs.php-version }}-db-${{ inputs.db-platform || 'none' }}${{ inputs.mysql-version || '' }}" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.setup-env.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ inputs.job-id || github.job }}-php-${{ inputs.php-version }}-db-${{ inputs.db-platform || 'none' }}-${{ hashFiles('**/composer.*') }} + restore-keys: | + ${{ inputs.job-id || github.job }}-php-${{ inputs.php-version }}-db-${{ inputs.db-platform || 'none' }}- + ${{ inputs.job-id || github.job }}-php-${{ inputs.php-version }}- + ${{ inputs.job-id || github.job }}- + + - name: Cache PHPUnit's static analysis cache + if: ${{ inputs.enable-artifact-upload }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: build/.phpunit.cache/code-coverage + key: phpunit-code-coverage-${{ hashFiles('**/phpunit.*') }} + restore-keys: | + phpunit-code-coverage- + + - name: Install dependencies + run: | + composer config --global github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} + composer update --ansi ${{ inputs.extra-composer-options }} + + - name: Compute additional PHPUnit options + id: phpunit-options + run: | + echo "EXTRA_PHPUNIT_OPTIONS=${{ format('{0} {1} {2}', env.GROUP_OPTION, env.COVERAGE_OPTION, inputs.extra-phpunit-options) }}" >> $GITHUB_OUTPUT + env: + COVERAGE_OPTION: ${{ inputs.enable-coverage && format('--coverage-php build/cov/coverage-{0}.cov', steps.setup-env.outputs.ARTIFACT_NAME) || '--no-coverage' }} + GROUP_OPTION: ${{ inputs.group-name && format('--group {0}', inputs.group-name) || '' }} + + - name: Run tests + run: script -e -c "vendor/bin/phpunit --color=always ${{ steps.phpunit-options.outputs.EXTRA_PHPUNIT_OPTIONS }}" + env: + DB: ${{ inputs.db-platform }} + TACHYCARDIA_MONITOR_GA: ${{ inputs.enable-profiling && 'enabled' || '' }} + TERM: xterm-256color + continue-on-error: ${{ inputs.php-version == '8.5' }} + + - name: Upload coverage results as artifact + if: ${{ inputs.enable-artifact-upload }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ steps.setup-env.outputs.ARTIFACT_NAME }} + path: build/cov/coverage-${{ steps.setup-env.outputs.ARTIFACT_NAME }}.cov + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/reusable-serviceless-phpunit-test.yml b/.github/workflows/reusable-serviceless-phpunit-test.yml new file mode 100644 index 000000000000..8420d2b8c978 --- /dev/null +++ b/.github/workflows/reusable-serviceless-phpunit-test.yml @@ -0,0 +1,135 @@ +# Reusable workflow for PHPUnit testing +# without Docker services and databases +name: Reusable Serviceless PHPUnit Test + +on: + workflow_call: + inputs: + job-name: + description: Name of the job to appear in GitHub UI + type: string + required: true + php-version: + description: The PHP version the workflow should run + type: string + required: true + job-id: + description: Job ID to be used as part of cache key and artifact name + type: string + required: false + group-name: + description: The @group to test + type: string + required: false + enable-artifact-upload: + description: Whether artifact uploading of coverage results should be enabled + type: boolean + required: false + enable-coverage: + description: Whether coverage should be enabled + type: boolean + required: false + enable-profiling: + description: Whether slow tests should be profiled + type: boolean + required: false + extra-extensions: + description: Additional PHP extensions that are needed to be enabled + type: string + required: false + extra-ini-options: + description: Additional PHP configuration directives that should be appended to the php.ini + type: string + required: false + extra-composer-options: + description: Additional Composer options that should be appended to the `composer update` call + type: string + required: false + extra-phpunit-options: + description: Additional PHPUnit options that should be appended to the `vendor/bin/phpunit` call + type: string + required: false + +permissions: + contents: read + +jobs: + tests: + name: ${{ inputs.job-name }} + runs-on: ubuntu-24.04 + + steps: + - name: Install latest ImageMagick + if: ${{ contains(inputs.extra-extensions, 'imagick') }} + run: | + sudo apt-get update + sudo apt-get install -y imagemagick libmagickwand-dev ghostscript poppler-data libjbig2dec0:amd64 libopenjp2-7:amd64 + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ inputs.php-version }} + tools: composer + extensions: gd, ${{ inputs.extra-extensions }} + ini-values: ${{ inputs.extra-ini-options }} + coverage: ${{ env.COVERAGE_DRIVER }} + env: + COVERAGE_DRIVER: ${{ inputs.enable-coverage && 'xdebug' || 'none' }} + + - name: Setup global environment variables + id: setup-env + run: | + echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + echo "ARTIFACT_NAME=${{ inputs.job-id || github.job }}-php-${{ inputs.php-version }}" >> $GITHUB_OUTPUT + + - name: Cache Composer dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.setup-env.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ inputs.job-id || github.job }}-php-${{ inputs.php-version }}-${{ hashFiles('**/composer.*') }} + restore-keys: | + ${{ inputs.job-id || github.job }}-php-${{ inputs.php-version }}- + ${{ inputs.job-id || github.job }}- + + - name: Cache PHPUnit's static analysis cache + if: ${{ inputs.enable-artifact-upload }} + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: build/.phpunit.cache/code-coverage + key: phpunit-code-coverage-${{ hashFiles('**/phpunit.*') }} + restore-keys: | + phpunit-code-coverage- + + - name: Install dependencies + run: | + composer config --global github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} + composer update --ansi ${{ inputs.extra-composer-options }} + + - name: Compute additional PHPUnit options + id: phpunit-options + run: | + echo "EXTRA_PHPUNIT_OPTIONS=${{ format('{0} {1} {2}', env.GROUP_OPTION, env.COVERAGE_OPTION, inputs.extra-phpunit-options) }}" >> $GITHUB_OUTPUT + env: + COVERAGE_OPTION: ${{ inputs.enable-coverage && format('--coverage-php build/cov/coverage-{0}.cov', steps.setup-env.outputs.ARTIFACT_NAME) || '--no-coverage' }} + GROUP_OPTION: ${{ inputs.group-name && format('--group {0}', inputs.group-name) || '' }} + + - name: Run tests + run: script -e -c "vendor/bin/phpunit --color=always ${{ steps.phpunit-options.outputs.EXTRA_PHPUNIT_OPTIONS }}" + env: + TACHYCARDIA_MONITOR_GA: ${{ inputs.enable-profiling && 'enabled' || '' }} + TERM: xterm-256color + + - name: Upload coverage results as artifact + if: ${{ inputs.enable-artifact-upload }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ steps.setup-env.outputs.ARTIFACT_NAME }} + path: build/cov/coverage-${{ steps.setup-env.outputs.ARTIFACT_NAME }}.cov + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/test-autoreview.yml b/.github/workflows/test-autoreview.yml new file mode 100644 index 000000000000..f06e02619966 --- /dev/null +++ b/.github/workflows/test-autoreview.yml @@ -0,0 +1,108 @@ +name: AutoReview + +on: + pull_request: + paths: + - composer.json + - spark + - '**.php' + - .github/workflows/test-autoreview.yml + push: + paths: + - composer.json + - spark + - '**.php' + - .github/workflows/test-autoreview.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + auto-review-tests: + name: Automatic Code Review + uses: ./.github/workflows/reusable-serviceless-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo + with: + job-name: PHP 8.2 + php-version: '8.2' + job-id: auto-review-tests + group-name: AutoReview + + composer-normalize-tests: + name: Check normalized composer.json + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: '8.2' + + - name: Install dependencies + run: composer update + + - name: Run on root composer.json + id: normalize-root-composer + if: ${{ always() }} + run: | + echo "ROOT_COMPOSER_RESULT<> $GITHUB_OUTPUT + echo "$(composer normalize)" >> $GITHUB_OUTPUT + echo "RCR" >> $GITHUB_OUTPUT + + - name: Run on framework composer.json + id: normalize-framework-composer + if: ${{ always() }} + working-directory: admin/framework + run: | + echo "FRAMEWORK_COMPOSER_RESULT<> $GITHUB_OUTPUT + echo "$(composer normalize)" >> $GITHUB_OUTPUT + echo "FCR" >> $GITHUB_OUTPUT + + - name: Run on starter composer.json + id: normalize-starter-composer + if: ${{ always() }} + working-directory: admin/starter + run: | + echo "STARTER_COMPOSER_RESULT<> $GITHUB_OUTPUT + echo "$(composer normalize)" >> $GITHUB_OUTPUT + echo "SCR" >> $GITHUB_OUTPUT + + - name: Analyse normalization results + run: | + if [[ '${{ steps.normalize-root-composer.conclusion }}' == 'failure' ]]; then + echo 'Normalization of root composer.json encountered a problem.'; + echo 'Please run it locally: `composer normalize`'; + exit 1; + fi + + if [[ ${{ contains(steps.normalize-root-composer.outputs.ROOT_COMPOSER_RESULT, 'Successfully normalized') }} == true ]]; then + echo 'Root composer.json is not yet normalized.'; + exit 1; + fi + + if [[ '${{ steps.normalize-framework-composer.conclusion }}' == 'failure' ]]; then + echo 'Normalization of framework composer.json encountered a problem.'; + echo 'Please run it locally: `composer normalize -d admin/framework`'; + exit 1; + fi + + if [[ ${{ contains(steps.normalize-framework-composer.outputs.FRAMEWORK_COMPOSER_RESULT, 'Successfully normalized') }} == true ]]; then + echo 'Framework composer.json is not yet normalized.'; + exit 1; + fi + + if [[ '${{ steps.normalize-starter-composer.conclusion }}' == 'failure' ]]; then + echo 'Normalization of starter composer.json encountered a problem.'; + echo 'Please run it locally: `composer normalize -d admin/starter`'; + exit 1; + fi + + if [[ ${{ contains(steps.normalize-starter-composer.outputs.STARTER_COMPOSER_RESULT, 'Successfully normalized') }} == true ]]; then + echo 'Starter composer.json is not yet normalized.'; + exit 1; + fi diff --git a/.github/workflows/test-coding-standards.yml b/.github/workflows/test-coding-standards.yml new file mode 100644 index 000000000000..b02059733b13 --- /dev/null +++ b/.github/workflows/test-coding-standards.yml @@ -0,0 +1,64 @@ +name: Coding Standards + +on: + push: + paths: + - '**.php' + - 'spark' + - '.github/workflows/test-coding-standards.yml' + pull_request: + paths: + - '**.php' + - 'spark' + - '.github/workflows/test-coding-standards.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + name: PHP ${{ matrix.php-version }} Lint with PHP CS Fixer + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + php-version: + - '8.2' + include: + - php-version: '8.5' + composer-option: '--ignore-platform-req=php' + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ matrix.php-version }} + extensions: tokenizer + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ runner.os }}-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.php-version }}- + ${{ runner.os }}- + + - name: Install dependencies + run: composer update --ansi --no-interaction ${{ matrix.composer-option }} + + - name: Run lint + run: composer cs diff --git a/.github/workflows/test-file-permissions.yml b/.github/workflows/test-file-permissions.yml new file mode 100644 index 000000000000..441d555137e8 --- /dev/null +++ b/.github/workflows/test-file-permissions.yml @@ -0,0 +1,24 @@ +name: Check File Permissions + +on: + pull_request: + push: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + permission-check: + name: Check File Permission + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Detect unnecessary execution permissions + run: php utils/check_permission_x.php diff --git a/.github/workflows/test-phpcpd.yml b/.github/workflows/test-phpcpd.yml new file mode 100644 index 000000000000..636252763ddf --- /dev/null +++ b/.github/workflows/test-phpcpd.yml @@ -0,0 +1,47 @@ +# When a PR is opened or a push is made, check code +# for duplication with PHP Copy/Paste Detector. +name: PHPCPD + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'public/**.php' + - 'system/**.php' + - '.github/workflows/test-phpcpd.yml' + push: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'public/**.php' + - 'system/**.php' + - '.github/workflows/test-phpcpd.yml' + +permissions: + contents: read + +jobs: + phpcpd: + # Note: Reusable workflow SHA must be manually updated. Check for updates with: + # git ls-remote https://github.com/codeigniter4/.github main | head -1 + uses: codeigniter4/.github/.github/workflows/phpcpd.yml@0ad5e1bc5620281e766d3267205dc4c22f4ac0ee # main + with: + dirs: "app/ public/ system/" + options: >- + --exclude system/Test + --exclude system/ThirdParty + --exclude system/Database/SQLSRV/Builder.php + --exclude system/Database/SQLSRV/Forge.php + --exclude system/Database/MySQLi/Builder.php + --exclude system/Database/OCI8/Builder.php + --exclude system/Database/Postgre/Builder.php + --exclude system/Debug/Exceptions.php + --exclude system/HTTP/SiteURI.php + --exclude system/Validation/Rules.php + --exclude system/Autoloader/Autoloader.php + --exclude system/Config/Filters.php diff --git a/.github/workflows/test-phpstan.yml b/.github/workflows/test-phpstan.yml new file mode 100644 index 000000000000..cc246f871808 --- /dev/null +++ b/.github/workflows/test-phpstan.yml @@ -0,0 +1,89 @@ +# When a PR is opened or a push is made, perform +# a static analysis check on the code using PHPStan. +name: PHPStan + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'utils/**.php' + - composer.json + - phpstan.dist.neon + - phpstan-baseline.php + - '.github/workflows/test-phpstan.yml' + + push: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'utils/**.php' + - composer.json + - phpstan.dist.neon + - phpstan-baseline.php + - '.github/workflows/test-phpstan.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + name: PHP ${{ matrix.php-versions }} Static Analysis + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: '8.2' + extensions: intl + coverage: none + + - name: Use latest Composer + run: composer self-update + + - name: Validate composer.json + run: composer validate --strict + + - name: Get composer cache directory + id: composer-cache + run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Create PHPStan result cache directory + run: mkdir -p build/phpstan + + - name: Cache PHPStan result cache directory + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: build/phpstan + key: ${{ runner.os }}-phpstan-${{ github.sha }} + restore-keys: ${{ runner.os }}-phpstan- + + - name: Install dependencies + run: composer update --ansi --no-interaction + + - name: Run static analysis + run: composer phpstan:check diff --git a/.github/workflows/test-phpunit.yml b/.github/workflows/test-phpunit.yml new file mode 100644 index 000000000000..8c17cae53de6 --- /dev/null +++ b/.github/workflows/test-phpunit.yml @@ -0,0 +1,178 @@ +name: PHPUnit + +on: + push: + branches: + - develop + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'spark' + - composer.json + - phpunit.dist.xml + - .github/workflows/test-phpunit.yml + - .github/workflows/reusable-phpunit-test.yml + + pull_request: + branches: + - develop + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'spark' + - composer.json + - phpunit.dist.xml + - .github/workflows/test-phpunit.yml + - .github/workflows/reusable-phpunit-test.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + # Any environment variables set in an env context defined at the workflow level + # in the caller workflow are not propagated to the called workflow. + coverage-php-version: + name: Setup PHP Version for Code Coverage + runs-on: ubuntu-24.04 + outputs: + version: ${{ steps.coverage-php-version.outputs.version }} + steps: + - id: coverage-php-version + run: | + echo "version=8.2" >> $GITHUB_OUTPUT + + sanity-tests: + name: Others + needs: coverage-php-version + + strategy: + matrix: + php-version: + - '8.2' + - '8.3' + - '8.4' + - '8.5' + + uses: ./.github/workflows/reusable-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo + with: + job-name: Sanity Tests + php-version: ${{ matrix.php-version }} + job-id: sanity-tests + group-name: Others + enable-artifact-upload: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + enable-coverage: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + enable-profiling: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + extra-extensions: imagick, redis, memcached + + database-live-tests: + name: DatabaseLive + needs: + - coverage-php-version + - sanity-tests + + strategy: + fail-fast: false + matrix: + php-version: + - '8.2' + - '8.3' + - '8.4' + - '8.5' + db-platform: + - MySQLi + - OCI8 + - Postgre + - SQLSRV + - SQLite3 + mysql-version: + - '8.0' + include: + - php-version: '8.2' + db-platform: MySQLi + mysql-version: '5.7' + + uses: ./.github/workflows/reusable-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo + with: + job-name: '' + php-version: ${{ matrix.php-version }} + job-id: database-live-tests + db-platform: ${{ matrix.db-platform }} + mysql-version: ${{ matrix.mysql-version }} + group-name: DatabaseLive + enable-artifact-upload: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + enable-coverage: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + enable-profiling: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + extra-extensions: mysqli, oci8, pgsql, sqlsrv, sqlite3 + + separate-process-tests: + name: SeparateProcess + needs: + - coverage-php-version + - sanity-tests + + strategy: + matrix: + php-version: + - '8.2' + - '8.3' + - '8.4' + - '8.5' + + uses: ./.github/workflows/reusable-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo + with: + job-name: '' + php-version: ${{ matrix.php-version }} + job-id: separate-process-tests + group-name: SeparateProcess + enable-artifact-upload: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + enable-coverage: true # needs xdebug for assertHeaderEmitted() tests + enable-profiling: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + extra-extensions: mysqli, oci8, pgsql, sqlsrv-5.10.1, sqlite3 + + cache-live-tests: + name: CacheLive + needs: + - coverage-php-version + - sanity-tests + + strategy: + matrix: + php-version: + - '8.2' + - '8.3' + - '8.4' + - '8.5' + + uses: ./.github/workflows/reusable-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo + with: + job-name: Cache Live Tests + php-version: ${{ matrix.php-version }} + job-id: cache-live-tests + group-name: CacheLive + enable-artifact-upload: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + enable-coverage: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + enable-profiling: ${{ matrix.php-version == needs.coverage-php-version.outputs.version }} + extra-extensions: redis, memcached, apcu + extra-ini-options: apc.enable_cli=1 + + coveralls: + name: Upload coverage results to Coveralls + if: github.repository_owner == 'codeigniter4' + needs: + - coverage-php-version + - sanity-tests + - cache-live-tests + - database-live-tests + - separate-process-tests + + uses: ./.github/workflows/reusable-coveralls.yml # @TODO Extract to codeigniter4/.github repo + with: + php-version: ${{ needs.coverage-php-version.outputs.version }} diff --git a/.github/workflows/test-psalm.yml b/.github/workflows/test-psalm.yml new file mode 100644 index 000000000000..3f385ce858fb --- /dev/null +++ b/.github/workflows/test-psalm.yml @@ -0,0 +1,79 @@ +name: Psalm + +on: + pull_request: + branches: + - develop + - '4.*' + paths: + - '**.php' + - 'composer.*' + - 'psalm*' + - '.github/workflows/test-psalm.yml' + push: + branches: + - develop + - '4.*' + paths: + - '**.php' + - 'composer.*' + - 'psalm*' + - '.github/workflows/test-psalm.yml' + +permissions: + contents: read + +jobs: + build: + name: Psalm Analysis + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + php-version: + - '8.2' + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ matrix.php-version }} + extensions: intl, json, mbstring, xml, mysqli, oci8, pgsql, sqlsrv, sqlite3 + coverage: none + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get composer cache directory + id: composer-cache + run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache composer dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Create Psalm cache directory + run: mkdir -p build/psalm + + - name: Cache Psalm results + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: build/psalm + key: ${{ runner.os }}-psalm-${{ github.sha }} + restore-keys: ${{ runner.os }}-psalm- + + - name: Install dependencies + run: | + composer require sebastian/diff:^5.0 --ansi --working-dir utils + composer update --ansi --no-interaction + + - name: Run Psalm analysis + run: utils/vendor/bin/psalm diff --git a/.github/workflows/test-random-execution.yml b/.github/workflows/test-random-execution.yml new file mode 100644 index 000000000000..e90de6bb031d --- /dev/null +++ b/.github/workflows/test-random-execution.yml @@ -0,0 +1,238 @@ +name: Random Test Execution Verification + +on: + push: + branches: + - develop + - '4.*' + paths: + - '.github/scripts/run-random-tests.sh' + - '.github/scripts/random-tests-config.txt' + - '.github/workflows/test-random-execution.yml' + - 'phpunit.dist.xml' + - 'system/**.php' + - 'tests/**.php' + + pull_request: + branches: + - develop + - '4.*' + paths: + - '.github/scripts/run-random-tests.sh' + - '.github/scripts/random-tests-config.txt' + - '.github/workflows/test-random-execution.yml' + - 'phpunit.dist.xml' + - 'system/**.php' + - 'tests/**.php' + + workflow_dispatch: + inputs: + quiet: + description: Suppress debug output + type: boolean + required: false + default: false + component: + description: Test single component (overrides config file) + type: string + required: false + default: '' + max-jobs: + description: Limit concurrent test jobs (auto-detect if omitted) + type: string + required: false + default: '' + repeat: + description: Repeat full component run REPEAT times + type: string + required: false + default: '10' + timeout: + description: Per-component timeout in seconds (0 disables) + type: string + required: false + default: '300' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + random-tests: + name: PHP ${{ matrix.php-version }} - ${{ matrix.db-platform }} + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + php-version: + - '8.5' + db-platform: + - MySQLi + - Postgre + - SQLSRV + - SQLite3 + - Oracle + + services: + mysql: + image: ${{ matrix.db-platform == 'MySQLi' && 'mysql:8.0' || '' }} + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: test + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + postgres: + image: ${{ matrix.db-platform == 'Postgre' && 'postgres' || '' }} + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test + ports: + - 5432:5432 + options: >- + --health-cmd=pg_isready + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + mssql: + image: ${{ matrix.db-platform == 'SQLSRV' && 'mcr.microsoft.com/mssql/server:2025-CU3-ubuntu-24.04' || '' }} + env: + MSSQL_SA_PASSWORD: 1Secure*Password1 + ACCEPT_EULA: Y + MSSQL_PID: Developer + ports: + - 1433:1433 + options: >- + --health-cmd="/opt/mssql-tools18/bin/sqlcmd -C -S 127.0.0.1 -U sa -P 1Secure*Password1 -Q 'SELECT @@VERSION'" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + oracle: + image: ${{ matrix.db-platform == 'Oracle' && 'gvenzl/oracle-free:latest' || '' }} + env: + ORACLE_RANDOM_PASSWORD: true + APP_USER: ORACLE + APP_USER_PASSWORD: ORACLE + ports: + - 1521:1521 + options: >- + --health-cmd healthcheck.sh + --health-interval 20s + --health-timeout 10s + --health-retries 10 + + redis: + image: redis + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + memcached: + image: memcached:1.6-alpine + ports: + - 11211:11211 + + steps: + - name: Install mssql-tools on runner + if: ${{ matrix.db-platform == 'SQLSRV' }} + run: | + source /etc/os-release + curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo gpg --dearmor --batch --yes -o /usr/share/keyrings/microsoft-prod.gpg + echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/ubuntu/${VERSION_ID}/prod ${UBUNTU_CODENAME} main" | sudo tee /etc/apt/sources.list.d/mssql-release.list + sudo apt-get update + sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 mssql-tools18 unixodbc-dev + echo "/opt/mssql-tools18/bin" >> $GITHUB_PATH + + - name: Create database for MSSQL Server + if: ${{ matrix.db-platform == 'SQLSRV' }} + run: | + sqlcmd -S 127.0.0.1 \ + -U sa -P 1Secure*Password1 \ + -N -C \ + -Q "CREATE DATABASE test COLLATE Latin1_General_100_CS_AS_SC_UTF8" + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Setup PHP ${{ matrix.php-version }} + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ matrix.php-version }} + extensions: gd, curl, iconv, json, mbstring, openssl, sodium + ini-values: opcache.enable_cli=0 + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache composer dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_FILES_DIR }} + key: PHP_${{ matrix.php-version }}-${{ hashFiles('**/composer.*') }} + restore-keys: | + PHP_${{ matrix.php-version }}- + + - name: Install project dependencies + run: | + composer config --global github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} + composer update --ansi + + - name: Run random test execution verification + run: | + args=() + + # Add --quiet flag if input is true + if [[ "${{ inputs.quiet }}" == "true" ]]; then + args+=("--quiet") + fi + + # Add --component flag if component is specified + if [[ -n "${{ inputs.component }}" ]]; then + args+=("--component" "${{ inputs.component }}") + fi + + # Add --max-jobs flag if specified (empty means auto-detect) + if [[ -n "${{ inputs.max-jobs }}" ]]; then + args+=("--max-jobs" "${{ inputs.max-jobs }}") + fi + + # Add --repeat flag (always, default is 10) + args+=("--repeat" "${{ inputs.repeat || '10' }}") + + # Add --timeout flag (always, default is 300) + args+=("--timeout" "${{ inputs.timeout || '300' }}") + + .github/scripts/run-random-tests.sh "${args[@]}" + env: + DB: ${{ matrix.db-platform }} + TERM: xterm-256color + + - name: Upload random-test artifacts on failure + if: ${{ always() && failure() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: random-tests-${{ matrix.db-platform }}-php${{ matrix.php-version }} + path: build/random-tests/ + retention-days: 1 + overwrite: true diff --git a/.github/workflows/test-rector.yml b/.github/workflows/test-rector.yml new file mode 100644 index 000000000000..2fa9b74c0e02 --- /dev/null +++ b/.github/workflows/test-rector.yml @@ -0,0 +1,94 @@ +# When a PR is opened or a push is made, perform +# a static analysis check on the code using Rector. +name: Rector + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'utils/**.php' + - '.github/workflows/test-rector.yml' + - composer.json + - rector.php + - '**.neon.dist' + + push: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'utils/**.php' + - '.github/workflows/test-rector.yml' + - composer.json + - rector.php + - '**.neon.dist' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + name: PHP ${{ matrix.php-version }} Analyze code (Rector) + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + php-version: + - '8.2' + include: + - php-version: '8.5' + composer-option: '--ignore-platform-req=php' + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ matrix.php-version }} + extensions: intl + + - name: Use latest Composer + run: composer self-update + + - name: Validate composer.json + run: composer validate --strict + + - name: Get composer cache directory + id: composer-cache + run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer update --ansi --no-interaction ${{ matrix.composer-option }} + + - name: Rector Cache + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: /tmp/rector + key: ${{ runner.os }}-rector-${{ github.run_id }} + restore-keys: ${{ runner.os }}-rector- + + - run: mkdir -p /tmp/rector + + - name: Run static analysis + run: vendor/bin/rector process --dry-run --no-progress-bar diff --git a/.github/workflows/test-scss.yml b/.github/workflows/test-scss.yml new file mode 100644 index 000000000000..0000e700a422 --- /dev/null +++ b/.github/workflows/test-scss.yml @@ -0,0 +1,57 @@ +name: SCSS Compilation + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - '**.scss' + - '**.css' + - '.github/workflows/test-scss.yml' + + push: + branches: + - 'develop' + - '4.*' + paths: + - '**.scss' + - '**.css' + - '.github/workflows/test-scss.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + name: Compilation of SCSS (Dart Sass) + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '24' + + - name: Install Dart Sass + run: | + npm install --global sass + sass --version + + - name: Run Dart Sass + run: sass --no-source-map admin/css/debug-toolbar/toolbar.scss system/Debug/Toolbar/Views/toolbar.css + + - name: Check for changed CSS files + run: | + if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then + echo "Your changes to the SCSS files did not match the expected CSS output." + git diff-files --patch + exit 1 + fi diff --git a/.github/workflows/test-structarmed.yml b/.github/workflows/test-structarmed.yml new file mode 100644 index 000000000000..1c6d731c9e4b --- /dev/null +++ b/.github/workflows/test-structarmed.yml @@ -0,0 +1,90 @@ +# When a PR is opened or a push is made, perform +# a static analysis check on the code using Structarmed. +name: Structarmed + +on: + pull_request: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'utils/**.php' + - '.github/workflows/test-structarmed.yml' + - composer.json + - structarmed.php + - '**.neon.dist' + + push: + branches: + - 'develop' + - '4.*' + paths: + - 'app/**.php' + - 'system/**.php' + - 'tests/**.php' + - 'utils/**.php' + - '.github/workflows/test-structarmed.yml' + - composer.json + - structarmed.php + - '**.neon.dist' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + name: PHP ${{ matrix.php-version }} Analyze code (Structarmed) + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + php-version: + - '8.2' + - '8.5' + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ matrix.php-version }} + extensions: intl + tools: composer + + - name: Validate composer.json + run: composer validate --strict + + - name: Get composer cache directory + id: composer-cache + run: echo "COMPOSER_CACHE_FILES_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_FILES_DIR }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer update --ansi --no-interaction + + - name: Structarmed Cache + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: /tmp/structarmed + key: ${{ runner.os }}-structarmed-${{ github.run_id }} + restore-keys: ${{ runner.os }}-structarmed- + + - run: mkdir -p /tmp/structarmed + + - name: Run static analysis + run: vendor/bin/structarmed analyze diff --git a/.github/workflows/test-userguide.yml b/.github/workflows/test-userguide.yml new file mode 100644 index 000000000000..88f316888f02 --- /dev/null +++ b/.github/workflows/test-userguide.yml @@ -0,0 +1,44 @@ +# When a Pull Request is opened that modifies +# the User Guide source, build the User Guide +# with Sphinx and let the contributor know of +# any errors. +name: Test User Guide + +on: + pull_request: + paths: + - 'user_guide_src/**' + - '.github/workflows/test-userguide.yml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + syntax_check: + name: Check User Guide syntax + runs-on: ubuntu-24.04 + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + + - name: Install Sphinx + run: | + python -m pip install --upgrade pip + pip install -r user_guide_src/requirements.txt + + - name: Detect usage of tabs in RST files + run: php utils/check_tabs_in_rst.php + + - name: Build Docs with Sphinx + run: make html SPHINXOPTS="-W --keep-going -w /tmp/sphinx-log" + working-directory: user_guide_src diff --git a/.github/workflows/verify-release.yml b/.github/workflows/verify-release.yml new file mode 100644 index 000000000000..0488b76a1a70 --- /dev/null +++ b/.github/workflows/verify-release.yml @@ -0,0 +1,185 @@ +# When a release is published, verify that the release pipeline delivered +# everything: the distributable repos, the new framework version on +# Packagist, and the user guide with its ePub. +# +# Use "Run workflow" with the release tag to verify an existing release. +name: Verify Release + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: The release tag to verify (e.g., v4.7.5) + type: string + required: true + +permissions: + contents: read + +env: + TAG: ${{ github.event.release.tag_name || inputs.version }} + PUBLISHED_AT: ${{ github.event.release.published_at }} + +jobs: + wait-for-deploy: + name: Wait for distributables deploy + if: github.repository == 'codeigniter4/CodeIgniter4' + runs-on: ubuntu-24.04 + timeout-minutes: 30 + permissions: + actions: read + contents: read + + steps: + - name: Wait for "Deploy Distributable Repos" to succeed + env: + GH_TOKEN: ${{ github.token }} + run: | + # On workflow_dispatch the event carries no publish time. + if [ -z "${PUBLISHED_AT}" ]; then + PUBLISHED_AT=$(gh api "repos/${{ github.repository }}/releases/tags/${TAG}" --jq '.published_at') + echo "Resolved published_at=${PUBLISHED_AT} for ${TAG}." + fi + + query="repos/${{ github.repository }}/actions/workflows/deploy-distributables.yml/runs?event=release&per_page=1&created=>=${PUBLISHED_AT}" + + while true; do + run=$(gh api "${query}" --jq '.workflow_runs[0] | "\(.status) \(.conclusion)"' || true) + status=${run% *} + conclusion=${run#* } + echo "Deploy Distributable Repos: status=${status:-not found} conclusion=${conclusion:-n/a}" + + if [ "${status}" = "completed" ]; then + if [ "${conclusion}" = "success" ]; then + exit 0 + fi + + echo "Deploy Distributable Repos did not succeed." + exit 1 + fi + + sleep 30 + done + + appstarter: + name: Install and test appstarter + needs: wait-for-deploy + runs-on: ubuntu-24.04 + timeout-minutes: 45 + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: '8.2' + tools: composer + extensions: intl, gd + coverage: none + + - name: Wait for Packagist to serve the new framework version + run: | + version="${TAG#v}" + + for i in $(seq 1 30); do + if curl -fsSL https://repo.packagist.org/p2/codeigniter4/framework.json \ + | jq -e --arg version "v${version}" \ + '.packages."codeigniter4/framework" | map(select(.version == $version)) | length > 0' > /dev/null; then + echo "Packagist serves codeigniter4/framework v${version}." + exit 0 + fi + + echo "Attempt ${i}: v${version} is not yet on Packagist." + sleep 60 + done + + echo "Timed out waiting for Packagist to serve v${version}." + exit 1 + + - name: Install appstarter + run: composer create-project codeigniter4/appstarter release-test --no-interaction + + - name: Verify the framework version + working-directory: release-test + run: | + version="${TAG#v}" + installed=$(composer show codeigniter4/framework --format=json | jq -r '.versions[0]') + echo "Installed codeigniter4/framework: ${installed}" + + if [ "${installed}" != "v${version}" ]; then + echo "Expected v${version}." + exit 1 + fi + + - name: Test appstarter + working-directory: release-test + run: composer test + + userguide: + name: Verify user guide deployment + needs: wait-for-deploy + runs-on: ubuntu-24.04 + timeout-minutes: 30 + + steps: + - name: Check that the ePub was added + env: + GH_TOKEN: ${{ github.token }} + run: | + version="${TAG#v}" + + for i in $(seq 1 30); do + if gh api "repos/codeigniter4/userguide/contents/CodeIgniter${version}.epub" --jq '.name' > /dev/null 2>&1; then + echo "CodeIgniter${version}.epub is in the userguide repo." + exit 0 + fi + + echo "Attempt ${i}: CodeIgniter${version}.epub is not yet in the userguide repo." + sleep 30 + done + + echo "Timed out waiting for CodeIgniter${version}.epub." + exit 1 + + - name: Check that the user guide workflows succeeded + env: + GH_TOKEN: ${{ github.token }} + run: | + # On workflow_dispatch the event carries no publish time. + if [ -z "${PUBLISHED_AT}" ]; then + PUBLISHED_AT=$(gh api "repos/${{ github.repository }}/releases/tags/${TAG}" --jq '.published_at') + echo "Resolved published_at=${PUBLISHED_AT} for ${TAG}." + fi + + for workflow in "Deploy Production" "pages build and deployment"; do + query="repos/codeigniter4/userguide/actions/runs?per_page=30&created=>=${PUBLISHED_AT}" + + success="" + + for _ in $(seq 1 30); do + run=$(gh api "${query}" \ + | jq -r --arg name "${workflow}" \ + '[.workflow_runs[] | select(.name == $name)][0] | "\(.status) \(.conclusion)"') + status=${run% *} + conclusion=${run#* } + echo "${workflow}: status=${status:-not found} conclusion=${conclusion:-n/a}" + + if [ "${status}" = "completed" ]; then + if [ "${conclusion}" = "success" ]; then + success=1 + break + fi + + echo "\"${workflow}\" did not succeed." + exit 1 + fi + + sleep 30 + done + + if [ -z "${success}" ]; then + echo "Timed out waiting for \"${workflow}\"." + exit 1 + fi + done diff --git a/.gitignore b/.gitignore index 276fdf44eacd..3e4a7c1d18f7 100644 --- a/.gitignore +++ b/.gitignore @@ -39,36 +39,50 @@ $RECYCLE.BIN/ #------------------------- # Environment Files #------------------------- - -application/config/development/* +# These should never be under version control, +# as it poses a security risk. +.env +.vagrant +Vagrantfile +/user_guide_src/venv/ +.python-version +/user_guide_src/.python-version #------------------------- # Temporary Files #------------------------- -writable/cache/* -!writable/cache/index.html -!writable/cache/.htaccess +/writable/cache/* +!/writable/cache/index.html + +/writable/logs/* +!/writable/logs/index.html + +/writable/session/* +!/writable/session/index.html + +/writable/uploads/* +!/writable/uploads/index.html + +/writable/debugbar/* +!/writable/debugbar/index.html -writable/logs/* -!writable/logs/index.html -!writable/logs/.htaccess +/writable/**/*.db +/writable/**/*.sqlite -writable/uploads/* -!writable/uploads/index.html -!writable/uploads/.htaccess +php_errors.log #------------------------- # User Guide Temp Files #------------------------- -user_guide_src/build/* -user_guide_src/cilexer/build/* -user_guide_src/cilexer/dist/* -user_guide_src/cilexer/pycilexer.egg-info/* +/user_guide_src/build/* #------------------------- # Test Files #------------------------- -tests/support/coverage* +/tests/coverage* + +# Don't save phpunit under version control. +/phpunit #------------------------- # Composer @@ -83,24 +97,36 @@ composer.lock # Modules Testing _modules/* +# phpenv local config +.php-version + # Jetbrains editors (PHPStorm, etc) .idea/ *.iml # Netbeans -nbproject/private/ -build/ -nbbuild/ -dist/ -nbdist/ -nbactions.xml -nb-configuration.xml -.nb-gradle/ - -## Sublime Text +/nbproject/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/nbactions.xml +/nb-configuration.xml +/.nb-gradle/ + +# Sublime Text *.tmlanguage.cache *.tmPreferences.cache *.stTheme.cache *.sublime-workspace *.sublime-project -/nbproject/ \ No newline at end of file +.phpintel +/api/ + +# Visual Studio Code +.vscode/ + +/results/ +/phpunit.xml + +/.php-cs-fixer.php diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 000000000000..ad6ffacb8f0c --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +use CodeIgniter\CodingStandard\CodeIgniter4; +use Nexus\CsConfig\Factory; +use PhpCsFixer\Finder; + +$finder = Finder::create() + ->files() + ->in([ + __DIR__ . '/system', + __DIR__ . '/utils', + ]) + ->exclude([ + 'Pager/Views', + 'ThirdParty', + 'Validation/Views', + ]) + ->append([ + __FILE__, + __DIR__ . '/.php-cs-fixer.no-header.php', + __DIR__ . '/.php-cs-fixer.tests.php', + __DIR__ . '/.php-cs-fixer.user-guide.php', + __DIR__ . '/preload.php', + __DIR__ . '/rector.php', + __DIR__ . '/spark', + ]); + +$overrides = []; + +$options = [ + 'cacheFile' => 'build/.php-cs-fixer.cache', + 'finder' => $finder, +]; + +return Factory::create(new CodeIgniter4(), $overrides, $options)->forLibrary( + 'CodeIgniter 4 framework', + 'CodeIgniter Foundation', + 'admin@codeigniter.com', +); diff --git a/.php-cs-fixer.no-header.php b/.php-cs-fixer.no-header.php new file mode 100644 index 000000000000..f3bc97dc78ac --- /dev/null +++ b/.php-cs-fixer.no-header.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +use PhpCsFixer\ConfigInterface; +use PhpCsFixer\Finder; + +/** @var ConfigInterface $config */ +$config = require __DIR__ . '/.php-cs-fixer.dist.php'; + +$finder = Finder::create() + ->files() + ->in([ + __DIR__ . '/admin', + __DIR__ . '/app', + __DIR__ . '/public', + ]) + ->exclude(['Views/errors/html']) + ->append([ + __DIR__ . '/admin/starter/builds', + ]); + +$overrides = [ + 'header_comment' => false, +]; + +return $config + ->setFinder($finder) + ->setCacheFile('build/.php-cs-fixer.no-header.cache') + ->setRules(array_merge($config->getRules(), $overrides)); diff --git a/.php-cs-fixer.tests.php b/.php-cs-fixer.tests.php new file mode 100644 index 000000000000..ca74f7f5e404 --- /dev/null +++ b/.php-cs-fixer.tests.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +use PhpCsFixer\Config; +use PhpCsFixer\Finder; +use Utils\PhpCsFixer\CodeIgniterRuleCustomisationPolicy; + +/** @var Config $config */ +$config = require __DIR__ . '/.php-cs-fixer.dist.php'; + +$finder = Finder::create() + ->files() + ->in([ + __DIR__ . '/tests', + ]) + ->notPath([ + '_support/View/Cells/multiplier.php', + '_support/View/Cells/colors.php', + '_support/View/Cells/addition.php', + ]); + +$overrides = [ + 'declare_strict_types' => true, + 'phpdoc_to_return_type' => true, + 'void_return' => true, +]; + +return $config + ->setRuleCustomisationPolicy(new CodeIgniterRuleCustomisationPolicy()) + ->setFinder($finder) + ->setCacheFile('build/.php-cs-fixer.tests.cache') + ->setRules(array_merge($config->getRules(), $overrides)); diff --git a/.php-cs-fixer.user-guide.php b/.php-cs-fixer.user-guide.php new file mode 100644 index 000000000000..6b925ee8b0b7 --- /dev/null +++ b/.php-cs-fixer.user-guide.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +use PhpCsFixer\ConfigInterface; +use PhpCsFixer\Finder; + +/** @var ConfigInterface $config */ +$config = require __DIR__ . '/.php-cs-fixer.dist.php'; + +$finder = Finder::create() + ->files() + ->in([ + __DIR__ . '/user_guide_src/source', + ]) + ->notPath([ + 'ci3sample/', + 'database/query_builder/075.php', + 'libraries/sessions/016.php', + 'outgoing/response/031.php', + 'outgoing/response/032.php', + ]); + +$overrides = [ + 'echo_tag_syntax' => false, + 'header_comment' => false, + 'php_unit_internal_class' => false, + 'no_unused_imports' => false, + 'class_attributes_separation' => false, + 'fully_qualified_strict_types' => [ + 'import_symbols' => false, + 'leading_backslash_in_global_namespace' => true, + ], +]; + +return $config + ->setFinder($finder) + ->setCacheFile('build/.php-cs-fixer.user-guide.cache') + ->setRules(array_merge($config->getRules(), $overrides)); diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5862edacab8b..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: php - -php: - - 7 - -script: - - php phpunit.phar diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000000..82d3974606b0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,266 @@ +# Changelog + +## [v4.7.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.4) (2026-07-07) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.3...v4.7.4) + +### Security + +* **IncomingRequest:** *HTTPS detection via client-supplied headers* was fixed. + ``IncomingRequest::isSecure()`` now trusts the ``X-Forwarded-Proto`` and + ``Front-End-Https`` headers only when the request comes from a trusted proxy + configured in ``Config\App::$proxyIPs``. + See the `Security advisory GHSA-7wmf-pw8j-mc78 `_ + for more information. + +* **Query Builder:** Fixed a SQL injection vulnerability in ``deleteBatch()``. + When ``deleteBatch()`` was used together with ``where()`` conditions, the + bound values from the WHERE clause were substituted into the generated SQL + with their escape flag ignored, so they were never escaped or quoted. The + WHERE binds are now escaped in the same way as a regular ``delete()``. + + See the `Security advisory GHSA-c9w5-rwh3-7pm9 `_ + for more information. + +* **UploadedFile:** ``UploadedFile::move()`` now sanitizes the client-provided + filename when called without a second argument. Previously, the unsanitized + client filename was used as the default, allowing path traversal sequences + (e.g. ``../../public/shell.php``) to write the uploaded file outside the + intended directory. A name explicitly passed as the second argument is not + sanitized and remains the caller's responsibility. + See the `Security advisory GHSA-hhmc-q9hp-r662 `_ + for more information. + +* **Validation:** The ``is_image`` and ``mime_in`` file upload validation rules + now also verify non-empty client filename extensions. Previously, these rules + classified an upload solely by its content-derived MIME type, so a file with a + dangerous client extension (for example, a ``.php`` file prepended with image + magic bytes) could pass validation while keeping its original extension on + disk. + See the `Security advisory GHSA-mmj4-63m4-r6h5 `_ + for more information. + + The ``is_image`` rule now rejects uploads when a non-empty client filename + extension is not an image extension. The ``mime_in`` rule now rejects uploads + when a non-empty client filename extension does not match the detected file + content, matching the same extension/content agreement used by ``ext_in``. + Files uploaded without any extension (such as JavaScript ``Blob`` uploads) + are still accepted, since the rules validate the file content. + +### Fixed Bugs + +* fix: prevent updateBatch with existing where conditions by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10236 +* fix: detect Safari version from Version token by @memleakd in https://github.com/codeigniter4/CodeIgniter4/pull/10251 +* fix: nested transformer request scope leakage by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10281 +* fix(model): pass $recursive parameter to parent in objectToRawArray by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10258 +* fix: preserve enclosing stream filter when using `MockInputOutput` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10307 +* fix: skip already-translated keys in `spark lang:find` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10308 +* fix(Filters): check both keys and values in `InvalidChars` arrays by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10303 +* fix: use dynamic lockMaxRetries limit in RedisHandler by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10295 +* fix: preserve sub-namespace when generating Entity from Model (i.e., `--return entity`) by @xgrind in https://github.com/codeigniter4/CodeIgniter4/pull/10232 +* fix: `env()` TypeError for non-string `$_SERVER` values + `esc()` fixes by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10305 +* fix: unify casing in BaseService::injectMock by @ThomasMeschke in https://github.com/codeigniter4/CodeIgniter4/pull/10316 +* fix: normalize SodiumHandler params and padding failures by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10321 +* fix(Validation): correct required_without logic and prevent array key warnings by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10328 +* fix: handle null ini values in phpini check by @Will-thom in https://github.com/codeigniter4/CodeIgniter4/pull/10233 +* fix: preserve zero values in XML export by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10367 +* fix: support array indexes in `getPostGet()` and `getGetPost()` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10362 + +### Refactoring + +* refactor: narrow `Image` original dimension types to `int` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10326 +* refactor(HTTP): optimize file path parsing in `download()` method by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10330 +* refactor(database): optimize groupGetType by caching it inside BaseBuilder loops by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10340 +* refactor: bump to phpstan-codeigniter v2.1 by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10312 +* refactor: replace type `mixed` with more specific types by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10345 +* refactor: optimize `prepQuotedPrintable()` with hash lookup and int-length tracking by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10344 +* refactor: add precise `array` phpdocs for `CLI` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10354 +* refactor(database): optimize `_processForeignKeys()` string allocations and loop invariants by @gr8man in https://github.com/codeigniter4/CodeIgniter4/pull/10351 + +## [v4.7.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.3) (2026-05-22) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.2...v4.7.3) + +### Security + +* **Validation**: *Uploaded file extension validation bypass in `ext_in` rule* + The ``ext_in`` file upload validation rule now validates the client filename extension and verifies that it + matches the detected MIME type. Previously, ``ext_in`` only checked the MIME-derived guessed extension, so + a file with a mismatched client extension could pass validation. + + See the [GHSA-2gr4-ppc7-7mhx security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-2gr4-ppc7-7mhx) for more information. Credits to @z3moo and @teebow1e for reporting the issue. + +### Fixed Bugs + +* fix: make Autoloader composer path injectable to fix parallel test race condition by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10082 +* fix: store SPL closures in `register()` so `unregister()` can remove them by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10097 +* fix: ensure output buffer is closed after use of `command()` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10099 +* fix: preserve null values in Validation::getValidated() by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10101 +* fix: refactor inconsistent behavior on `CLI::write()` and `CLI::error()` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10106 +* fix: ensure calling `env` command with options only would not throw by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10114 +* fix: suppress stty stderr leak in `CLI::generateDimensions()` when stdin is not a TTY by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10124 +* fix: reset Kint CSP state in worker mode by @memleakd in https://github.com/codeigniter4/CodeIgniter4/pull/10139 +* fix: make `Time::createFromTimestamp` locale-independent by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10151 +* fix: SQLSRV driver's `decrement()` method by @patel-vansh in https://github.com/codeigniter4/CodeIgniter4/pull/10155 +* fix: suppress tput stderr leak when TERM is not present by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10167 +* fix: support third-party loggers in toolbar logs collector by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10173 +* fix: PostgreSQL Builder's `increment()` and `decrement()` methods not working for numeric columns by @patel-vansh in https://github.com/codeigniter4/CodeIgniter4/pull/10172 +* fix: preserve cached table list shape by @memleakd in https://github.com/codeigniter4/CodeIgniter4/pull/10179 +* fix: harden regex matching on `key:generate` command by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10183 +* fix: restore deep dot-notation traversal in `Language::getLine()` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10189 +* fix: make frankenphp-worker.php template idempotent on watcher restart by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10191 +* fix: `Entity::normalizeValue()` must handle `UnitEnum` before `toArray()` by @maniaba in https://github.com/codeigniter4/CodeIgniter4/pull/10137 +* fix: recognize off zlib output compression value by @memleakd in https://github.com/codeigniter4/CodeIgniter4/pull/10193 +* fix: escape `--host` option in `serve` command by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10203 + +### Refactoring + +* refactor: add full testing for `logs:clear` command by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10090 +* refactor: add full testing for `debugbar:clear` command by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10093 +* refactor: pass `--do-not-cache-result` to prevent shared cache corruption by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10098 +* refactor: add full testing for `cache:clear` command by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10094 +* refactor: rename `-h` option of `routes` command as `--handler` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10113 +* refactor: further rename `--handler` to `--sort-by-handler` for `routes` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10125 +* refactor: UX: `ClearLogs::execute()` error message is misleading after interactive `'n'` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10126 +* refactor: simplify `FileLocator::listFiles()` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10142 +* refactor: reduce PHPStan child return type baseline by @memleakd in https://github.com/codeigniter4/CodeIgniter4/pull/10165 +* refactor: remove PHPStan callable signature baseline by @memleakd in https://github.com/codeigniter4/CodeIgniter4/pull/10166 + +## [v4.7.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.2) (2026-03-24) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.1...v4.7.2) + +### Fixed Bugs + +* fix: preserve JSON body when CSRF token is sent in header by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10064 + +## [v4.7.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.1) (2026-03-22) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.7.0...v4.7.1) + +### Breaking Changes + +* fix: SQLite3 config type handling for `.env` overrides by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10037 + +### Fixed Bugs + +* fix: escape CSP nonce attributes in JSON responses by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9938 +* fix: correct `savePath` check in `MemcachedHandler` constructor by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9941 +* fix: preserve index field in `updateBatch()` when `updateOnlyChanged` is `true` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9944 +* fix: Hardcoded CSP Nonce Tags in ResponseTrait by @patel-vansh in https://github.com/codeigniter4/CodeIgniter4/pull/9937 +* fix: initialize standalone toolbar by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9950 +* fix: add fallback for `appOverridesFolder` config in View by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9958 +* fix: avoid double-prefixing in `BaseConnection::callFunction()` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9959 +* fix: generate inputs for all route params in Debug Toolbar by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9964 +* fix: preserve Postgre casts when converting named placeholders in prepared queries by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9960 +* fix: prevent extra query and invalid size in `Model::chunk()` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9961 +* fix: worker mode events cleanup by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9997 +* fix: add nonce to script-src-elem and style-src-elem when configured by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9999 +* fix: `FeatureTestTrait::withRoutes()` may throw all sorts of errors on invalid HTTP methods by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/10004 +* fix: validation when key does not exists by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10006 +* fix: handle HTTP/2 responses without a reason phrase in CURLRequest by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10050 + +### Refactoring + +* chore: signature for the `$headers` param in `FeatureTestTrait::withHeaders()` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9932 +* refactor: implement development versions for `CodeIgniter::CI_VERSION` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9951 +* feat: Add `builds next` option by @neznaika0 in https://github.com/codeigniter4/CodeIgniter4/pull/9946 +* refactor: use `__unserialize` instead of `__wakeup` in `TimeTrait` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9957 +* refactor: remove `Exceptions::isImplicitNullableDeprecationError` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9965 +* refactor: fix `Security` test fail by itself by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9969 +* refactor: make random-order API tests deterministic by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9983 +* refactor: make random-order CLI tests deterministic by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9998 +* refactor: fix phpstan no type specified ValidationModelTest by @adiprsa in https://github.com/codeigniter4/CodeIgniter4/pull/10008 +* refactor: fix dependency on test execution order by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10014 +* refactor: update tests with old entities definition by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/10026 + +## [v4.7.0](https://github.com/codeigniter4/CodeIgniter4/tree/v4.7.0) (2026-02-01) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.6.5...v4.7.0) + +### Breaking Changes + +* feat: require double curly braces for placeholders in `regex_match` rule by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9597 +* feat(cache): add `deleteMatching` method definition in CacheInterface by @yassinedoghri in https://github.com/codeigniter4/CodeIgniter4/pull/9809 +* feat(cache): add native types to all CacheInterface methods by @yassinedoghri in https://github.com/codeigniter4/CodeIgniter4/pull/9811 +* feat(entity): deep change tracking for objects and arrays by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9779 +* feat(model): primary key validation by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9840 +* feat(entity): properly convert arrays of entities in `toRawArray()` by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9841 +* feat: add configurable status code filtering for `PageCache` filter by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9856 +* fix: inconsistent `key` handling in encryption by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9868 +* refactor: complete `QueryInterface` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9892 +* feat: add `remember()` to `CacheInterface` by @datamweb in https://github.com/codeigniter4/CodeIgniter4/pull/9875 +* refactor: Use native return types instead of using `#[ReturnTypeWillChange]` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9900 + +### Fixed Bugs + +* fix: ucfirst all cookie samesite values by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9564 +* fix: controller attribute filters with parameters by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9769 +* fix: Fixed test Transformers by @neznaika0 in https://github.com/codeigniter4/CodeIgniter4/pull/9778 +* fix: signal trait by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9846 + +### New Features + +* feat: signals by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9690 +* feat(app): Added controller attributes by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/9745 +* feat: API transformers by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/9763 +* feat: FrankenPHP Worker Mode by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9889 + +### Enhancements + +* feat: add email/smtp plain auth method by @ip-qi in https://github.com/codeigniter4/CodeIgniter4/pull/9462 +* feat: rewrite `ImageMagickHandler` to rely solely on the PHP `imagick` extension by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9526 +* feat: add `Time::addCalendarMonths()` and `Time::subCalendarMonths()` methods by @christianberkman in https://github.com/codeigniter4/CodeIgniter4/pull/9528 +* feat: add `clearMetadata()` method to provide privacy options when using imagick handler by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9538 +* feat: add `dns_cache_timeout` for option `CURLRequest` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9553 +* feat: added `fresh_connect` options to `CURLRequest` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9559 +* feat: update `CookieInterface::EXPIRES_FORMAT` to use date format per RFC 7231 by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9563 +* feat: share connection & DNS Cache to `CURLRequest` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9557 +* feat: add option to change default behaviour of `JSONFormatter` max depth by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9585 +* feat: customizable `.env` directory path by @totoprayogo1916 in https://github.com/codeigniter4/CodeIgniter4/pull/9631 +* feat: migrations lock by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9660 +* feat: uniform rendering of stack trace from failed DB operations by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9677 +* feat: make `insertBatch()` and `updateBatch()` respect model rules by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9708 +* feat: add enum casting by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9752 +* feat(app): Added pagination response to API ResponseTrait by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/9758 +* feat: update robots definition for `UserAgent` class by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9782 +* feat: added `async` & `persistent` options to Cache Redis by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9792 +* feat: Add support for HTTP status in `ResponseCache` by @sk757a in https://github.com/codeigniter4/CodeIgniter4/pull/9855 +* feat: prevent `Maximum call stack size exceeded` on client-managed requests by @datamweb in https://github.com/codeigniter4/CodeIgniter4/pull/9852 +* feat: add `isPast()` and `isFuture()` time convenience methods by @datamweb in https://github.com/codeigniter4/CodeIgniter4/pull/9861 +* feat: allow overriding namespaced views via `app/Views` directory by @datamweb in https://github.com/codeigniter4/CodeIgniter4/pull/9860 +* feat: make DebugToolbar smarter about detecting binary/streamed responses by @datamweb in https://github.com/codeigniter4/CodeIgniter4/pull/9862 +* feat: complete `Superglobals` implementation by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9858 +* feat: encryption key rotation by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9870 +* feat: APCu caching driver by @sk757a in https://github.com/codeigniter4/CodeIgniter4/pull/9874 +* feat: added ``persistent`` config item to redis handler `Session` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9793 +* feat: Add CSP3 `script-src-elem` directive by @mark-unwin in https://github.com/codeigniter4/CodeIgniter4/pull/9722 +* feat: Add support for CSP3 keyword-sources by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9906 +* feat: enclose hash-based CSP directive values in single quotes by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9908 +* feat: add support for more CSP3 directives by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9909 +* feat: add support for CSP3 `report-to` directive by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9910 + +### Refactoring + +* refactor: cleanup code in `Email` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9570 +* refactor: remove deprecated types in random_string() helper by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9592 +* refactor: do not use future-deprecated `DATE_RFC7231` constant by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9657 +* refactor: remove `curl_close` has no effect since PHP 8.0 by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9683 +* refactor: remove `finfo_close` has no effect since PHP 8.1 by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9684 +* refactor: remove `imagedestroy` has no effect since PHP 8.0 by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9688 +* refactor: deprecated PHP 8.5 constant `FILTER_DEFAULT` for `filter_*()` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9699 +* chore: bump minimum required `PHP 8.2` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/9701 +* refactor: add the `SensitiveParameter` attribute to methods dealing with sensitive info by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9710 +* fix: Remove check ext-json by @neznaika0 in https://github.com/codeigniter4/CodeIgniter4/pull/9713 +* refactor(app): Standardize subdomain detection logic by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/9751 +* refactor: Types for `BaseModel`, `Model` and dependencies by @neznaika0 in https://github.com/codeigniter4/CodeIgniter4/pull/9830 +* chore: remove IncomingRequest deprecations by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/9851 +* refactor: Session library by @neznaika0 in https://github.com/codeigniter4/CodeIgniter4/pull/9831 +* refactor: Superglobals - remove property promotion and fix PHPDocs by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9871 +* refactor: Rework `Entity` class by @neznaika0 in https://github.com/codeigniter4/CodeIgniter4/pull/9878 +* refactor: compare `$db->connID` to `false` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9891 +* refactor: cleanup `ContentSecurityPolicy` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9904 +* refactor: deprecate `CodeIgniter\HTTP\ContentSecurityPolicy::$nonces` since never used by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/9905 + +For the changelog of v4.6, see [CHANGELOG_4.6.md](./changelogs/CHANGELOG_4.6.md).
+For the changelog of v4.5, see [CHANGELOG_4.5.md](./changelogs/CHANGELOG_4.5.md).
+For the changelog of v4.4, see [CHANGELOG_4.4.md](./changelogs/CHANGELOG_4.4.md).
+For the changelog of v4.3, see [CHANGELOG_4.3.md](./changelogs/CHANGELOG_4.3.md).
+For the changelog of v4.2, see [CHANGELOG_4.2.md](./changelogs/CHANGELOG_4.2.md).
+For the changelog of v4.1, see [CHANGELOG_4.1.md](./changelogs/CHANGELOG_4.1.md).
+For the changelog of v4.0, see [CHANGELOG_4.0.md](./changelogs/CHANGELOG_4.0.md). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..047a477a2945 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at admin@codeigniter.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..3688517a265a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,6 @@ +# Contributing to CodeIgniter4 + +CodeIgniter is a community driven project and accepts contributions of +code and documentation from the community. + +If you'd like to contribute, please read the [Contributing to CodeIgniter](./contributing/README.md). diff --git a/DCO.txt b/DCO.txt deleted file mode 100644 index a404c0d38b0d..000000000000 --- a/DCO.txt +++ /dev/null @@ -1,25 +0,0 @@ -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(1) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(2) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(3) The contribution was provided directly to me by some other - person who certified (1), (2) or (3) and I have not modified - it. - -(4) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..24728f607f02 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014-2019 British Columbia Institute of Technology +Copyright (c) 2019-present CodeIgniter Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 93feaa74fc57..6a4e1025a8f5 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,110 @@ # CodeIgniter 4 Development -[![Build Status](https://travis-ci.org/lonnieezell/CodeIgniter4.svg?branch=develop)](https://travis-ci.org/lonnieezell/CodeIgniter4) +[![PHPUnit](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/test-phpunit.yml/badge.svg)](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/test-phpunit.yml) +[![PHPStan](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/test-phpstan.yml/badge.svg)](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/test-phpstan.yml) +[![Psalm](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/test-psalm.yml/badge.svg)](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/test-psalm.yml) +[![Coverage Status](https://coveralls.io/repos/github/codeigniter4/CodeIgniter4/badge.svg?branch=develop)](https://coveralls.io/github/codeigniter4/CodeIgniter4?branch=develop) +[![Downloads](https://poser.pugx.org/codeigniter4/framework/downloads)](https://packagist.org/packages/codeigniter4/framework) +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/codeigniter4/CodeIgniter4)](https://packagist.org/packages/codeigniter4/framework) +[![GitHub stars](https://img.shields.io/github/stars/codeigniter4/CodeIgniter4)](https://packagist.org/packages/codeigniter4/framework) +[![GitHub license](https://img.shields.io/github/license/codeigniter4/CodeIgniter4)](https://github.com/codeigniter4/CodeIgniter4/blob/develop/LICENSE) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/codeigniter4/CodeIgniter4/pulls) +
-This repo contains work that may or may not make it into the official version of CodeIgniter. +## What is CodeIgniter? -More information about the plans for version 4 can be found in [the announcement](http://forum.codeigniter.com/thread-62615.html) on the forums. +CodeIgniter is a PHP full-stack web framework that is light, fast, flexible and secure. +More information can be found at the [official site](https://codeigniter.com). + +This repository holds the source code for CodeIgniter 4 only. +Version 4 is a complete rewrite to bring the quality and the code into a more modern version, +while still keeping as many of the things intact that has made people love the framework over the years. + +More information about the plans for version 4 can be found in [CodeIgniter 4](https://forum.codeigniter.com/forumdisplay.php?fid=28) on the forums. + +### Documentation + +The [User Guide](https://codeigniter.com/user_guide/) is the primary documentation for CodeIgniter 4. + +You will also find the [current **in-progress** User Guide](https://codeigniter4.github.io/CodeIgniter4/). +As with the rest of the framework, it is a work in progress, and will see changes over time to structure, explanations, etc. + +You might also be interested in the [API documentation](https://codeigniter4.github.io/api/) for the framework components. + +## Important Change with index.php + +`index.php` is no longer in the root of the project! It has been moved inside the *public* folder, +for better security and separation of components. + +This means that you should configure your web server to "point" to your project's *public* folder, and +not to the project root. A better practice would be to configure a virtual host to point there. A poor practice would be to point your web server to the project root and expect to enter *public/...*, as the rest of your logic and the +framework are exposed. + +**Please** read the user guide for a better explanation of how CI4 works! + +## Repository Management + +CodeIgniter is developed completely on a volunteer basis. As such, please give up to 7 days +for your issues to be reviewed. If you haven't heard from one of the team in that time period, +feel free to leave a comment on the issue so that it gets brought back to our attention. + +> [!IMPORTANT] +> We use GitHub issues to track **BUGS** and to track approved **DEVELOPMENT** work packages. +> We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss +> FEATURE REQUESTS. + +If you raise an issue here that pertains to support or a feature request, it will +be closed! If you are not sure if you have found a bug, raise a thread on the forum first - +someone else may have encountered the same thing. + +Before raising a new GitHub issue, please check that your bug hasn't already +been reported or fixed. + +We use pull requests (PRs) for CONTRIBUTIONS to the repository. +We are looking for contributions that address one of the reported bugs or +approved work packages. + +Do not use a PR as a form of feature request. +Unsolicited contributions will only be considered if they fit nicely +into the framework roadmap. +Remember that some components that were part of CodeIgniter 3 are being moved +to optional packages, with their own repository. ## Contributing -We are not accepting contributions from the public until a stable enough base has been formed, and our plans fleshed out and things settle down a little bit. At that point, we will welcome your comments and help creating the best framework for our community. + +We **are** accepting contributions from the community! It doesn't matter whether you can code, write documentation, or help find bugs, +all contributions are welcome. + +Please read the [*Contributing to CodeIgniter*](https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/README.md). + +CodeIgniter has had thousands on contributions from people since its creation. This project would not be what it is without them. + + + + + +Made with [contrib.rocks](https://contrib.rocks). ## Server Requirements -PHP version 7 is required. -You can obtain a working Vagrant box from Rasmus Lerdorf [here](https://github.com/rlerdorf/php7dev). +PHP version 8.2 or higher is required, with the following extensions installed: -## Tests -Tests are done using the latest version of PHPUnit, which is currently 4.8.x. It is easiest to [install it globally using Composer](https://phpunit.de/manual/current/en/installation.html#installation.composer). Tests are restricted to classes within the `/system` directory, and the directory structure should of the tests should mimic the directory structure of `/system`. +- [intl](http://php.net/manual/en/intl.requirements.php) +- [mbstring](http://php.net/manual/en/mbstring.installation.php) -### Running Tests -Tests are run from the command line from the project root - the same folder that has the `phpunit.xml` file in it. +> [!WARNING] +> - The end of life date for PHP 7.4 was November 28, 2022. +> - The end of life date for PHP 8.0 was November 26, 2023. +> - The end of life date for PHP 8.1 was December 31, 2025. +> - If you are still using a PHP version below 8.2, you should upgrade immediately. +> - The end of life date for PHP 8.2 will be December 31, 2026. - $ phpunit +Additionally, make sure that the following extensions are enabled in your PHP: -Simply calling this will run the entire test suite. +- json (enabled by default - don't turn it off) +- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) if you plan to use MySQL +- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library -### Checking Code Coverage -Code Coverage requires that XDebug is also install and running in your current PHP setup. The following command will run coverage, provide a coverage report on screen, in a text file (coverage.txt) and within the /coverage directory. +## Running CodeIgniter Tests - $ phpunit --colors --coverage-text=coverage.txt --coverage-html=coverage/ \ No newline at end of file +Information on running the CodeIgniter test suite can be found in the [README.md](tests/README.md) file in the tests directory. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000000..87b894ee713c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +The development team and community take all security issues seriously. **Please do not make public any uncovered flaws.** + +## Reporting a Vulnerability + +Thank you for improving the security of our code! Any assistance in removing security flaws will be acknowledged. + +**Please report security flaws by emailing the development team directly: security@codeigniter.com**. + +The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating +the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the +progress towards a fix and full announcement, and may ask for additional information or guidance. + +## Disclosure Policy + +When the security team receives a security bug report, they will assign it to a primary handler. +This person will coordinate the fix and release process, involving the following steps: + +- Confirm the problem and determine the affected versions. +- Audit code to find any potential similar problems. +- Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible. +- Publish security advisories at https://github.com/codeigniter4/CodeIgniter4/security/advisories + +## Comments on this Policy + +If you have suggestions on how this process could be improved please submit a Pull Request. diff --git a/admin/README.md b/admin/README.md new file mode 100644 index 000000000000..a4048bf59152 --- /dev/null +++ b/admin/README.md @@ -0,0 +1,81 @@ +# CodeIgniter 4 Admin + +This folder contains tools or docs useful for project maintainers. + +## Repositories inside https://github.com/codeigniter4 + +- **CodeIgniter4** is the main development repository. + It supports issues and pull requests, and has a rule to enforce GPG-signed commits. + In addition to the framework source, it includes unit testing and documentation source. + The three repositories following are built from this one as part of the release workflow. + This repo is meant to be forked by contributors. +- **framework** is the released developer repository. + It contains all the main pieces of the framework that developers would use to + build their apps, but not the framework unit testing or the user guide source. + It is meant to be downloaded by developers, or composer-installed. + This is a read-only repository. +- **appstarter** is the released application starter repository. + It is derived from the framework's `app` and `public` folders, with + a composer requirement dependency to pull in the framework itself. + It is meant to be downloaded or composer-installed. + This is a read-only repository. +- **userguide** is released documentation publishing repository. + It contains built versions of the user guide, corresponding to the + framework releases. + It could be downloaded, forked or potentially composer-installed. + This is a read-only repository. +- **coding-standard** is the coding style standards repository. + It contains PHP-CS-Fixer rules to ensure consistent code style + within the framework itself. + It is meant to be composer-installed. +- **translations** is the repository holding official translations of + the locale-dependent system messages. + It is community-maintained, and accepts issues and pull requests. + It could be downloaded, forked or composer-installed. + +## Contributor Scripts + +- **setup.sh** installs a git pre-commit hook into a contributor's + local clone of their fork of the `CodeIgniter4` repository. +- **pre-commit** runs PHP Lint and PHP CodeSniffer on any files + to be added as part of a git commit, ensuring that they conform to the + framework coding style standards, and automatically fixing what can be. + +## Release Scripts + +The release process is detailed in [RELEASE.md](./RELEASE.md). These scripts +are used as part of that process: + +- **generate-changelog.php** prepends the CHANGELOG.md entry for a new + release, built from GitHub's auto-generated release notes and the + SECURITY section of the version's detailed changelog. + Usage: `php admin/generate-changelog.php 4.x.x [--dry-run]` +- **prepare-release.php** creates the `release-4.x.x` branch and updates + version references in the framework source, the user guide, and the + distribution build script. + Usage: `php admin/prepare-release.php 4.x.x` +- **create-new-changelog.php** creates the changelog and upgrade guide + stubs for the next version and adds them to their index files. + Usage: `php admin/create-new-changelog.php ` +- **check-pr-labels.php** flags PRs merged after a release (default: the + latest) that appear to be missing the labels used to generate the + changelog. + Usage: `php admin/check-pr-labels.php []` +- **update-upgrade-guide.php** fills the "Config" and "All Changes" sections + of the version's upgrade guide with the project space files changed since + the last release. + Usage: `php admin/update-upgrade-guide.php [--dry-run]` +- **sync-release-branches.php** checks out the target branch and merges + upstream and the source branch into it, as done at several points of the + release process. Without `--push`, the result is left unpushed for review. + Usage: `php admin/sync-release-branches.php [--push]` + +## Other Stuff + +- The **framework** and **starter** subfolders contain files that will over-ride + those from the development repository, when the distribution repositories + are built. +- The subfolders inside `admin` contain "next release" files in the case of + `codeigniter4` and over-written distribution files in the other cases. +- The CHANGELOG.md file is generated from GitHub's auto-generated release + notes, as described in [RELEASE.md](./RELEASE.md). diff --git a/admin/RELEASE.md b/admin/RELEASE.md new file mode 100644 index 000000000000..75f2a0fbf35a --- /dev/null +++ b/admin/RELEASE.md @@ -0,0 +1,254 @@ +# Release Process + +> Documentation guide based on the releases of `4.0.5` and `4.1.0` on January 31, 2021 (MGatner) +> +> Updated for `4.5.0` on April 7, 2024 (kenjis) +> Updated for `4.6.0` on January 19, 2025 (paulbalandan) +> Updated for `4.7.5` on July 8, 2026 (paulbalandan) + +## Notation + +- `4.x.x`: The new release version. (e.g., `4.5.3`) +- `4.y`: The next minor version. (e.g., `4.6`) +- `4.z`: The next next minor version. (e.g., `4.7`) + +> [!NOTE] +> Copy this file, and replace the versions above with the actual versions. + +## Merge `develop` branch into next minor version branch `4.y` + +Before starting the release process, if there are commits in `develop` branch that +are not merged into `4.y` branch, merge them. This is because if conflicts occur, +merging will take time. + +* [ ] Merge `develop` into `4.y`: + ```console + php admin/sync-release-branches.php 4.y develop --push + ``` + +## [Minor version only] Merge minor version branch into `develop` + +If you release a new minor version. + +* [ ] Create PR to merge `4.y` into `develop`: + * Title: `4.y.0 Merge code` + * Description: blank +* [ ] Rename the current minor version (e.g., `4.5`) in Setting > Branches > + "Branch protection rules" to the next minor version (e.g. `4.5` → `4.6`). +* [ ] Delete the merged `4.y` branch (this closes all PRs to the branch). + +## Preparation + +Work off direct clones of the repos so the release branches persist for a time. + +* [ ] Clone both **codeigniter4/CodeIgniter4** and **codeigniter4/userguide** and + resolve any necessary PRs + ```console + rm -rf CodeIgniter4.bk userguide.bk + mv CodeIgniter4 CodeIgniter4.bk + mv userguide userguide.bk + git clone git@github.com:codeigniter4/CodeIgniter4.git + git clone git@github.com:codeigniter4/userguide.git + ``` +* [ ] Vet the **admin/** folders for any removed hidden files (Action deploy scripts + *do not remove these*) + ```console + cd CodeIgniter4 + git diff --name-status origin/master admin/ + ``` + +## Changelog + +When generating the changelog, each pull request to be included must have one of +the following [labels](https://github.com/codeigniter4/CodeIgniter4/labels): +- **bug** ... PRs that fix bugs +- **enhancement** ... PRs to improve existing functionalities +- **new feature** ... PRs for new features +- **refactor** ... PRs to refactor + +PRs with breaking changes must have the following additional label: +- **breaking change** ... PRs that may break existing functionalities + +At any time, missing labels can be checked for with +`php admin/check-pr-labels.php`, which flags PRs merged since the last release +that appear to lack a changelog label. It requires the authenticated +[GitHub CLI](https://cli.github.com/). + +### Generate Changelog + +The changelog is generated from GitHub's auto-generated release notes with a +script. It requires the authenticated [GitHub CLI](https://cli.github.com/). + +The script also copies the security fixes from the SECURITY section of +**user_guide_src/source/changelogs/v4.x.x.rst**, so any security advisory PRs +must be merged and documented there before generating the changelog. + +* [ ] Run `php admin/generate-changelog.php 4.x.x --dry-run` and check the output. + * The script lists the PRs that have none of the changelog labels. If any of + them should be included in the changelog, add a label to the PR and run + the script again. +* [ ] Run `php admin/generate-changelog.php 4.x.x` to prepend the new entry to + **CHANGELOG.md**. + +If the script cannot be used, the same notes can be generated from the +[Releases](https://github.com/codeigniter4/CodeIgniter4/releases) page: +click "Draft a new release", choose the tag `v4.x.x` (create new tag on +publish) with target `develop`, and click "Generate release notes". Copy the +resulting contents into **CHANGELOG.md** and adjust the format to match the +existing content. + +## Process + +> [!NOTE] +> Most changes that need noting in the User Guide and docs should have +> been included with their PR, so this process assumes you will not be +> generating much new content. + +* [ ] Merge any security advisory PRs in private forks. +* [ ] Check that **CHANGELOG.md** contains the entry for the new version, generated above. +* [ ] Update **user_guide_src/source/changelogs/v4.x.x.rst** + * Remove the section titles that have no items +* [ ] Update **user_guide_src/source/installation/upgrade_4xx.rst** + * [ ] Run `php admin/update-upgrade-guide.php 4.x.x` to fill in the "Config" and + "All Changes" sections with the project space files changed since the last release. + * The "Config" section is not modified if it already has content. The script + prints any missing entries to merge manually. + * Add notes to the "Config" entries as needed. + * [ ] Remove the section titles that have no items + * [ ] [Minor version only] Update the "from" version in the title, (e.g., `from 4.3.x` → `from 4.3.8`). +* [ ] Run `php admin/prepare-release.php 4.x.x` and push to origin. + * The above command does the following: + * Create a new branch `release-4.x.x` + * Update **system/CodeIgniter.php** with the new version number: + `const CI_VERSION = '4.x.x';` + * Update **user_guide_src/source/conf.py** with the new `version = '4.x'` + and `release = '4.x.x'`. + * Update **user_guide_src/source/changelogs/{version}.rst** + * Set the date to format `Release Date: January 31, 2021` + * Update **phpdoc.dist.xml** with the new `CodeIgniter v4.x API` + and `` + * Update **admin/starter/builds**: + * Set `define('LATEST_RELEASE', '^4.x')` + * Set `define('NEXT_MINOR', '^4.y-dev')`. + * If the major version changes, you need to manually change to `define('NEXT_MINOR', '^5.0-dev')`. + * Commit the changes with `Prep for 4.x.x release` +* [ ] Create a new PR from `release-4.x.x` to `develop`: + * Title: `Prep for 4.x.x release` + * Description: + ``` + Updates changelog and version references for 4.x.x. + + Previous version: #xxxx + Release Code: TODO + New Changelog: TODO + + (plus checklist) + ``` + +* [ ] Let all tests run, then review and merge the PR. +* [ ] Create a new PR from `develop` to `master`: + * Title: `4.x.x Ready code` + * Description: blank +* [ ] Merge the PR and wait for all tests. +* [ ] Create a new Release: + * Choose a tag: `v4.x.x` (Create new tag: v4.x.x on publish) + * Target: `master` + * Title: `CodeIgniter 4.x.x` + * Description: + ``` + CodeIgniter 4.x.x release. + + See the changelog: https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md + + ## New Contributors + * + + **Full Changelog**: https://github.com/codeigniter4/CodeIgniter4/compare/v4.x.w...v4.x.x + ``` + Click the "Generate release notes" button, and get the "New Contributors". +* [ ] Watch the "[Verify Release](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/verify-release.yml)" + workflow and make sure it passes. It does the following: + * Wait for the "[Deploy Distributable Repos](https://github.com/codeigniter4/CodeIgniter4/actions/workflows/deploy-distributables.yml)" + action to update **framework**, **appstarter**, and **userguide** + * Install `appstarter` from Packagist, verify it pulls framework `v4.x.x`, + and run its tests + * Verify that the user guide deployments succeeded ("Deploy Production" and + "pages build and deployment" in the UG repo) and that **CodeIgniter4.x.x.epub** + was added + * If it fails, fix the cause and re-run it via "Run workflow" with the tag `v4.x.x`. +* [ ] Fast-forward `develop` branch to catch the merge commit from `master` + ```console + php admin/sync-release-branches.php develop master --push + ``` +* [ ] Update the next minor version branch `4.y`: + ```console + php admin/sync-release-branches.php 4.y develop --push + ``` +* [ ] [Minor version only] Create the new next minor version branch `4.z`: + ```console + git fetch upstream + git switch develop + git switch -c 4.z + git push upstream HEAD + ``` +* [ ] Request CVEs and publish any security advisories that were resolved from private forks + (note: publishing is restricted to administrators). +* [ ] Announce the release on the forums and Slack channel + (note: this forum is restricted to administrators). + * Make a new topic in the "News & Discussion" forums: + https://forum.codeigniter.com/forum-2.html + * The content is somewhat organic, but should include any major features and + changes as well as a link to the User Guide's changelog +* [ ] Run `php admin/create-new-changelog.php ` + * The above command does the following: + * Create **user_guide_src/source/changelogs/{next_version}.rst** and add it to + **index.rst** (See **next-changelog-*.rst**) + * Create **user_guide_src/source/installation/upgrade_{next_version}.rst** and add it to + **upgrading.rst** (See **next-upgrading-guide.rst**) +* [ ] Create a PR for new changelog and upgrade for the next version + +## Appendix + +### Sphinx Installation + +You may need to install Sphinx and its dependencies prior to building the User +Guide. + +This worked seamlessly on Ubuntu 20.04: +```console +sudo apt install python3-sphinx +sudo pip3 install sphinxcontrib-phpdomain +sudo pip3 install sphinx_rtd_theme +``` + +### Manual User Guide Process + +> [!NOTE] +> This process is normally not needed. The "Deploy Distributable Repos" action +> builds and deploys the User Guide automatically when a release is published. +> Follow these steps only if the automatic deployment fails. + +* [ ] Still in the **CodeIgniter4** repo enter the **user_guide_src** directory +* [ ] Clear out any old build files: `rm -rf build/` +* [ ] Build the HTML version of the User Guide: `make html` +* [ ] Build the ePub version of the User Guide: `make epub` +* [ ] Switch to the **userguide** repo and create a new branch `release-4.x.x` +* [ ] Replace **docs/** with **CodeIgniter4/user_guide_src/build/html** +* [ ] Ensure the file **docs/.nojekyll** exists or GitHub Pages will ignore folders + with an underscore prefix +* [ ] Copy **CodeIgniter4/user_guide_src/build/epub/CodeIgniter.epub** to + **./CodeIgniter4.x.x.epub** +* [ ] Commit the changes with "Update for 4.x.x" and push to origin +* [ ] Create a new PR from `release-4.x.x` to `develop`: + * Title: "Update for 4.x.x" + * Description: blank +* [ ] Merge the PR +* [ ] Create a new Release: + * Version: "v4.x.x" + * Title: "CodeIgniter 4.x.x User Guide" + * Description: "CodeIgniter 4.x.x User Guide" +* [ ] Watch for the "github pages" Environment to make sure the deployment succeeds + +The User Guide website should update itself via the deploy GitHub Action. Should +this fail the server must be updated manually. See repo and hosting details in +the deploy script at the User Guide repo. diff --git a/admin/check-pr-labels.php b/admin/check-pr-labels.php new file mode 100644 index 000000000000..a1a2f7d9c33e --- /dev/null +++ b/admin/check-pr-labels.php @@ -0,0 +1,203 @@ +&1", $repo, $number), $files, $exitCode); + + if ($exitCode !== 0) { + return true; + } + + foreach ($files as $file) { + if (str_starts_with($file, 'system/')) { + return true; + } + } + + return false; +} + +chdir(__DIR__ . '/..'); + +$ansi = stream_isatty(STDOUT); +$repo = 'codeigniter4/CodeIgniter4'; + +// Labels used by the changelog categories. See ".github/release.yml". +$changelogLabels = ['breaking change', 'bug', 'enhancement', 'new feature', 'refactor']; + +// PR title types that map to a changelog label. +$typeToLabel = [ + 'fix' => 'bug', + 'feat' => 'new feature', + 'perf' => 'enhancement', + 'refactor' => 'refactor', +]; + +// PR title types that need no changelog label. +$typesWithoutLabel = ['chore', 'ci', 'docs', 'style', 'test']; + +// Release process PRs carry no labels. +$releaseTitles = '/\A(?:Prep for \d+\.\d+\.\d+ release|\d+\.\d+\.\d+ (?:Ready|Merge) code)\z/'; + +$tag = null; + +foreach (array_slice($argv, 1) as $arg) { + if (preg_match('/\Av?(\d+\.\d+\.\d+)\z/', $arg, $matches) === 1) { + $tag = "v{$matches[1]}"; + + continue; + } + + echo sprintf("Usage: php %s [version]\n", $argv[0]); + echo sprintf("E.g.,: php %s 4.7.3\n", $argv[0]); + echo "Checks the PRs merged after the given release. Defaults to the latest release.\n"; + + exit(1); +} + +$endpoint = $tag === null ? sprintf('repos/%s/releases/latest', $repo) : sprintf('repos/%s/releases/tags/%s', $repo, $tag); +exec(sprintf('gh api %s 2>&1', $endpoint), $output, $exitCode); + +if ($exitCode !== 0) { + echo sprintf("Failed to fetch the release %s from GitHub:\n", $tag ?? '(latest)'); + echo implode("\n", $output) . "\n"; + + exit(1); +} + +$release = json_decode(implode("\n", $output), true); +$since = $release['published_at'] ?? ''; + +echo sprintf("Checking PRs merged since %s (%s).\n", $release['tag_name'], $since); + +$command = sprintf( + 'gh pr list --repo %s --search %s --json number,title,labels,baseRefName,url --limit 300 2>&1', + $repo, + escapeshellarg(sprintf('is:merged -base:master merged:>%s', $since)), +); +exec($command, $prOutput, $exitCode); + +if ($exitCode !== 0) { + echo "Failed to fetch the merged PRs from GitHub:\n"; + echo implode("\n", $prOutput) . "\n"; + + exit(1); +} + +$pulls = json_decode(implode("\n", $prOutput), true); + +if (! is_array($pulls)) { + echo "Unexpected response from GitHub.\n"; + + exit(1); +} + +echo sprintf("Found %d merged PRs.\n\n", count($pulls)); + +$missingLabel = []; +$needManualLook = []; + +foreach ($pulls as $pull) { + $labels = array_column($pull['labels'], 'name'); + $hasLabel = array_intersect($changelogLabels, $labels) !== []; + $type = null; + + if (preg_match('/\A(\w+)(?:\([^)]*\))?:/', $pull['title'], $matches) === 1) { + $type = $matches[1]; + } + + if ($hasLabel || in_array($type, $typesWithoutLabel, true)) { + continue; + } + + if (preg_match($releaseTitles, $pull['title']) === 1) { + continue; + } + + if ($type !== null && isset($typeToLabel[$type])) { + // The refactor label applies only to refactoring in system/. Test-only + // refactors may use the testing label, which is not in the changelog. + if ($type === 'refactor' && ! touches_system_dir($repo, (int) $pull['number'])) { + continue; + } + + $missingLabel[] = [ + 'number' => $pull['number'], + 'title' => $pull['title'], + 'base' => $pull['baseRefName'], + 'url' => $pull['url'], + 'label' => $typeToLabel[$type], + ]; + } else { + $needManualLook[] = [ + 'number' => $pull['number'], + 'title' => $pull['title'], + 'base' => $pull['baseRefName'], + 'url' => $pull['url'], + ]; + } +} + +if ($missingLabel !== []) { + echo color('PRs that appear to be missing a changelog label:', '1', $ansi) . "\n"; + + foreach ($missingLabel as $pull) { + echo sprintf("%s [suggested: %s]\n", format_pull_line($pull, $ansi), color($pull['label'], '33', $ansi)); + } + + echo "\nTo add the suggested labels, run the following commands (drop any that are not warranted):\n"; + + foreach ($missingLabel as $pull) { + echo sprintf("gh pr edit %d --repo %s --add-label \"%s\"\n", $pull['number'], $repo, $pull['label']); + } + + echo "\n"; +} + +if ($needManualLook !== []) { + echo color('PRs with no changelog label and no recognized title type (check manually):', '1', $ansi) . "\n"; + + foreach ($needManualLook as $pull) { + echo format_pull_line($pull, $ansi) . "\n"; + } + + echo "\n"; +} + +if ($missingLabel === []) { + echo color('No PRs are missing changelog labels.', '32', $ansi) . "\n"; + + exit(0); +} + +exit(1); diff --git a/admin/create-new-changelog.php b/admin/create-new-changelog.php new file mode 100644 index 000000000000..32ac5fdaa52c --- /dev/null +++ b/admin/create-new-changelog.php @@ -0,0 +1,99 @@ + [--dry-run]\n"; + echo "E.g. : php {$argv[0]} 4.4.3 4.4.4 --dry-run\n"; + + exit(1); +} + +// Gets version number from argument. +$currentVersion = $argv[1]; // e.g., '4.4.3' +$currentVersionParts = explode('.', $currentVersion, 3); +$currentMinorVersion = $currentVersionParts[0] . '.' . $currentVersionParts[1]; +$newVersion = $argv[2]; // e.g., '4.4.4' +$newVersionParts = explode('.', $newVersion, 3); +$newMinorVersion = $newVersionParts[0] . '.' . $newVersionParts[1]; +$isMinorUpdate = $currentMinorVersion !== $newMinorVersion; + +// Creates a branch for release +if (! in_array('--dry-run', $argv, true)) { + if (! $isMinorUpdate) { + system('git switch develop'); + } + + system("git switch -c docs-changelog-{$newVersion}"); + system("git switch docs-changelog-{$newVersion}"); +} + +// Copy changelog +$newChangelog = "./user_guide_src/source/changelogs/v{$newVersion}.rst"; +$changelogIndex = './user_guide_src/source/changelogs/index.rst'; + +if ($isMinorUpdate) { + copy('./admin/next-changelog-minor.rst', $newChangelog); +} else { + copy('./admin/next-changelog-patch.rst', $newChangelog); +} + +// Replace version in CodeIgniter.php to {version}-dev. +replace_file_content( + './system/CodeIgniter.php', + '/public const CI_VERSION = \'.*?\';/u', + "public const CI_VERSION = '{$newVersion}-dev';", +); + +// Add changelog to index.rst. +replace_file_content( + $changelogIndex, + '/\.\. toctree::\n :titlesonly:\n/u', + ".. toctree::\n :titlesonly:\n\n v{$newVersion}", +); + +// Replace {version} +$underline = str_repeat('#', mb_strlen("Version {$newVersion}")); +replace_file_content( + $newChangelog, + '/#################\nVersion {version}\n#################/u', + "{$underline}\nVersion {$newVersion}\n{$underline}", +); +replace_file_content($newChangelog, '/{version}/u', $newVersion); + +// Copy upgrading +$versionWithoutDots = str_replace('.', '', $newVersion); +$newUpgrading = "./user_guide_src/source/installation/upgrade_{$versionWithoutDots}.rst"; +$upgradingIndex = './user_guide_src/source/installation/upgrading.rst'; +copy('./admin/next-upgrading-guide.rst', $newUpgrading); + +// Add upgrading to upgrading.rst. +replace_file_content( + $upgradingIndex, + '/ backward_compatibility_notes\n/u', + " backward_compatibility_notes\n\n upgrade_{$versionWithoutDots}", +); + +// Replace {version} +$underline = str_repeat('#', mb_strlen("Upgrading from {$currentVersion} to {$newVersion}")); +replace_file_content( + $newUpgrading, + '/##############################\nUpgrading from {version} to {version}\n##############################/u', + "{$underline}\nUpgrading from {$currentVersion} to {$newVersion}\n{$underline}", +); + +if (! in_array('--dry-run', $argv, true)) { + system('git add ./system/CodeIgniter.php'); + system("git add {$newChangelog} {$changelogIndex}"); + system("git add {$newUpgrading} {$upgradingIndex}"); + system("git commit -m \"docs: add changelog and upgrade for v{$newVersion}\""); +} diff --git a/admin/css/debug-toolbar/README.md b/admin/css/debug-toolbar/README.md new file mode 100644 index 000000000000..2c70e8147eae --- /dev/null +++ b/admin/css/debug-toolbar/README.md @@ -0,0 +1 @@ +See [contributing/css.md](../../../contributing/css.md). diff --git a/admin/css/debug-toolbar/_graphic-charter.scss b/admin/css/debug-toolbar/_graphic-charter.scss new file mode 100644 index 000000000000..522f07f6e255 --- /dev/null +++ b/admin/css/debug-toolbar/_graphic-charter.scss @@ -0,0 +1,20 @@ +// COLORS +// ========================================================================== */ + +// Themes +$t-dark: #252525; +$t-light: #FFFFFF; + +// Glossy colors +$g-blue: #5BC0DE; +$g-gray: #434343; +$g-green: #9ACE25; +$g-orange: #DD8615; +$g-red: #DD4814; + +// Matt colors +$m-blue: #D8EAF0; +$m-gray: #DFDFDF; +$m-green: #DFF0D8; +$m-orange: #FDC894; +$m-red: #EF9090; diff --git a/admin/css/debug-toolbar/_mixins.scss b/admin/css/debug-toolbar/_mixins.scss new file mode 100644 index 000000000000..69af2b67c475 --- /dev/null +++ b/admin/css/debug-toolbar/_mixins.scss @@ -0,0 +1,14 @@ +// MIXINS +// ========================================================================== */ + +@mixin border-radius($radius) { + border-radius: $radius; + -moz-border-radius: $radius; + -webkit-border-radius: $radius; +} + +@mixin box-shadow($left, $top, $radius, $color) { + box-shadow: $left $top $radius $color; + -moz-box-shadow: $left $top $radius $color; + -webkit-box-shadow: $left $top $radius $color; +} diff --git a/admin/css/debug-toolbar/_settings.scss b/admin/css/debug-toolbar/_settings.scss new file mode 100644 index 000000000000..1bb1386a46a0 --- /dev/null +++ b/admin/css/debug-toolbar/_settings.scss @@ -0,0 +1,8 @@ +// FONT +// ========================================================================== */ + +// Standard "sans-serif" font stack used by GitHub +$base-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + +// Default size, all other styles are based on this size +$base-size: 16px; diff --git a/admin/css/debug-toolbar/_theme-dark.scss b/admin/css/debug-toolbar/_theme-dark.scss new file mode 100644 index 000000000000..83176023a87e --- /dev/null +++ b/admin/css/debug-toolbar/_theme-dark.scss @@ -0,0 +1,167 @@ +// IMPORTS +// ========================================================================== */ + +// The "box-shadow" mixin uses colors +@use '_mixins'; + +// Graphic charter +@use '_graphic-charter'; + + +// DEBUG ICON +// ========================================================================== */ + +#debug-icon { + background-color: graphic-charter.$t-dark; + @include mixins.box-shadow(0, 0, 4px, graphic-charter.$m-gray); + + a:active, + a:link, + a:visited { + color: graphic-charter.$g-orange; + } +} + + +// DEBUG BAR +// ========================================================================== */ + +#debug-bar { + background-color: graphic-charter.$t-dark; + color: graphic-charter.$m-gray; + + // Reset to prevent conflict with other CSS files + h1, + h2, + h3, + p, + a, + button, + table, + thead, + tr, + td, + button, + .toolbar { + background-color: transparent; + color: graphic-charter.$m-gray; + } + + // Buttons + button { + background-color: graphic-charter.$t-dark; + } + + // Tables + table { + strong { + color: graphic-charter.$g-orange; + } + + tbody tr { + &:hover { + background-color: graphic-charter.$g-gray; + } + + &.current { + background-color: graphic-charter.$m-orange; + + td { + color: graphic-charter.$t-dark; + } + + &:hover td { + background-color: graphic-charter.$g-red; + color: graphic-charter.$t-light; + } + } + } + } + + // The toolbar + .toolbar { + background-color: graphic-charter.$g-gray; + @include mixins.box-shadow(0, 0, 4px, graphic-charter.$g-gray); + + img { + filter: brightness(0) invert(1); + } + } + + // Fixed top + &.fixed-top { + .toolbar { + @include mixins.box-shadow(0, 0, 4px, graphic-charter.$g-gray); + } + + .tab { + @include mixins.box-shadow(0, 1px, 4px, graphic-charter.$g-gray); + } + } + + // "Muted" elements + .muted { + color: graphic-charter.$m-gray; + + td { + color: graphic-charter.$g-gray; + } + + &:hover td { + color: graphic-charter.$m-gray; + } + } + + // The toolbar preferences + #toolbar-position, + #toolbar-theme { + filter: brightness(0) invert(0.6); + } + + // The toolbar menus + .ci-label { + &.active { + background-color: graphic-charter.$t-dark; + } + + &:hover { + background-color: graphic-charter.$t-dark; + } + + .badge { + background-color: graphic-charter.$g-red; + color: graphic-charter.$t-light; + } + } + + // The tabs container + .tab { + background-color: graphic-charter.$t-dark; + @include mixins.box-shadow(0, -1px, 4px, graphic-charter.$g-gray); + } + + // The "Timeline" tab + .timeline { + th, + td { + border-color: graphic-charter.$g-gray; + } + + .timer { + background-color: graphic-charter.$g-orange; + } + } +} + + +// DEBUG VIEW +// ========================================================================== */ + +.debug-view.show-view { + border-color: graphic-charter.$g-orange; +} + +.debug-view-path { + background-color: graphic-charter.$m-orange; + color: graphic-charter.$g-gray; +} diff --git a/admin/css/debug-toolbar/_theme-light.scss b/admin/css/debug-toolbar/_theme-light.scss new file mode 100644 index 000000000000..9aa0a5a2c9b4 --- /dev/null +++ b/admin/css/debug-toolbar/_theme-light.scss @@ -0,0 +1,163 @@ +// IMPORTS +// ========================================================================== */ + +// The "box-shadow" mixin uses colors +@use '_mixins'; + +// Graphic charter +@use '_graphic-charter'; + + +// DEBUG ICON +// ========================================================================== */ + +#debug-icon { + background-color: graphic-charter.$t-light; + @include mixins.box-shadow(0, 0, 4px, graphic-charter.$m-gray); + + a:active, + a:link, + a:visited { + color: graphic-charter.$g-orange; + } +} + + +// DEBUG BAR +// ========================================================================== */ + +#debug-bar { + background-color: graphic-charter.$t-light; + color: graphic-charter.$g-gray; + + // Reset to prevent conflict with other CSS files + h1, + h2, + h3, + p, + a, + button, + table, + thead, + tr, + td, + button, + .toolbar { + background-color: transparent; + color: graphic-charter.$g-gray; + } + + // Buttons + button { + background-color: graphic-charter.$t-light; + } + + // Tables + table { + strong { + color: graphic-charter.$g-orange; + } + + tbody tr { + &:hover { + background-color: graphic-charter.$m-gray; + } + + &.current { + background-color: graphic-charter.$m-orange; + + &:hover td { + background-color: graphic-charter.$g-red; + color: graphic-charter.$t-light; + } + } + } + } + + // The toolbar + .toolbar { + background-color: graphic-charter.$t-light; + @include mixins.box-shadow(0, 0, 4px, graphic-charter.$m-gray); + + img { + filter: brightness(0) invert(0.4); + } + } + + // Fixed top + &.fixed-top { + .toolbar { + @include mixins.box-shadow(0, 0, 4px, graphic-charter.$m-gray); + } + + .tab { + @include mixins.box-shadow(0, 1px, 4px, graphic-charter.$m-gray); + } + } + + // "Muted" elements + .muted { + color: graphic-charter.$g-gray; + + td { + color: graphic-charter.$m-gray; + } + + &:hover td { + color: graphic-charter.$g-gray; + } + } + + // The toolbar preferences + #toolbar-position, + #toolbar-theme { + filter: brightness(0) invert(0.6); + } + + // The toolbar menus + .ci-label { + &.active { + background-color: graphic-charter.$m-gray; + } + + &:hover { + background-color: graphic-charter.$m-gray; + } + + .badge { + background-color: graphic-charter.$g-red; + color: graphic-charter.$t-light; + } + } + + // The tabs container + .tab { + background-color: graphic-charter.$t-light; + @include mixins.box-shadow(0, -1px, 4px, graphic-charter.$m-gray); + } + + // The "Timeline" tab + .timeline { + th, + td { + border-color: graphic-charter.$m-gray; + } + + .timer { + background-color: graphic-charter.$g-orange; + } + } +} + + +// DEBUG VIEW +// ========================================================================== */ + +.debug-view.show-view { + border-color: graphic-charter.$g-orange; +} + +.debug-view-path { + background-color: graphic-charter.$m-orange; + color: graphic-charter.$g-gray; +} diff --git a/admin/css/debug-toolbar/toolbar.scss b/admin/css/debug-toolbar/toolbar.scss new file mode 100644 index 000000000000..767c908bf176 --- /dev/null +++ b/admin/css/debug-toolbar/toolbar.scss @@ -0,0 +1,598 @@ +/** + * This file is part of the CodeIgniter 4 framework. + * + * (c) CodeIgniter Foundation + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// IMPORTS +// ========================================================================== */ + +@use "sass:meta"; +@use '_mixins'; +@use '_settings'; + +// DEBUG ICON +// ========================================================================== */ + +#debug-icon { + // Position + bottom: 0; + position: fixed; + right: 0; + z-index: 10000; + + // Size + height: 36px; + width: 36px; + + // Spacing + margin: 0; + padding: 0; + + // Content + clear: both; + text-align: center; + + cursor: pointer; + + a svg { + margin: 8px; + max-width: 20px; + max-height: 20px; + } + + &.fixed-top { + bottom: auto; + top: 0; + } + + .debug-bar-ndisplay { + display: none; + } +} + + +// DEBUG BAR +// ========================================================================== */ + +.debug-bar-vars { + cursor: pointer; +} + +#debug-bar { + // Position + bottom: 0; + left: 0; + position: fixed; + right: 0; + z-index: 10000; + + // Size + height: 36px; + + // Spacing + line-height: 36px; + + // Typography + font-family: settings.$base-font; + font-size: settings.$base-size; + font-weight: 400; + + // General elements + h1 { + display: flex; + font-weight: normal; + margin: 0 0 0 auto; + padding: 0; + font-family: settings.$base-font; + + svg { + width: 16px; + margin-right: 5px; + } + } + + h2 { + font-weight: bold; + font-size: settings.$base-size; + margin: 0; + padding: 5px 0 10px 0; + + span { + font-size: 13px; + } + } + + h3 { + font-size: settings.$base-size - 4; + font-weight: 200; + margin: 0 0 0 10px; + padding: 0; + text-transform: uppercase; + } + + p { + font-size: settings.$base-size - 4; + margin: 0 0 0 15px; + padding: 0; + } + + a { + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + button { + border: 1px solid; + @include mixins.border-radius(4px); + cursor: pointer; + line-height: 15px; + + &:hover { + text-decoration: underline; + } + } + + table { + border-collapse: collapse; + font-size: settings.$base-size - 2; + line-height: normal; + + // Tables indentation + margin: 5px 10px 15px 10px; + + // Make sure it still fits the container, even with the margins + width: calc(100% - 10px); + + strong { + font-weight: 500; + } + + th { + display: table-cell; + font-weight: 600; + padding-bottom: 0.7em; + text-align: left; + } + + tr { + border: none; + } + + td { + border: none; + display: table-cell; + margin: 0; + text-align: left; + + &:first-child { + max-width: 20%; + + &.narrow { + width: 7em; + } + } + } + } + + td[data-debugbar-route] { + form { + display: none; + } + + &:hover { + form { + display: block; + } + + &>div { + display: none; + } + } + + input[type=text] { + padding: 2px; + } + } + + // The toolbar + .toolbar { + display: flex; + overflow: hidden; + overflow-y: auto; + padding: 0 12px 0 12px; + + // Give room for OS X scrollbar + white-space: nowrap; + z-index: 10000; + // Endless rotate + .rotate { + animation: toolbar-rotate 9s linear infinite; + } + @keyframes toolbar-rotate { + to { + transform: rotate(360deg); + } + } + } + + // Fixed top + &.fixed-top { + bottom: auto; + top: 0; + + .tab { + bottom: auto; + top: 36px; + } + } + + // The toolbar preferences + #toolbar-position, + #toolbar-theme { + padding: 0 6px; + display: inline-flex; + vertical-align: top; + cursor: pointer; + + &:hover { + text-decoration: none; + } + } + + // The "Open/Close" toggle + #debug-bar-link { + display: flex; + padding: 6px; + cursor: pointer; + } + + // The toolbar menus + .ci-label { + display: inline-flex; + font-size: settings.$base-size - 2; + + &:hover { + cursor: pointer; + } + + a { + color: inherit; + display: flex; + letter-spacing: normal; + padding: 0 10px; + text-decoration: none; + align-items: center; + } + + // The toolbar icons + img { + margin: 6px 3px 6px 0; + width: 16px !important; + } + + // The toolbar notification badges + .badge { + @include mixins.border-radius(12px); + display: inline-block; + font-size: 75%; + font-weight: bold; + line-height: 12px; + margin-left: 5px; + padding: 2px 5px; + text-align: center; + vertical-align: baseline; + white-space: nowrap; + } + } + + // The tabs container + .tab { + height: fit-content; + text-align: left; + bottom: 35px; + display: none; + left: 0; + max-height: 62%; + overflow: hidden; + overflow-y: auto; + padding: 1em 2em; + position: fixed; + right: 0; + z-index: 9999; + } + + // The "Timeline" tab + .timeline { + position: static; + display: table; + margin-left: 0; + width: 100%; + + th { + border-left: 1px solid; + font-size: settings.$base-size - 4; + font-weight: 200; + padding: 5px 5px 10px 5px; + position: relative; + text-align: left; + + &:first-child { + border-left: 0; + } + } + + td { + border-left: 1px solid; + padding: 5px; + position: relative; + + &:first-child { + border-left: 0; + max-width: none; + } + + &.child-container { + padding: 0px; + + .timeline { + margin: 0px; + + td { + &:first-child { + &:not(.child-container) { + padding-left: calc(5px + 10px * var(--level)); + } + } + } + } + } + } + + .timer { + @include mixins.border-radius(4px); + display: inline-block; + padding: 5px; + position: absolute; + top: 30%; + } + + .timeline-parent { + cursor: pointer; + + td { + &:first-child { + nav { + background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAxNTAiPjxwYXRoIGQ9Ik02IDdoMThsLTkgMTV6bTAgMzBoMThsLTkgMTV6bTAgNDVoMThsLTktMTV6bTAgMzBoMThsLTktMTV6bTAgMTJsMTggMThtLTE4IDBsMTgtMTgiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNNiAxMjZsMTggMThtLTE4IDBsMTgtMTgiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjNTU1Ii8+PC9zdmc+") no-repeat scroll 0 0/15px 75px transparent; + background-position: 0 25%; + display: inline-block; + height: 15px; + width: 15px; + margin-right: 3px; + vertical-align: middle; + } + } + } + } + + .timeline-parent-open { + background-color: #DFDFDF; + + td { + &:first-child { + nav { + background-position: 0 75%; + } + } + } + } + + .child-row { + &:hover { + background: transparent; + } + } + } + + // The "Routes" tab + .route-params, + .route-params-item { + vertical-align: top; + + td:first-child { + font-style: italic; + padding-left: 1em; + text-align: right; + } + } + + // show tab + &>.debug-bar-dblock { + display: block; + } +} + + +// DEBUG VIEW +// ========================================================================== */ + +.debug-view.show-view { + border: 1px solid; + margin: 4px; +} + +.debug-view-path { + font-family: monospace; + font-size: settings.$base-size - 4; + letter-spacing: normal; + min-height: 16px; + padding: 2px; + text-align: left; +} + +.show-view .debug-view-path { + display: block !important; +} + + +// RESPONSIVE DESIGN +// ========================================================================== */ + +@media screen and (max-width: 1024px) { + #debug-bar { + .ci-label { + img { + margin: unset + } + } + } + + .hide-sm { + display: none !important; + } +} + +@media screen and (max-width: 768px) { + #debug-bar { + table { + display: block; + overflow-x: auto; + font-size: 12px; + margin: 5px 5px 10px 5px; + + td, + th { + padding: 4px 6px; + } + } + + .timeline { + display: block; + white-space: nowrap; + font-size: 12px; + } + + .toolbar { + overflow-x: auto; + } + } + } + +// THEMES +// ========================================================================== */ + +// Default theme is "Light" +@include meta.load-css('_theme-light'); + +// If the browser supports "prefers-color-scheme" and the scheme is "Dark" +@media (prefers-color-scheme: dark) { + @include meta.load-css('_theme-dark'); +} + +// If we force the "Dark" theme +#toolbarContainer.dark { + @include meta.load-css('_theme-dark'); + + td[data-debugbar-route] input[type=text] { + background: #000; + color: #fff; + } +} + +// If we force the "Light" theme +#toolbarContainer.light { + @include meta.load-css('_theme-light'); +} + +// LAYOUT HELPERS +// ========================================================================== */ + +.debug-bar-width30 { + width: 30%; +} + +.debug-bar-width10 { + width: 10%; +} + +.debug-bar-width70p { + width: 70px; +} + +.debug-bar-width190p { + width: 190px; +} + +.debug-bar-width20e { + width: 20em; +} + +.debug-bar-width6r { + width: 6rem; +} + +.debug-bar-ndisplay { + display: none; +} + +.debug-bar-alignRight { + text-align: right; +} + +.debug-bar-alignLeft { + text-align: left; +} + +.debug-bar-noverflow { + overflow: hidden; +} + +.debug-bar-dtableRow { + display: table-row; +} + +.debug-bar-dinlineBlock { + display: inline-block; +} + +.debug-bar-pointer { + cursor: pointer; +} + +.debug-bar-mleft4 { + margin-left: 4px; +} + +.debug-bar-level-0 { + --level: 0; +} + +.debug-bar-level-1 { + --level: 1; +} + +.debug-bar-level-2 { + --level: 2; +} + +.debug-bar-level-3 { + --level: 3; +} + +.debug-bar-level-4 { + --level: 4; +} + +.debug-bar-level-5 { + --level: 5; +} + +.debug-bar-level-6 { + --level: 6; +} diff --git a/admin/framework/.gitattributes b/admin/framework/.gitattributes new file mode 100644 index 000000000000..99484cbc3c97 --- /dev/null +++ b/admin/framework/.gitattributes @@ -0,0 +1,3 @@ +/.github export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore diff --git a/admin/framework/.github/workflows/close-pull-request.yml b/admin/framework/.github/workflows/close-pull-request.yml new file mode 100644 index 000000000000..ed698e3e33c1 --- /dev/null +++ b/admin/framework/.github/workflows/close-pull-request.yml @@ -0,0 +1,24 @@ +name: Close Pull Request + +on: + pull_request_target: + types: [opened, reopened] + +permissions: + pull-requests: write + +jobs: + main: + runs-on: ubuntu-24.04 + steps: + - name: Close PR with nice message + run: gh pr close ${{ env.ISSUE }} -c "${{ env.COMMENT }}" + working-directory: ${{ github.workspace }} + env: + COMMENT: > + Thank you for your pull request. However, you have submitted your PR on a read-only + split of codeigniter4/CodeIgniter4. This repository, unfortunately, does + not accept PRs. Please submit your PR at https://github.com/codeigniter4/CodeIgniter4 + repository.

Thank you. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE: ${{ github.event.pull_request.html_url }} diff --git a/admin/framework/.gitignore b/admin/framework/.gitignore new file mode 100644 index 000000000000..56b9f10d9160 --- /dev/null +++ b/admin/framework/.gitignore @@ -0,0 +1,126 @@ +#------------------------- +# Operating Specific Junk Files +#------------------------- + +# OS X +.DS_Store +.AppleDouble +.LSOverride + +# OS X Thumbnails +._* + +# Windows image file caches +Thumbs.db +ehthumbs.db +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Linux +*~ + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +#------------------------- +# Environment Files +#------------------------- +# These should never be under version control, +# as it poses a security risk. +.env +.vagrant +Vagrantfile + +#------------------------- +# Temporary Files +#------------------------- +/writable/cache/* +!/writable/cache/index.html + +/writable/logs/* +!/writable/logs/index.html + +/writable/session/* +!/writable/session/index.html + +/writable/uploads/* +!/writable/uploads/index.html + +/writable/debugbar/* +!/writable/debugbar/index.html + +php_errors.log + +#------------------------- +# User Guide Temp Files +#------------------------- +/user_guide_src/build/* +/user_guide_src/cilexer/build/* +/user_guide_src/cilexer/dist/* +/user_guide_src/cilexer/pycilexer.egg-info/* + +#------------------------- +# Test Files +#------------------------- +/tests/coverage* + +# Don't save phpunit under version control. +/phpunit + +#------------------------- +# Composer +#------------------------- +vendor/ + +#------------------------- +# IDE / Development Files +#------------------------- + +# Modules Testing +_modules/* + +# phpenv local config +.php-version + +# Jetbrains editors (PHPStorm, etc) +.idea/ +*.iml + +# NetBeans +/nbproject/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/nbactions.xml +/nb-configuration.xml +/.nb-gradle/ + +# Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project +.phpintel +/api/ + +# Visual Studio Code +.vscode/ + +/results/ +/phpunit.xml diff --git a/admin/framework/README.md b/admin/framework/README.md new file mode 100644 index 000000000000..dd39ef950968 --- /dev/null +++ b/admin/framework/README.md @@ -0,0 +1,61 @@ +# CodeIgniter 4 Framework + +## What is CodeIgniter? + +CodeIgniter is a PHP full-stack web framework that is light, fast, flexible and secure. +More information can be found at the [official site](https://codeigniter.com). + +This repository holds the distributable version of the framework. +It has been built from the +[development repository](https://github.com/codeigniter4/CodeIgniter4). + +More information about the plans for version 4 can be found in [CodeIgniter 4](https://forum.codeigniter.com/forumdisplay.php?fid=28) on the forums. + +You can read the [user guide](https://codeigniter.com/user_guide/) +corresponding to the latest version of the framework. + +## Important Change with index.php + +`index.php` is no longer in the root of the project! It has been moved inside the *public* folder, +for better security and separation of components. + +This means that you should configure your web server to "point" to your project's *public* folder, and +not to the project root. A better practice would be to configure a virtual host to point there. A poor practice would be to point your web server to the project root and expect to enter *public/...*, as the rest of your logic and the +framework are exposed. + +**Please** read the user guide for a better explanation of how CI4 works! + +## Repository Management + +We use GitHub issues, in our main repository, to track **BUGS** and to track approved **DEVELOPMENT** work packages. +We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss +FEATURE REQUESTS. + +This repository is a "distribution" one, built by our release preparation script. +Problems with it can be raised on our forum, or as issues in the main repository. + +## Contributing + +We welcome contributions from the community. + +Please read the [*Contributing to CodeIgniter*](https://github.com/codeigniter4/CodeIgniter4/blob/develop/CONTRIBUTING.md) section in the development repository. + +## Server Requirements + +PHP version 8.2 or higher is required, with the following extensions installed: + +- [intl](http://php.net/manual/en/intl.requirements.php) +- [mbstring](http://php.net/manual/en/mbstring.installation.php) + +> [!WARNING] +> - The end of life date for PHP 7.4 was November 28, 2022. +> - The end of life date for PHP 8.0 was November 26, 2023. +> - The end of life date for PHP 8.1 was December 31, 2025. +> - If you are still using below PHP 8.2, you should upgrade immediately. +> - The end of life date for PHP 8.2 will be December 31, 2026. + +Additionally, make sure that the following extensions are enabled in your PHP: + +- json (enabled by default - don't turn it off) +- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) if you plan to use MySQL +- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library diff --git a/admin/framework/composer.json b/admin/framework/composer.json new file mode 100644 index 000000000000..ab34d89fd260 --- /dev/null +++ b/admin/framework/composer.json @@ -0,0 +1,69 @@ +{ + "name": "codeigniter4/framework", + "description": "The CodeIgniter framework v4", + "license": "MIT", + "type": "project", + "homepage": "https://codeigniter.com", + "support": { + "forum": "https://forum.codeigniter.com/", + "source": "https://github.com/codeigniter4/CodeIgniter4", + "slack": "https://codeigniterchat.slack.com" + }, + "require": { + "php": "^8.2", + "ext-intl": "*", + "ext-mbstring": "*", + "laminas/laminas-escaper": "^2.18", + "psr/log": "^3.0" + }, + "require-dev": { + "codeigniter/coding-standard": "^1.7", + "fakerphp/faker": "^1.24", + "friendsofphp/php-cs-fixer": "^3.47.1", + "kint-php/kint": "^6.1", + "mikey179/vfsstream": "^1.6.12", + "nexusphp/cs-config": "^3.6", + "phpunit/phpunit": "^10.5.16 || ^11.2", + "predis/predis": "^3.0" + }, + "suggest": { + "ext-apcu": "If you use Cache class ApcuHandler", + "ext-curl": "If you use CURLRequest class", + "ext-dom": "If you use TestResponse", + "ext-exif": "If you run Image class tests", + "ext-fileinfo": "Improves mime type detection for files", + "ext-gd": "If you use Image class GDHandler", + "ext-imagick": "If you use Image class ImageMagickHandler", + "ext-libxml": "If you use TestResponse", + "ext-memcache": "If you use Cache class MemcachedHandler with Memcache", + "ext-memcached": "If you use Cache class MemcachedHandler with Memcached", + "ext-mysqli": "If you use MySQL", + "ext-oci8": "If you use Oracle Database", + "ext-pcntl": "If you use Signals", + "ext-pgsql": "If you use PostgreSQL", + "ext-posix": "If you use Signals", + "ext-readline": "Improves CLI::input() usability", + "ext-redis": "If you use Cache class RedisHandler", + "ext-simplexml": "If you format XML", + "ext-sodium": "If you use Encryption SodiumHandler", + "ext-sqlite3": "If you use SQLite3", + "ext-sqlsrv": "If you use SQL Server", + "ext-xdebug": "If you use CIUnitTestCase::assertHeaderEmitted()" + }, + "autoload": { + "psr-4": { + "CodeIgniter\\": "system/" + }, + "exclude-from-classmap": [ + "**/Database/Migrations/**" + ] + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true + }, + "scripts": { + "test": "phpunit" + } +} diff --git a/admin/framework/phpunit.dist.xml b/admin/framework/phpunit.dist.xml new file mode 100644 index 000000000000..98e56e2141a5 --- /dev/null +++ b/admin/framework/phpunit.dist.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + ./tests + + + + + + + + + + + + ./app + + + ./app/Views + ./app/Config/Routes.php + + + + + + + + + + + + + + + + diff --git a/admin/generate-changelog.php b/admin/generate-changelog.php new file mode 100644 index 000000000000..f6578cb1372f --- /dev/null +++ b/admin/generate-changelog.php @@ -0,0 +1,153 @@ + [--dry-run] + */ +chdir(__DIR__ . '/..'); + +$args = array_slice($argv, 1); +$options = array_values(array_filter($args, static fn (string $arg): bool => str_starts_with($arg, '--'))); +$params = array_values(array_diff($args, $options)); + +if (count($params) !== 1 || preg_match('/\A\d+\.\d+\.\d+\z/', $params[0]) !== 1) { + echo "Usage: php {$argv[0]} [--dry-run]\n"; + echo "E.g.,: php {$argv[0]} 4.7.5\n"; + + exit(1); +} + +$version = $params[0]; +$dryRun = in_array('--dry-run', $options, true); +$repo = 'codeigniter4/CodeIgniter4'; + +$changelogPath = './CHANGELOG.md'; +$changelog = file_get_contents($changelogPath); + +if (! $dryRun && str_contains($changelog, "## [v{$version}]")) { + echo "CHANGELOG.md already contains an entry for v{$version}.\n"; + + exit(1); +} + +// Fetches the auto-generated release notes. +$command = sprintf( + 'gh api repos/%s/releases/generate-notes -f tag_name=v%s -f target_commitish=develop 2>&1', + $repo, + $version, +); +exec($command, $output, $exitCode); + +if ($exitCode !== 0) { + echo "Failed to fetch the release notes from GitHub:\n"; + echo implode("\n", $output) . "\n"; + + exit(1); +} + +$response = json_decode(implode("\n", $output), true); +$body = $response['body'] ?? ''; + +// Parses the release notes into categories. See ".github/release.yml". +$sections = []; +$current = null; +$previousTag = null; + +foreach (preg_split('/\R/', $body) as $line) { + if (preg_match('/^### (.+)$/', $line, $matches) === 1) { + $current = $matches[1]; + $sections[$current] = []; + + continue; + } + + if (str_starts_with($line, '## ')) { + $current = null; + + continue; + } + + if (preg_match('~^\*\*Full Changelog\*\*: https://github\.com/[^/]+/[^/]+/compare/(.+?)\.\.\.~', $line, $matches) === 1) { + $previousTag = $matches[1]; + + continue; + } + + if ($current !== null && str_starts_with($line, '* ')) { + $sections[$current][] = $line; + } +} + +if ($previousTag === null) { + echo "Could not determine the previous tag from the release notes.\n"; + + exit(1); +} + +// PRs in the catch-all category have none of the changelog labels. They are +// listed for checking only and are not part of the generated entry. +$othersTitle = 'Others (Only for checking. Remove this category)'; +$others = $sections[$othersTitle] ?? []; +unset($sections[$othersTitle]); + +// Extracts the security fixes from the SECURITY section of the detailed +// changelog, if any. These come from security advisories, not from PRs, so +// they are not part of the generated release notes. +$rstPath = "./user_guide_src/source/changelogs/v{$version}.rst"; +$security = ''; + +if (is_file($rstPath)) { + $rst = file_get_contents($rstPath); + + if (preg_match('/^\*+\nSECURITY\n\*+\n(.*?)(?=^\*+$|\z)/msu', $rst, $matches) === 1) { + $security = trim($matches[1], "\n"); + $security = preg_replace('/^- /mu', '* ', $security); + } +} + +$date = date('Y-m-d'); +$entry = "## [v{$version}](https://github.com/{$repo}/tree/v{$version}) ({$date})\n"; +$entry .= "[Full Changelog](https://github.com/{$repo}/compare/{$previousTag}...v{$version})\n"; + +if ($security !== '') { + $entry .= "\n### Security\n\n{$security}\n"; +} + +foreach ($sections as $title => $items) { + if ($items === []) { + continue; + } + + $entry .= "\n### {$title}\n\n"; + $entry .= implode("\n", $items) . "\n"; +} + +if ($others !== []) { + echo "The following PRs have no changelog label and were NOT included.\n"; + echo "If any of them belong in the changelog, label the PR and run this script again:\n"; + echo implode("\n", $others) . "\n\n"; +} + +if ($dryRun) { + echo $entry; + + exit(0); +} + +$updated = preg_replace('/\A# Changelog\n\n/', "# Changelog\n\n{$entry}\n", $changelog, 1, $count); + +if ($count !== 1) { + echo 'Could not find the "# Changelog" header in CHANGELOG.md.' . "\n"; + + exit(1); +} + +file_put_contents($changelogPath, $updated); + +echo "Added the v{$version} entry to CHANGELOG.md.\n"; diff --git a/admin/next-changelog-minor.rst b/admin/next-changelog-minor.rst new file mode 100644 index 000000000000..c22f0be7d9f3 --- /dev/null +++ b/admin/next-changelog-minor.rst @@ -0,0 +1,84 @@ +################# +Version {version} +################# + +Release Date: Unreleased + +**{version} release of CodeIgniter4** + +.. contents:: + :local: + :depth: 3 + +********** +Highlights +********** + +- TBD + +******** +BREAKING +******** + +Behavior Changes +================ + +Interface Changes +================= + +Method Signature Changes +======================== + +************ +Enhancements +************ + +Commands +======== + +Testing +======= + +Database +======== + +Query Builder +------------- + +Forge +----- + +Others +------ + +Model +===== + +Libraries +========= + +Helpers and Functions +===================== + +Others +====== + +*************** +Message Changes +*************** + +******* +Changes +******* + +************ +Deprecations +************ + +********** +Bugs Fixed +********** + +See the repo's +`CHANGELOG.md `_ +for a complete list of bugs fixed. diff --git a/admin/next-changelog-patch.rst b/admin/next-changelog-patch.rst new file mode 100644 index 000000000000..ed2ba70570b3 --- /dev/null +++ b/admin/next-changelog-patch.rst @@ -0,0 +1,35 @@ +################# +Version {version} +################# + +Release Date: Unreleased + +**{version} release of CodeIgniter4** + +.. contents:: + :local: + :depth: 3 + +******** +BREAKING +******** + +*************** +Message Changes +*************** + +******* +Changes +******* + +************ +Deprecations +************ + +********** +Bugs Fixed +********** + +See the repo's +`CHANGELOG.md `_ +for a complete list of bugs fixed. diff --git a/admin/next-upgrading-guide.rst b/admin/next-upgrading-guide.rst new file mode 100644 index 000000000000..e38bebe3c4a7 --- /dev/null +++ b/admin/next-upgrading-guide.rst @@ -0,0 +1,55 @@ +############################## +Upgrading from {version} to {version} +############################## + +Please refer to the upgrade instructions corresponding to your installation method. + +- :ref:`Composer Installation App Starter Upgrading ` +- :ref:`Composer Installation Adding CodeIgniter4 to an Existing Project Upgrading ` +- :ref:`Manual Installation Upgrading ` + +.. contents:: + :local: + :depth: 2 + +********************** +Mandatory File Changes +********************** + +**************** +Breaking Changes +**************** + +********************* +Breaking Enhancements +********************* + +************* +Project Files +************* + +Some files in the **project space** (root, app, public, writable) received updates. Due to +these files being outside of the **system** scope they will not be changed without your intervention. + +.. note:: There are some third-party CodeIgniter modules available to assist + with merging changes to the project space: + `Explore on Packagist `_. + +Content Changes +=============== + +The following files received significant changes (including deprecations or visual adjustments) +and it is recommended that you merge the updated versions with your application: + +Config +------ + +- @TODO + +All Changes +=========== + +This is a list of all files in the **project space** that received changes; +many will be simple comments or formatting that have no effect on the runtime: + +- @TODO diff --git a/admin/pre-commit b/admin/pre-commit new file mode 100644 index 000000000000..19bec4f4726b --- /dev/null +++ b/admin/pre-commit @@ -0,0 +1,40 @@ +#!/bin/sh + +PROJECT=`php -r "echo dirname(dirname(dirname(realpath('$0'))));"` +STAGED_PHP_FILES=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php$` +STAGED_RST_FILES=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.rst$` + +echo "Starting CodeIgniter precommit..." + +if [ "$STAGED_PHP_FILES" != "" ]; then + echo "Linting PHP code..." + for FILE in $STAGED_PHP_FILES; do + php -l -d display_errors=0 "$PROJECT/$FILE" + + if [ $? != 0 ]; then + echo "Fix the error(s) before commit." + exit 1 + fi + + FILES="$FILES $FILE" + done +fi + +if [ "$FILES" != "" ]; then + echo "Running PHP CS Fixer..." + + # Run on whole codebase to skip on unnecessary filtering + composer cs + + if [ $? != 0 ]; then + echo "There are PHP files which are not following the coding standards. Please fix them before commit." + exit 1 + fi +fi + +if [ "$STAGED_RST_FILES" != "" ]; then + echo "Checking for tabs in RST files" + php ./utils/check_tabs_in_rst.php +fi + +exit $? diff --git a/admin/prepare-release.php b/admin/prepare-release.php new file mode 100644 index 000000000000..d68ae249b604 --- /dev/null +++ b/admin/prepare-release.php @@ -0,0 +1,88 @@ +" . PHP_EOL; + echo "E.g.,: php {$argv[0]} 4.4.3" . PHP_EOL; + + exit(1); +} + +// Gets version number from argument. +$version = $argv[1]; // e.g., '4.4.3' +$versionParts = explode('.', $version); +$minor = $versionParts[0] . '.' . $versionParts[1]; + +// Note: Major version will change someday (4.x..5.x) - update manually. +$nextMinor = $versionParts[0] . '.' . $versionParts[1] + 1; + +// Creates a branch for release. +system('git switch develop'); +system('git branch -D release-' . $version); +system('git switch -c release-' . $version); + +// Updates version number in "CodeIgniter.php". +replace_file_content( + './system/CodeIgniter.php', + '/public const CI_VERSION = \'.*?\';/u', + "public const CI_VERSION = '{$version}';", +); + +// Updates version number in "conf.py". +replace_file_content( + './user_guide_src/source/conf.py', + '/^version = \'.*?\'/mu', + "version = '{$minor}'", +); +replace_file_content( + './user_guide_src/source/conf.py', + '/^release = \'.*?\'/mu', + "release = '{$version}'", +); + +// Updates version number in "phpdoc.dist.xml". +replace_file_content( + './phpdoc.dist.xml', + '!CodeIgniter v.*? API!mu', + "CodeIgniter v{$minor} API", +); +replace_file_content( + './phpdoc.dist.xml', + '//mu', + "", +); + +// Updates release date in changelogs. +$date = date('F j, Y'); +replace_file_content( + "./user_guide_src/source/changelogs/v{$version}.rst", + '/^Release Date: .*/mu', + "Release Date: {$date}", +); + +// Update appstarter/builds script +replace_file_content( + './admin/starter/builds', + '/define\(\'LATEST_RELEASE\', \'.*?\'\);/mu', + "define('LATEST_RELEASE', '^{$minor}');", +); +replace_file_content( + './admin/starter/builds', + '/define\(\'NEXT_MINOR\', \'.*?\'\);/mu', + "define('NEXT_MINOR', '^{$nextMinor}-dev');", +); + +// Commits +system('git add -u'); +system('git commit -m "Prep for ' . $version . ' release"'); diff --git a/admin/setup.sh b/admin/setup.sh new file mode 100644 index 000000000000..55adf547e4bd --- /dev/null +++ b/admin/setup.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Install a pre-commit hook that +# automatically runs php-cs-fixer to lint code +mkdir -p .git/hooks +cp admin/pre-commit .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit diff --git a/admin/starter/.gitattributes b/admin/starter/.gitattributes new file mode 100644 index 000000000000..aacb20e85228 --- /dev/null +++ b/admin/starter/.gitattributes @@ -0,0 +1,2 @@ +/.gitattributes export-ignore +/.github export-ignore diff --git a/admin/starter/.github/workflows/close-pull-request.yml b/admin/starter/.github/workflows/close-pull-request.yml new file mode 100644 index 000000000000..ed698e3e33c1 --- /dev/null +++ b/admin/starter/.github/workflows/close-pull-request.yml @@ -0,0 +1,24 @@ +name: Close Pull Request + +on: + pull_request_target: + types: [opened, reopened] + +permissions: + pull-requests: write + +jobs: + main: + runs-on: ubuntu-24.04 + steps: + - name: Close PR with nice message + run: gh pr close ${{ env.ISSUE }} -c "${{ env.COMMENT }}" + working-directory: ${{ github.workspace }} + env: + COMMENT: > + Thank you for your pull request. However, you have submitted your PR on a read-only + split of codeigniter4/CodeIgniter4. This repository, unfortunately, does + not accept PRs. Please submit your PR at https://github.com/codeigniter4/CodeIgniter4 + repository.

Thank you. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE: ${{ github.event.pull_request.html_url }} diff --git a/admin/starter/.github/workflows/phpunit.yml b/admin/starter/.github/workflows/phpunit.yml new file mode 100644 index 000000000000..308e0565835f --- /dev/null +++ b/admin/starter/.github/workflows/phpunit.yml @@ -0,0 +1,55 @@ +name: PHPUnit + +on: + pull_request: + branches: + - develop + +permissions: + contents: read + +jobs: + main: + name: Build and test + + strategy: + matrix: + php-versions: ['8.2', '8.5'] + + runs-on: ubuntu-24.04 + if: (! contains(github.event.pull_request.title, '[ci skip]')) + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # 2.36.0 + with: + php-version: ${{ matrix.php-versions }} + tools: composer, pecl, phpunit + extensions: intl, json, mbstring, mysqlnd, xdebug, xml, sqlite3 + coverage: xdebug + + - name: Get composer cache directory + id: composer-cache + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache composer dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ${{ steps.composer-cache.outputs.COMPOSER_CACHE_DIR }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --no-interaction + # To prevent rate limiting you may need to supply an OAuth token in Settings > Secrets + # env: + # https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens + # COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }} + + - name: Test with phpunit + run: vendor/bin/phpunit --coverage-text diff --git a/admin/starter/.gitignore b/admin/starter/.gitignore new file mode 100644 index 000000000000..56b9f10d9160 --- /dev/null +++ b/admin/starter/.gitignore @@ -0,0 +1,126 @@ +#------------------------- +# Operating Specific Junk Files +#------------------------- + +# OS X +.DS_Store +.AppleDouble +.LSOverride + +# OS X Thumbnails +._* + +# Windows image file caches +Thumbs.db +ehthumbs.db +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Linux +*~ + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +#------------------------- +# Environment Files +#------------------------- +# These should never be under version control, +# as it poses a security risk. +.env +.vagrant +Vagrantfile + +#------------------------- +# Temporary Files +#------------------------- +/writable/cache/* +!/writable/cache/index.html + +/writable/logs/* +!/writable/logs/index.html + +/writable/session/* +!/writable/session/index.html + +/writable/uploads/* +!/writable/uploads/index.html + +/writable/debugbar/* +!/writable/debugbar/index.html + +php_errors.log + +#------------------------- +# User Guide Temp Files +#------------------------- +/user_guide_src/build/* +/user_guide_src/cilexer/build/* +/user_guide_src/cilexer/dist/* +/user_guide_src/cilexer/pycilexer.egg-info/* + +#------------------------- +# Test Files +#------------------------- +/tests/coverage* + +# Don't save phpunit under version control. +/phpunit + +#------------------------- +# Composer +#------------------------- +vendor/ + +#------------------------- +# IDE / Development Files +#------------------------- + +# Modules Testing +_modules/* + +# phpenv local config +.php-version + +# Jetbrains editors (PHPStorm, etc) +.idea/ +*.iml + +# NetBeans +/nbproject/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/nbactions.xml +/nb-configuration.xml +/.nb-gradle/ + +# Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project +.phpintel +/api/ + +# Visual Studio Code +.vscode/ + +/results/ +/phpunit.xml diff --git a/admin/starter/README.md b/admin/starter/README.md new file mode 100644 index 000000000000..45f98af6033e --- /dev/null +++ b/admin/starter/README.md @@ -0,0 +1,69 @@ +# CodeIgniter 4 Application Starter + +## What is CodeIgniter? + +CodeIgniter is a PHP full-stack web framework that is light, fast, flexible and secure. +More information can be found at the [official site](https://codeigniter.com). + +This repository holds a composer-installable app starter. +It has been built from the +[development repository](https://github.com/codeigniter4/CodeIgniter4). + +More information about the plans for version 4 can be found in [CodeIgniter 4](https://forum.codeigniter.com/forumdisplay.php?fid=28) on the forums. + +You can read the [user guide](https://codeigniter.com/user_guide/) +corresponding to the latest version of the framework. + +## Installation & updates + +`composer create-project codeigniter4/appstarter` then `composer update` whenever +there is a new release of the framework. + +When updating, check the release notes to see if there are any changes you might need to apply +to your `app` folder. The affected files can be copied or merged from +`vendor/codeigniter4/framework/app`. + +## Setup + +Copy `env` to `.env` and tailor for your app, specifically the baseURL +and any database settings. + +## Important Change with index.php + +`index.php` is no longer in the root of the project! It has been moved inside the *public* folder, +for better security and separation of components. + +This means that you should configure your web server to "point" to your project's *public* folder, and +not to the project root. A better practice would be to configure a virtual host to point there. A poor practice would be to point your web server to the project root and expect to enter *public/...*, as the rest of your logic and the +framework are exposed. + +**Please** read the user guide for a better explanation of how CI4 works! + +## Repository Management + +We use GitHub issues, in our main repository, to track **BUGS** and to track approved **DEVELOPMENT** work packages. +We use our [forum](http://forum.codeigniter.com) to provide SUPPORT and to discuss +FEATURE REQUESTS. + +This repository is a "distribution" one, built by our release preparation script. +Problems with it can be raised on our forum, or as issues in the main repository. + +## Server Requirements + +PHP version 8.2 or higher is required, with the following extensions installed: + +- [intl](http://php.net/manual/en/intl.requirements.php) +- [mbstring](http://php.net/manual/en/mbstring.installation.php) + +> [!WARNING] +> - The end of life date for PHP 7.4 was November 28, 2022. +> - The end of life date for PHP 8.0 was November 26, 2023. +> - The end of life date for PHP 8.1 was December 31, 2025. +> - If you are still using below PHP 8.2, you should upgrade immediately. +> - The end of life date for PHP 8.2 will be December 31, 2026. + +Additionally, make sure that the following extensions are enabled in your PHP: + +- json (enabled by default - don't turn it off) +- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) if you plan to use MySQL +- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library diff --git a/admin/starter/app/Config/Paths.php b/admin/starter/app/Config/Paths.php new file mode 100644 index 000000000000..ee2d7ff3ba71 --- /dev/null +++ b/admin/starter/app/Config/Paths.php @@ -0,0 +1,90 @@ + 'vcs', + 'url' => GITHUB_URL, + ]; + } + + $array['require']['codeigniter4/codeigniter4'] = $branch === 'next' ? NEXT_MINOR : DEVELOP_BRANCH; + unset($array['require']['codeigniter4/framework']); + } else { + unset($array['minimum-stability']); + + if (isset($array['repositories'])) { + foreach ($array['repositories'] as $i => $repository) { + if ($repository['url'] === GITHUB_URL) { + unset($array['repositories'][$i]); + break; + } + } + + if ($array['repositories'] === []) { + unset($array['repositories']); + } + } + + $array['require']['codeigniter4/framework'] = LATEST_RELEASE; + unset($array['require']['codeigniter4/codeigniter4']); + } + + file_put_contents($file, json_encode($array, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL); + + $modified[] = $file; + } else { + echo 'Warning: Unable to decode composer.json! Skipping...' . PHP_EOL; + } + } else { + echo 'Warning: Unable to read composer.json! Skipping...' . PHP_EOL; + } +} + +$files = [ + __DIR__ . DIRECTORY_SEPARATOR . 'app/Config/Paths.php', + __DIR__ . DIRECTORY_SEPARATOR . 'phpunit.dist.xml', + __DIR__ . DIRECTORY_SEPARATOR . 'phpunit.xml', +]; + +foreach ($files as $file) { + if (is_file($file)) { + $contents = file_get_contents($file); + + if ($branch !== 'release') { + $contents = str_replace('vendor/codeigniter4/framework', 'vendor/codeigniter4/codeigniter4', $contents); + } else { + $contents = str_replace('vendor/codeigniter4/codeigniter4', 'vendor/codeigniter4/framework', $contents); + } + + file_put_contents($file, $contents); + + $modified[] = $file; + } +} + +if ($modified === []) { + echo 'No files modified.' . PHP_EOL; +} else { + echo 'The following files were modified:' . PHP_EOL; + + foreach ($modified as $file) { + echo " * {$file}" . PHP_EOL; + } +} + +echo 'Run `composer update` to sync changes with your vendor folder.' . PHP_EOL; +echo 'Don\'t forget to update the project files in app folder from "vendor/codeigniter4/*/app/".' . PHP_EOL; diff --git a/admin/starter/composer.json b/admin/starter/composer.json new file mode 100644 index 000000000000..d47149e0287f --- /dev/null +++ b/admin/starter/composer.json @@ -0,0 +1,43 @@ +{ + "name": "codeigniter4/appstarter", + "description": "CodeIgniter4 starter app", + "license": "MIT", + "type": "project", + "homepage": "https://codeigniter.com", + "support": { + "forum": "https://forum.codeigniter.com/", + "source": "https://github.com/codeigniter4/CodeIgniter4", + "slack": "https://codeigniterchat.slack.com" + }, + "require": { + "php": "^8.2", + "codeigniter4/framework": "^4.7" + }, + "require-dev": { + "fakerphp/faker": "^1.9", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^10.5.16" + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Config\\": "app/Config/" + }, + "exclude-from-classmap": [ + "**/Database/Migrations/**" + ] + }, + "autoload-dev": { + "psr-4": { + "Tests\\Support\\": "tests/_support" + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true + }, + "scripts": { + "test": "phpunit" + } +} diff --git a/admin/starter/phpunit.dist.xml b/admin/starter/phpunit.dist.xml new file mode 100644 index 000000000000..d9d2c6ade852 --- /dev/null +++ b/admin/starter/phpunit.dist.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + ./tests + + + + + + + + + + + + ./app + + + ./app/Views + ./app/Config/Routes.php + + + + + + + + + + + + + + + + diff --git a/admin/starter/tests/.htaccess b/admin/starter/tests/.htaccess new file mode 100644 index 000000000000..3462048add78 --- /dev/null +++ b/admin/starter/tests/.htaccess @@ -0,0 +1,6 @@ + + Require all denied + + + Deny from all + diff --git a/admin/starter/tests/README.md b/admin/starter/tests/README.md new file mode 100644 index 000000000000..d5b12eea54f7 --- /dev/null +++ b/admin/starter/tests/README.md @@ -0,0 +1,118 @@ +# Running Application Tests + +This is the quick-start to CodeIgniter testing. Its intent is to describe what +it takes to set up your application and get it ready to run unit tests. +It is not intended to be a full description of the test features that you can +use to test your application. Those details can be found in the documentation. + +## Resources + +* [CodeIgniter 4 User Guide on Testing](https://codeigniter.com/user_guide/testing/index.html) +* [PHPUnit docs](https://phpunit.de/documentation.html) +* [Any tutorials on Unit testing in CI4?](https://forum.codeigniter.com/showthread.php?tid=81830) + +## Requirements + +It is recommended to use the latest version of PHPUnit. At the time of this +writing, we are running version 9.x. Support for this has been built into the +**composer.json** file that ships with CodeIgniter and can easily be installed +via [Composer](https://getcomposer.org/) if you don't already have it installed globally. + +```console +> composer install +``` + +If running under macOS or Linux, you can create a symbolic link to make running tests a touch nicer. + +```console +> ln -s ./vendor/bin/phpunit ./phpunit +``` + +You also need to install [XDebug](https://xdebug.org/docs/install) in order +for code coverage to be calculated successfully. After installing `XDebug`, you must add `xdebug.mode=coverage` in the **php.ini** file to enable code coverage. + +## Setting Up + +A number of the tests use a running database. +In order to set up the database edit the details for the `tests` group in +**app/Config/Database.php** or **.env**. +Make sure that you provide a database engine that is currently running on your machine. +More details on a test database setup are in the +[Testing Your Database](https://codeigniter.com/user_guide/testing/database.html) section of the documentation. + +## Running the tests + +The entire test suite can be run by simply typing one command-line command from the main directory. + +```console +> ./phpunit +``` + +If you are using Windows, use the following command. + +```console +> vendor\bin\phpunit +``` + +You can limit tests to those within a single test directory by specifying the +directory name after phpunit. + +```console +> ./phpunit app/Models +``` + +## Generating Code Coverage + +To generate coverage information, including HTML reports you can view in your browser, +you can use the following command: + +```console +> ./phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m +``` + +This runs all of the tests again collecting information about how many lines, +functions, and files are tested. It also reports the percentage of the code that is covered by tests. +It is collected in two formats: a simple text file that provides an overview as well +as a comprehensive collection of HTML files that show the status of every line of code in the project. + +The text file can be found at **tests/coverage.txt**. +The HTML files can be viewed by opening **tests/coverage/index.html** in your favorite browser. + +## PHPUnit XML Configuration + +The repository has a ``phpunit.dist.xml`` file in the project root that's used for +PHPUnit configuration. This is used to provide a default configuration if you +do not have your own configuration file in the project root. + +The normal practice would be to copy ``phpunit.dist.xml`` to ``phpunit.xml`` +(which is git ignored), and to tailor it as you see fit. +For instance, you might wish to exclude database tests, or automatically generate +HTML code coverage reports. + +## Test Cases + +Every test needs a *test case*, or class that your tests extend. CodeIgniter 4 +provides one class that you may use directly: +* `CodeIgniter\Test\CIUnitTestCase` + +Most of the time you will want to write your own test cases that extend `CIUnitTestCase` +to hold functions and services common to your test suites. + +## Creating Tests + +All tests go in the **tests/** directory. Each test file is a class that extends a +**Test Case** (see above) and contains methods for the individual tests. These method +names must start with the word "test" and should have descriptive names for precisely what +they are testing: +`testUserCanModifyFile()` `testOutputColorMatchesInput()` `testIsLoggedInFailsWithInvalidUser()` + +Writing tests is an art, and there are many resources available to help learn how. +Review the links above and always pay attention to your code coverage. + +### Database Tests + +Tests can include migrating, seeding, and testing against a mock or live database. +Be sure to modify the test case (or create your own) to point to your seed and migrations +and include any additional steps to be run before tests in the `setUp()` method. +See [Testing Your Database](https://codeigniter.com/user_guide/testing/database.html) +for details. diff --git a/admin/starter/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php b/admin/starter/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php new file mode 100644 index 000000000000..a73356d3479a --- /dev/null +++ b/admin/starter/tests/_support/Database/Migrations/2020-02-22-222222_example_migration.php @@ -0,0 +1,37 @@ +forge->addField('id'); + $this->forge->addField([ + 'name' => ['type' => 'varchar', 'constraint' => 31], + 'uid' => ['type' => 'varchar', 'constraint' => 31], + 'class' => ['type' => 'varchar', 'constraint' => 63], + 'icon' => ['type' => 'varchar', 'constraint' => 31], + 'summary' => ['type' => 'varchar', 'constraint' => 255], + 'created_at' => ['type' => 'datetime', 'null' => true], + 'updated_at' => ['type' => 'datetime', 'null' => true], + 'deleted_at' => ['type' => 'datetime', 'null' => true], + ]); + + $this->forge->addKey('name'); + $this->forge->addKey('uid'); + $this->forge->addKey(['deleted_at', 'id']); + $this->forge->addKey('created_at'); + + $this->forge->createTable('factories'); + } + + public function down(): void + { + $this->forge->dropTable('factories'); + } +} diff --git a/admin/starter/tests/_support/Database/Seeds/ExampleSeeder.php b/admin/starter/tests/_support/Database/Seeds/ExampleSeeder.php new file mode 100644 index 000000000000..619fc27a7292 --- /dev/null +++ b/admin/starter/tests/_support/Database/Seeds/ExampleSeeder.php @@ -0,0 +1,41 @@ + 'Test Factory', + 'uid' => 'test001', + 'class' => 'Factories\Tests\NewFactory', + 'icon' => 'fas fa-puzzle-piece', + 'summary' => 'Longer sample text for testing', + ], + [ + 'name' => 'Widget Factory', + 'uid' => 'widget', + 'class' => 'Factories\Tests\WidgetPlant', + 'icon' => 'fas fa-puzzle-piece', + 'summary' => 'Create widgets in your factory', + ], + [ + 'name' => 'Evil Factory', + 'uid' => 'evil-maker', + 'class' => 'Factories\Evil\MyFactory', + 'icon' => 'fas fa-book-dead', + 'summary' => 'Abandon all hope, ye who enter here', + ], + ]; + + $builder = $this->db->table('factories'); + + foreach ($factories as $factory) { + $builder->insert($factory); + } + } +} diff --git a/admin/starter/tests/_support/Libraries/ConfigReader.php b/admin/starter/tests/_support/Libraries/ConfigReader.php new file mode 100644 index 000000000000..0bb4a8fcf598 --- /dev/null +++ b/admin/starter/tests/_support/Libraries/ConfigReader.php @@ -0,0 +1,19 @@ +findAll(); + + // Make sure the count is as expected + $this->assertCount(3, $objects); + } + + public function testSoftDeleteLeavesRow(): void + { + $model = new ExampleModel(); + $this->setPrivateProperty($model, 'useSoftDeletes', true); + $this->setPrivateProperty($model, 'tempUseSoftDeletes', true); + + /** @var stdClass $object */ + $object = $model->first(); + $model->delete($object->id); + + // The model should no longer find it + $this->assertNull($model->find($object->id)); + + // ... but it should still be in the database + $result = $model->builder()->where('id', $object->id)->get()->getResult(); + + $this->assertCount(1, $result); + } +} diff --git a/application/index.html b/admin/starter/tests/index.html similarity index 100% rename from application/index.html rename to admin/starter/tests/index.html diff --git a/admin/starter/tests/session/ExampleSessionTest.php b/admin/starter/tests/session/ExampleSessionTest.php new file mode 100644 index 000000000000..33242a477112 --- /dev/null +++ b/admin/starter/tests/session/ExampleSessionTest.php @@ -0,0 +1,17 @@ +set('logged_in', 123); + $this->assertSame(123, $session->get('logged_in')); + } +} diff --git a/admin/starter/tests/unit/HealthTest.php b/admin/starter/tests/unit/HealthTest.php new file mode 100644 index 000000000000..1df24df823a7 --- /dev/null +++ b/admin/starter/tests/unit/HealthTest.php @@ -0,0 +1,49 @@ +assertTrue(defined('APPPATH')); + } + + public function testBaseUrlHasBeenSet(): void + { + $validation = service('validation'); + + $env = false; + + // Check the baseURL in .env + if (is_file(HOMEPATH . '.env')) { + $env = preg_grep('/^app\.baseURL = ./', file(HOMEPATH . '.env')) !== false; + } + + if ($env) { + // BaseURL in .env is a valid URL? + // phpunit.dist.xml sets app.baseURL in $_SERVER + // So if you set app.baseURL in .env, it takes precedence + $config = new App(); + $this->assertTrue( + $validation->check($config->baseURL, 'valid_url'), + 'baseURL "' . $config->baseURL . '" in .env is not valid URL', + ); + } + + // Get the baseURL in app/Config/App.php + // You can't use Config\App, because phpunit.dist.xml sets app.baseURL + $reader = new ConfigReader(); + + // BaseURL in app/Config/App.php is a valid URL? + $this->assertTrue( + $validation->check($reader->baseURL, 'valid_url'), + 'baseURL "' . $reader->baseURL . '" in app/Config/App.php is not valid URL', + ); + } +} diff --git a/admin/sync-release-branches.php b/admin/sync-release-branches.php new file mode 100644 index 000000000000..4f82a424bc65 --- /dev/null +++ b/admin/sync-release-branches.php @@ -0,0 +1,96 @@ + [--push] + */ +function run_command(string $command): void +{ + echo sprintf("$ %s\n", $command); + system($command, $exitCode); + + if ($exitCode !== 0) { + exit($exitCode); + } +} + +function merge_branch(string $ref): void +{ + echo sprintf("$ git merge %s\n", $ref); + system(sprintf('git merge %s', escapeshellarg($ref)), $exitCode); + + if ($exitCode !== 0) { + echo sprintf("\nMerging %s failed. Resolve the conflicts and run \"git merge --continue\",\n", $ref); + echo "then push with \"git push upstream HEAD\".\n"; + + exit(1); + } +} + +chdir(__DIR__ . '/..'); + +$args = array_slice($argv, 1); +$options = array_values(array_filter($args, static fn (string $arg): bool => str_starts_with($arg, '--'))); +$params = array_values(array_diff($args, $options)); + +$isBranch = static fn (string $name): bool => preg_match('/\A(?:develop|master|\d+\.\d+)\z/', $name) === 1; + +if ( + count($params) !== 2 + || ! $isBranch($params[0]) + || ! $isBranch($params[1]) + || $params[0] === $params[1] + || array_diff($options, ['--push']) !== [] +) { + echo sprintf("Usage: php %s [--push]\n", $argv[0]); + echo sprintf("E.g.,: php %s 4.8 develop --push # merges develop into 4.8\n", $argv[0]); + echo sprintf(" php %s develop master --push # merges master into develop\n", $argv[0]); + echo "Checks out , merges upstream/ and then upstream/ into it.\n"; + echo "Without --push, the result is left unpushed for review.\n"; + + exit(1); +} + +[$target, $source] = $params; +$push = in_array('--push', $options, true); + +exec('git remote get-url upstream 2>&1', $remoteOutput, $exitCode); + +if ($exitCode !== 0) { + echo "The \"upstream\" remote is not configured.\n"; + + exit(1); +} + +exec('git status --porcelain 2>&1', $statusOutput, $exitCode); + +if ($exitCode !== 0 || $statusOutput !== []) { + echo "The working tree is not clean. Commit or stash your changes first.\n"; + + exit(1); +} + +run_command('git fetch upstream'); + +exec(sprintf('git rev-parse --verify --quiet refs/heads/%s 2>&1', escapeshellarg($target)), $verifyOutput, $exitCode); + +if ($exitCode === 0) { + run_command(sprintf('git switch %s', escapeshellarg($target))); +} else { + run_command(sprintf('git switch -c %s upstream/%s', escapeshellarg($target), escapeshellarg($target))); +} + +merge_branch("upstream/{$target}"); +merge_branch("upstream/{$source}"); + +if ($push) { + run_command('git push upstream HEAD'); + + echo sprintf("Merged upstream/%s into %s and pushed to upstream.\n", $source, $target); +} else { + echo sprintf("Merged upstream/%s into %s. Review the result, then run \"git push upstream HEAD\".\n", $source, $target); +} diff --git a/admin/update-upgrade-guide.php b/admin/update-upgrade-guide.php new file mode 100644 index 000000000000..c80f0a75b635 --- /dev/null +++ b/admin/update-upgrade-guide.php @@ -0,0 +1,197 @@ + [--dry-run] + */ +function previous_release_tag(string $version): ?string +{ + // Not `git describe`: the base must be the highest stable tag below the + // given version regardless of the checked out branch, and prerelease + // tags (e.g. v4.0.0-rc.4) must be excluded. + exec("git tag -l 'v*' 2>&1", $tags, $exitCode); + + if ($exitCode !== 0) { + return null; + } + + $tags = array_filter( + $tags, + static fn (string $tag): bool => preg_match('/\Av\d+\.\d+\.\d+\z/', $tag) === 1 + && version_compare($tag, "v{$version}", '<'), + ); + + if ($tags === []) { + return null; + } + + usort($tags, version_compare(...)); + + return end($tags); +} + +/** + * @param list $items + */ +function bullets(array $items): string +{ + return implode("\n", array_map(static fn (string $item): string => "- {$item}", $items)); +} + +chdir(__DIR__ . '/..'); + +$args = array_slice($argv, 1); +$options = array_values(array_filter($args, static fn (string $arg): bool => str_starts_with($arg, '--'))); +$params = array_values(array_diff($args, $options)); + +if (count($params) !== 1 || preg_match('/\A\d+\.\d+\.\d+\z/', $params[0]) !== 1) { + echo sprintf("Usage: php %s [--dry-run]\n", $argv[0]); + echo sprintf("E.g.,: php %s 4.7.5\n", $argv[0]); + + exit(1); +} + +$version = $params[0]; +$dryRun = in_array('--dry-run', $options, true); + +$upgradePath = sprintf('./user_guide_src/source/installation/upgrade_%s.rst', str_replace('.', '', $version)); + +if (! is_file($upgradePath)) { + echo sprintf("%s is not found. Run \"php admin/create-new-changelog.php\" first.\n", $upgradePath); + + exit(1); +} + +$baseTag = previous_release_tag($version); + +if ($baseTag === null) { + echo "Could not determine the previous release tag. Are the tags fetched?\n"; + + exit(1); +} + +echo sprintf("Checking the project space files changed since %s.\n", $baseTag); + +// Paths that are not part of the project space. Keep in sync with the +// distribution repos: `tests/` is not used there, see "admin/starter/tests". +$excludes = [ + ':!.github/', ':!admin/', ':!changelogs/', ':!contributing/', + ':!system/', ':!tests/', ':!user_guide_src/', ':!utils/', + ':!*.json', ':!*.xml', ':!*.dist', ':!rector.php', ':!structarmed.php', + ':!phpstan*', ':!psalm*', ':!.php-cs-fixer.*', ':!LICENSE', ':!CHANGELOG.md', +]; + +$command = sprintf( + 'git diff --name-status %s -- . %s 2>&1', + escapeshellarg($baseTag), + implode(' ', array_map(escapeshellarg(...), $excludes)), +); +exec($command, $diffOutput, $exitCode); + +if ($exitCode !== 0) { + echo "Failed to diff against the latest release tag:\n"; + echo implode("\n", $diffOutput) . "\n"; + + exit(1); +} + +$allChanges = []; +$configChanged = []; +$configNew = []; + +foreach ($diffOutput as $line) { + $fields = preg_split('/\t/', $line); + + if ($fields === false || count($fields) < 2) { + continue; + } + + $status = $fields[0][0]; + $path = end($fields); + + $allChanges[] = $status === 'D' ? "{$path} (deleted)" : $path; + + if (! str_starts_with($path, 'app/Config/')) { + continue; + } + + if ($status === 'A') { + $configNew[] = $path; + } elseif ($status !== 'D') { + $configChanged[] = $path; + } +} + +sort($allChanges); +sort($configChanged); +sort($configNew); + +// Builds the "Config" section contents. +$configContent = bullets($configChanged); + +if ($configNew !== []) { + $configContent .= ($configContent === '' ? '' : "\n\n") . "These files are new in this release:\n\n" . bullets($configNew); +} + +if ($configContent === '') { + $configContent = '- No config files were changed in this release.'; +} + +// Builds the "All Changes" section contents. +$allChangesContent = $allChanges === [] + ? '- No project files were changed in this release.' + : bullets($allChanges); + +if ($dryRun) { + echo "\nConfig\n------\n\n{$configContent}\n"; + echo "\nAll Changes\n===========\n\n{$allChangesContent}\n"; + + exit(0); +} + +$rst = file_get_contents($upgradePath); + +// Fills the "Config" section, only if it still has the placeholder. Entries +// may have been added ahead of time for changes that need discussion, and +// those must not be overwritten. +if (preg_match('/^Config\n------\n\n(.*?)(?=^[^\s-][^\n]*\n(?:-+|=+)$|\z)/msu', $rst, $matches) !== 1) { + echo "Could not find the \"Config\" section in {$upgradePath}. Update it manually.\n"; +} elseif (trim($matches[1]) === '- @TODO') { + $rst = str_replace("Config\n------\n\n{$matches[1]}", "Config\n------\n\n{$configContent}\n\n", $rst); + + echo "The \"Config\" section was updated. Add notes to each entry as needed.\n"; +} else { + $mentioned = []; + + if (preg_match_all('~app/Config/[\w./-]+~u', $matches[1], $found) > 0) { + $mentioned = $found[0]; + } + + $missing = array_diff([...$configChanged, ...$configNew], $mentioned); + + if ($missing === []) { + echo "The \"Config\" section already has content and mentions all changed config files. It was not modified.\n"; + } else { + echo "The \"Config\" section already has content, so it was not modified.\n"; + echo "Merge these missing entries manually:\n" . bullets(array_values($missing)) . "\n"; + } +} + +// Fills the "All Changes" section, replacing any previous list. +$pattern = '/^(All Changes\n===========\n\n.*?:\n\n)(.*?)(?=^\*+$|\z)/msu'; +$updated = preg_replace($pattern, "\$1{$allChangesContent}\n", $rst, 1, $count); + +if ($count !== 1) { + echo "Could not find the \"All Changes\" section in {$upgradePath}. Update it manually.\n"; + + exit(1); +} + +file_put_contents($upgradePath, $updated); + +echo sprintf("The \"All Changes\" section of %s was updated.\n", $upgradePath); +echo "Remember to remove the section titles that have no items.\n"; diff --git a/admin/userguide/.github/scripts/deploy.sh b/admin/userguide/.github/scripts/deploy.sh new file mode 100755 index 000000000000..43e0675db0e6 --- /dev/null +++ b/admin/userguide/.github/scripts/deploy.sh @@ -0,0 +1,23 @@ +#!/bin/sh -e + +# Deploys the User Guide to the production +# website. Triggered by updates to the GitHub +# codeigniter4/userguide repo's master branch. +# See https://github.com/codeigniter4/userguide/blob/master/.github/workflows/deploy.yml + +REPO="/opt/userguide" +SITE="/home/public_html/userguides/userguide4" + +if [ "$(id -u)" = "0" ]; then + echo "Cannot be run as root. Please run as the user for deployment." + exit 1 +fi + +cd "$REPO" +git switch master +git pull + +cp -R "$REPO/docs" "$SITE.new" +mv "$SITE" "$SITE.old" +mv "$SITE.new" "$SITE" +rm -rf "$SITE.old" diff --git a/admin/userguide/.github/workflows/close-pull-request.yml b/admin/userguide/.github/workflows/close-pull-request.yml new file mode 100644 index 000000000000..ed698e3e33c1 --- /dev/null +++ b/admin/userguide/.github/workflows/close-pull-request.yml @@ -0,0 +1,24 @@ +name: Close Pull Request + +on: + pull_request_target: + types: [opened, reopened] + +permissions: + pull-requests: write + +jobs: + main: + runs-on: ubuntu-24.04 + steps: + - name: Close PR with nice message + run: gh pr close ${{ env.ISSUE }} -c "${{ env.COMMENT }}" + working-directory: ${{ github.workspace }} + env: + COMMENT: > + Thank you for your pull request. However, you have submitted your PR on a read-only + split of codeigniter4/CodeIgniter4. This repository, unfortunately, does + not accept PRs. Please submit your PR at https://github.com/codeigniter4/CodeIgniter4 + repository.

Thank you. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE: ${{ github.event.pull_request.html_url }} diff --git a/admin/userguide/.github/workflows/deploy.yml b/admin/userguide/.github/workflows/deploy.yml new file mode 100644 index 000000000000..8dad96764a03 --- /dev/null +++ b/admin/userguide/.github/workflows/deploy.yml @@ -0,0 +1,24 @@ +# Deploys the User Guide to the production +# website whenever master branch is updated +name: Deploy Production + +on: + push: + branches: + - master + +permissions: {} + +jobs: + build: + runs-on: ubuntu-24.04 + + steps: + - name: executing remote ssh commands using ssh key + uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.KEY }} + port: ${{ secrets.PORT }} + script: /opt/userguide/.github/scripts/deploy.sh diff --git a/admin/userguide/.gitignore b/admin/userguide/.gitignore new file mode 100644 index 000000000000..d0eba7f4bdaa --- /dev/null +++ b/admin/userguide/.gitignore @@ -0,0 +1,13 @@ +*/config/development +*/logs/log-*.php +!*/logs/index.html +*/cache/* +!*/cache/index.html +!*/cache/.htaccess + +user_guide_src/build/* +user_guide_src/cilexer/build/* +user_guide_src/cilexer/dist/* +user_guide_src/cilexer/pycilexer.egg-info/* + +/vendor/ diff --git a/admin/userguide/README.md b/admin/userguide/README.md new file mode 100644 index 000000000000..0ea07075db61 --- /dev/null +++ b/admin/userguide/README.md @@ -0,0 +1,15 @@ +# CodeIgniter 4 User Guide + +CodeIgniter 4 is a PHP web framework that is light, fast, flexible, and secure. +More information can be found at the [official site](http://codeigniter.com). + +**This is a read-only repository used to publish the user guide for the current release.** +It is built automatically as part of the framework release workflow, and pull +requests are not accepted here. + +Development is done in the [main repository](https://github.com/codeigniter4/codeigniter4). +If you find problems with the user guide, please submit a correcting pull request there. + +If you feel that features are missing or unclear, please comment on our +[forum](https://forum.codeigniter.com/index.php), +in the appropriate CodeIgniter4 subforum. diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 000000000000..3462048add78 --- /dev/null +++ b/app/.htaccess @@ -0,0 +1,6 @@ + + Require all denied + + + Deny from all + diff --git a/app/Common.php b/app/Common.php new file mode 100644 index 000000000000..95f554425236 --- /dev/null +++ b/app/Common.php @@ -0,0 +1,15 @@ + + */ + public array $allowedHostnames = []; + + /** + * -------------------------------------------------------------------------- + * Index File + * -------------------------------------------------------------------------- + * + * Typically, this will be your `index.php` file, unless you've renamed it to + * something else. If you have configured your web server to remove this file + * from your site URIs, set this variable to an empty string. + */ + public string $indexPage = 'index.php'; + + /** + * -------------------------------------------------------------------------- + * URI PROTOCOL + * -------------------------------------------------------------------------- + * + * This item determines which server global should be used to retrieve the + * URI string. The default setting of 'REQUEST_URI' works for most servers. + * If your links do not seem to work, try one of the other delicious flavors: + * + * 'REQUEST_URI': Uses $_SERVER['REQUEST_URI'] + * 'QUERY_STRING': Uses $_SERVER['QUERY_STRING'] + * 'PATH_INFO': Uses $_SERVER['PATH_INFO'] + * + * WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded! + */ + public string $uriProtocol = 'REQUEST_URI'; + + /* + |-------------------------------------------------------------------------- + | Allowed URL Characters + |-------------------------------------------------------------------------- + | + | This lets you specify which characters are permitted within your URLs. + | When someone tries to submit a URL with disallowed characters they will + | get a warning message. + | + | As a security measure you are STRONGLY encouraged to restrict URLs to + | as few characters as possible. + | + | By default, only these are allowed: `a-z 0-9~%.:_-` + | + | Set an empty string to allow all characters -- but only if you are insane. + | + | The configured value is actually a regular expression character group + | and it will be used as: '/\A[]+\z/iu' + | + | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! + | + */ + public string $permittedURIChars = 'a-z 0-9~%.:_\-'; + + /** + * -------------------------------------------------------------------------- + * Default Locale + * -------------------------------------------------------------------------- + * + * The Locale roughly represents the language and location that your visitor + * is viewing the site from. It affects the language strings and other + * strings (like currency markers, numbers, etc), that your program + * should run under for this request. + */ + public string $defaultLocale = 'en'; + + /** + * -------------------------------------------------------------------------- + * Negotiate Locale + * -------------------------------------------------------------------------- + * + * If true, the current Request object will automatically determine the + * language to use based on the value of the Accept-Language header. + * + * If false, no automatic detection will be performed. + */ + public bool $negotiateLocale = false; + + /** + * -------------------------------------------------------------------------- + * Supported Locales + * -------------------------------------------------------------------------- + * + * If $negotiateLocale is true, this array lists the locales supported + * by the application in descending order of priority. If no match is + * found, the first locale will be used. + * + * IncomingRequest::setLocale() also uses this list. + * + * @var list + */ + public array $supportedLocales = ['en']; + + /** + * -------------------------------------------------------------------------- + * Application Timezone + * -------------------------------------------------------------------------- + * + * The default timezone that will be used in your application to display + * dates with the date helper, and can be retrieved through app_timezone() + * + * @see https://www.php.net/manual/en/timezones.php for list of timezones + * supported by PHP. + */ + public string $appTimezone = 'UTC'; + + /** + * -------------------------------------------------------------------------- + * Default Character Set + * -------------------------------------------------------------------------- + * + * This determines which character set is used by default in various methods + * that require a character set to be provided. + * + * @see http://php.net/htmlspecialchars for a list of supported charsets. + */ + public string $charset = 'UTF-8'; + + /** + * -------------------------------------------------------------------------- + * Force Global Secure Requests + * -------------------------------------------------------------------------- + * + * If true, this will force every request made to this application to be + * made via a secure connection (HTTPS). If the incoming request is not + * secure, the user will be redirected to a secure version of the page + * and the HTTP Strict Transport Security (HSTS) header will be set. + */ + public bool $forceGlobalSecureRequests = false; + + /** + * -------------------------------------------------------------------------- + * Reverse Proxy IPs + * -------------------------------------------------------------------------- + * + * If your server is behind a reverse proxy, you must whitelist the proxy + * IP addresses from which CodeIgniter should trust headers such as + * X-Forwarded-For or Client-IP in order to properly identify + * the visitor's IP address. + * + * You need to set a proxy IP address or IP address with subnets and + * the HTTP header for the client IP address. + * + * Here are some examples: + * [ + * '10.0.1.200' => 'X-Forwarded-For', + * '192.168.5.0/24' => 'X-Real-IP', + * ] + * + * @var array + */ + public array $proxyIPs = []; + + /** + * -------------------------------------------------------------------------- + * Content Security Policy + * -------------------------------------------------------------------------- + * + * Enables the Response's Content Secure Policy to restrict the sources that + * can be used for images, scripts, CSS files, audio, video, etc. If enabled, + * the Response object will populate default values for the policy from the + * `ContentSecurityPolicy.php` file. Controllers can always add to those + * restrictions at run time. + * + * For a better understanding of CSP, see these documents: + * + * @see http://www.html5rocks.com/en/tutorials/security/content-security-policy/ + * @see http://www.w3.org/TR/CSP/ + */ + public bool $CSPEnabled = false; +} diff --git a/app/Config/Autoload.php b/app/Config/Autoload.php new file mode 100644 index 000000000000..9a928241a0eb --- /dev/null +++ b/app/Config/Autoload.php @@ -0,0 +1,92 @@ +|string> + */ + public $psr4 = [ + APP_NAMESPACE => APPPATH, + ]; + + /** + * ------------------------------------------------------------------- + * Class Map + * ------------------------------------------------------------------- + * The class map provides a map of class names and their exact + * location on the drive. Classes loaded in this manner will have + * slightly faster performance because they will not have to be + * searched for within one or more directories as they would if they + * were being autoloaded through a namespace. + * + * Prototype: + * $classmap = [ + * 'MyClass' => '/path/to/class/file.php' + * ]; + * + * @var array + */ + public $classmap = []; + + /** + * ------------------------------------------------------------------- + * Files + * ------------------------------------------------------------------- + * The files array provides a list of paths to __non-class__ files + * that will be autoloaded. This can be useful for bootstrap operations + * or for loading functions. + * + * Prototype: + * $files = [ + * '/path/to/my/file.php', + * ]; + * + * @var list + */ + public $files = []; + + /** + * ------------------------------------------------------------------- + * Helpers + * ------------------------------------------------------------------- + * Prototype: + * $helpers = [ + * 'form', + * ]; + * + * @var list + */ + public $helpers = []; +} diff --git a/app/Config/Boot/development.php b/app/Config/Boot/development.php new file mode 100644 index 000000000000..a868447a8e37 --- /dev/null +++ b/app/Config/Boot/development.php @@ -0,0 +1,34 @@ + + * + * @see https://www.php.net/manual/en/curl.constants.php#constant.curl-lock-data-connect + */ + public array $shareConnectionOptions = [ + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_DNS, + ]; + + /** + * -------------------------------------------------------------------------- + * CURLRequest Share Options + * -------------------------------------------------------------------------- + * + * Whether share options between requests or not. + * + * If true, all the options won't be reset between requests. + * It may cause an error request with unnecessary headers. + */ + public bool $shareOptions = false; +} diff --git a/app/Config/Cache.php b/app/Config/Cache.php new file mode 100644 index 000000000000..38ac5419d84c --- /dev/null +++ b/app/Config/Cache.php @@ -0,0 +1,198 @@ + WRITEPATH . 'cache/', + 'mode' => 0640, + ]; + + /** + * ------------------------------------------------------------------------- + * Memcached settings + * ------------------------------------------------------------------------- + * + * Your Memcached servers can be specified below, if you are using + * the Memcached drivers. + * + * @see https://codeigniter.com/user_guide/libraries/caching.html#memcached + * + * @var array{host?: string, port?: int, weight?: int, raw?: bool} + */ + public array $memcached = [ + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 1, + 'raw' => false, + ]; + + /** + * ------------------------------------------------------------------------- + * Redis settings + * ------------------------------------------------------------------------- + * + * Your Redis server can be specified below, if you are using + * the Redis or Predis drivers. + * + * @var array{ + * host?: string, + * password?: string|null, + * port?: int, + * timeout?: int, + * async?: bool, + * persistent?: bool, + * database?: int + * } + */ + public array $redis = [ + 'host' => '127.0.0.1', + 'password' => null, + 'port' => 6379, + 'timeout' => 0, + 'async' => false, // specific to Predis and ignored by the native Redis extension + 'persistent' => false, + 'database' => 0, + ]; + + /** + * -------------------------------------------------------------------------- + * Available Cache Handlers + * -------------------------------------------------------------------------- + * + * This is an array of cache engine alias' and class names. Only engines + * that are listed here are allowed to be used. + * + * @var array> + */ + public array $validHandlers = [ + 'apcu' => ApcuHandler::class, + 'dummy' => DummyHandler::class, + 'file' => FileHandler::class, + 'memcached' => MemcachedHandler::class, + 'predis' => PredisHandler::class, + 'redis' => RedisHandler::class, + 'wincache' => WincacheHandler::class, + ]; + + /** + * -------------------------------------------------------------------------- + * Web Page Caching: Cache Include Query String + * -------------------------------------------------------------------------- + * + * Whether to take the URL query string into consideration when generating + * output cache files. Valid options are: + * + * false = Disabled + * true = Enabled, take all query parameters into account. + * Please be aware that this may result in numerous cache + * files generated for the same page over and over again. + * ['q'] = Enabled, but only take into account the specified list + * of query parameters. + * + * @var bool|list + */ + public $cacheQueryString = false; + + /** + * -------------------------------------------------------------------------- + * Web Page Caching: Cache Status Codes + * -------------------------------------------------------------------------- + * + * HTTP status codes that are allowed to be cached. Only responses with + * these status codes will be cached by the PageCache filter. + * + * Default: [] - Cache all status codes (backward compatible) + * + * Recommended: [200] - Only cache successful responses + * + * You can also use status codes like: + * [200, 404, 410] - Cache successful responses and specific error codes + * [200, 201, 202, 203, 204] - All 2xx successful responses + * + * WARNING: Using [] may cache temporary error pages (404, 500, etc). + * Consider restricting to [200] for production applications to avoid + * caching errors that should be temporary. + * + * @var list + */ + public array $cacheStatusCodes = []; +} diff --git a/app/Config/Constants.php b/app/Config/Constants.php new file mode 100644 index 000000000000..fb56bb1c5b03 --- /dev/null +++ b/app/Config/Constants.php @@ -0,0 +1,79 @@ +|string|null + */ + public $defaultSrc; + + /** + * Lists allowed scripts' URLs. + * + * @var list|string + */ + public $scriptSrc = 'self'; + + /** + * Specifies valid sources for JavaScript + + + + +
+
+ Displayed at — + PHP: — + CodeIgniter: -- + Environment: +
+
+

getCode() ? ' #' . $exception->getCode() : '') ?>

+

+ getMessage())) ?> + getMessage())) ?>" + rel="noreferrer" target="_blank">search → +

+
+
+ + +
+

at line

+ + +
+ +
+ +
+ +
+ getPrevious()) { + $last = $prevException; + ?> + +
+    Caused by:
+    getCode() ? ' #' . $prevException->getCode() : '') ?>
+
+    getMessage())) ?>
+    getMessage())) ?>"
+       rel="noreferrer" target="_blank">search →
+    getFile()) . ':' . $prevException->getLine()) ?>
+    
+ + +
+ + +
+ + + +
+ + +
+ +
    + $row) : ?> + +
  1. +

    + + + + + {PHP internal code} + + + + +   —   + + + ( arguments ) +

    + + + getParameters(); + } + + foreach ($row['args'] as $key => $value) : ?> + + + + + + +
    name : "#{$key}") ?>
    +
    + + () + + + + +   —   () + +

    + + + +
    + +
    + +
  2. + + +
+ +
+ + +
+ + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + + +

Constants

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathgetUri()) ?>
HTTP MethodgetMethod()) ?>
IP AddressgetIPAddress()) ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent()->getAgentString()) ?>
+ + + + + + + + +

$

+ + + + + + + + + + $value) : ?> + + + + + + +
KeyValue
+ + + +
+ +
+ + + + + +
+ No $_GET, $_POST, or $_COOKIE Information to show. +
+ + + + headers(); ?> + + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
+ getValueLine(), 'html'); + } else { + foreach ($value as $i => $header) { + echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html'); + } + } + ?> +
+ + +
+ + + setStatusCode(http_response_code()); + ?> +
+ + + + + +
Response StatusgetStatusCode() . ' - ' . $response->getReasonPhrase()) ?>
+ + headers(); ?> + +

Headers

+ + + + + + + + + + $value) : ?> + + + + + + +
HeaderValue
+ getHeaderLine($name), 'html'); + } else { + foreach ($value as $i => $header) { + echo ' ('. $i+1 . ') ' . esc($header->getValueLine(), 'html'); + } + } + ?> +
+ + +
+ + +
+ + +
    + +
  1. + +
+
+ + +
+ + + + + + + + + + + + + + + + +
Memory Usage
Peak Memory Usage:
Memory Limit:
+ +
+ +
+ +
+ + + + diff --git a/app/Views/errors/html/production.php b/app/Views/errors/html/production.php new file mode 100644 index 000000000000..2f59a8de1bc7 --- /dev/null +++ b/app/Views/errors/html/production.php @@ -0,0 +1,25 @@ + + + + + + + <?= lang('Errors.whoops') ?> + + + + + +
+ +

+ +

+ +
+ + + + diff --git a/app/Views/welcome_message.php b/app/Views/welcome_message.php new file mode 100644 index 000000000000..c18eca3c9ed3 --- /dev/null +++ b/app/Views/welcome_message.php @@ -0,0 +1,331 @@ + + + + + Welcome to CodeIgniter 4! + + + + + + + + + + + +
+ + + +
+ +

Welcome to CodeIgniter

+ +

The small framework with powerful features

+ +
+ +
+ + + +
+ +

About this page

+ +

The page you are looking at is being generated dynamically by CodeIgniter.

+ +

If you would like to edit this page you will find it located at:

+ +
app/Views/welcome_message.php
+ +

The corresponding controller for this page can be found at:

+ +
app/Controllers/Home.php
+ +
+ +
+ +
+ +

Go further

+ +

+ + Learn +

+ +

The User Guide contains an introduction, tutorial, a number of "how to" + guides, and then reference documentation for the components that make up + the framework. Check the User Guide !

+ +

+ + Discuss +

+ +

CodeIgniter is a community-developed open source project, with several + venues for the community members to gather and exchange ideas. View all + the threads on CodeIgniter's forum, or chat on Slack !

+ +

+ + Contribute +

+ +

CodeIgniter is a community driven project and accepts contributions + of code and documentation from the community. Why not + + join us ?

+ +
+ +
+ + + +
+
+ +

Page rendered in {elapsed_time} seconds using {memory_usage} MB of memory.

+ +

Environment:

+ +
+ +
+ +

© CodeIgniter Foundation. CodeIgniter is open source project released under the MIT + open source licence.

+ +
+ +
+ + + + + + + + + diff --git a/app/index.html b/app/index.html new file mode 100644 index 000000000000..69df4e1dff68 --- /dev/null +++ b/app/index.html @@ -0,0 +1,11 @@ + + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + diff --git a/application/.htaccess b/application/.htaccess deleted file mode 100644 index 6c63ed4c4d27..000000000000 --- a/application/.htaccess +++ /dev/null @@ -1,6 +0,0 @@ - - Require all denied - - - Deny from all - \ No newline at end of file diff --git a/application/config/AppConfig.php b/application/config/AppConfig.php deleted file mode 100644 index 1f17505dfc1f..000000000000 --- a/application/config/AppConfig.php +++ /dev/null @@ -1,201 +0,0 @@ - SYSPATH - * `]; - */ - $psr4 = [ - APP_NAMESPACE => realpath(APPPATH), - APP_NAMESPACE.'\Config' => APPPATH.'config', - APP_NAMESPACE.'\Controllers' => APPPATH.'controllers', - ]; - - /** - * ------------------------------------------------------------------- - * Class Map - * ------------------------------------------------------------------- - * The class map provides a map of class names and their exact - * location on the drive. Classes loaded in this manner will have - * slightly faster performance because they will not have to be - * searched for within one or more directories as they would if they - * were being autoloaded through a namespace. - * - * Prototype: - * - * $config['classmap'] = [ - * 'MyClass' => '/path/to/class/file.php' - * ]; - */ - $classmap = []; - - //-------------------------------------------------------------------- - // Do Not Edit Below This Line - //-------------------------------------------------------------------- - - $this->psr4 = array_merge($this->psr4, $psr4); - $this->classmap = array_merge($this->classmap, $classmap); - - unset($psr4, $classmap); - } - - //-------------------------------------------------------------------- - -} diff --git a/application/config/Constants.php b/application/config/Constants.php deleted file mode 100644 index 34dba685d172..000000000000 --- a/application/config/Constants.php +++ /dev/null @@ -1,15 +0,0 @@ - "\\App\\Config\\Database\\DefaultConnection", - ]; -} diff --git a/application/config/Database/DefaultConnection.php b/application/config/Database/DefaultConnection.php deleted file mode 100644 index 2f4d5ce70c06..000000000000 --- a/application/config/Database/DefaultConnection.php +++ /dev/null @@ -1,122 +0,0 @@ - [ - - /* - * The log levels that this handler will handle. - */ - 'handles' => ['critical', 'alert', 'emergency', 'debug', - 'error', 'info', 'notice', 'warning'], - - /* - * Leave this BLANK unless you would like to set something other than the default - * writeable/logs/ directory. Use a full getServer path with trailing slash. - */ - 'path' => WRITEPATH.'logs/', - - /* - * The default filename extension for log files. The default 'php' allows for - * protecting the log files via basic scripting, when they are to be stored - * under a publicly accessible directory. - * - * Note: Leaving it blank will default to 'php'. - */ - 'fileExtension' => 'php', - - /* - * The file system permissions to be applied on newly created log files. - * - * IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal - * integer notation (i.e. 0700, 0644, etc.) - */ - 'filePermissions' => 0644 - ], - - /** - * The ChromeLoggerHandler requires the use of the Chrome web browser - * and the ChromeLogger extension. Uncomment this block to use it. - */ -// 'CodeIgniter\Log\Handlers\ChromeLoggerHandler' => [ -// /* -// * The log levels that this handler will handle. -// */ -// 'handles' => ['critical', 'alert', 'emergency', 'debug', -// 'error', 'info', 'notice', 'warning'], -// ] - ]; -} diff --git a/application/config/Routes.php b/application/config/Routes.php deleted file mode 100644 index bc21b9d4c727..000000000000 --- a/application/config/Routes.php +++ /dev/null @@ -1,86 +0,0 @@ -defaultNamespace() - * - * Modifies the namespace that is added to a controller if it doesn't - * already have one. By default this is the global namespace (\). - * - * $routes->defaultController() - * - * Changes the name of the class used as a controller when the route - * points to a folder instead of a class. - * - * $routes->defaultMethod() - * - * Assigns the method inside the controller that is ran when the - * Router is unable to determine the appropriate method to run. - * - * $routes->setAutoRoute() - * - * Determines whether the Router will attempt to match URIs to - * controllers when no specific route has been defined. If false, - * only routes that have been defined here will be available. - */ -$routes->setDefaultNamespace(''); -$routes->setDefaultController('Home'); -$routes->setDefaultMethod('index'); -$routes->setTranslateURIDashes(false); -$routes->setAutoRoute(true); - -/** - * -------------------------------------------------------------------- - * Route Definitions - * -------------------------------------------------------------------- - */ - -// We get a performance increase by specifying the default -// route since we don't have to scan directories. -$routes->add('/', 'Home::index'); - -/** - * -------------------------------------------------------------------- - * Additional Routing - * -------------------------------------------------------------------- - * - * There will often be times that you need additional routing and you - * need to it be able to override any defaults in this file. Environment - * based routes is one such time. require() additional route files here - * to make that happen. - * - * You will have access to the $routes object within that file without - * needing to reload it. - */ -if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/Routes.php')) -{ - require APPPATH.'config/'.ENVIRONMENT.'/Routes.php'; -} diff --git a/application/config/Services.php b/application/config/Services.php deleted file mode 100644 index 4002c03afe02..000000000000 --- a/application/config/Services.php +++ /dev/null @@ -1,417 +0,0 @@ -sessionDriver; - $driver = new $driverName($config); - $driver->setLogger(self::logger(true)); - - $session = new \CodeIgniter\Session\Session($driver, $config); - $session->setLogger(self::logger()); - $session->initialize(); - - return $session; - } - - return self::getSharedInstance('session'); - } - - //-------------------------------------------------------------------- - - /** - * The Timer class provides a simple way to Benchmark portions of your - * application. - */ - public static function timer($getShared = false) - { - if (! $getShared) - { - return new \CodeIgniter\Debug\Timer(); - } - - return self::getSharedInstance('timer'); - } - - //-------------------------------------------------------------------- - - public static function toolbar(AppConfig $config = null, $getShared = false) - { - if (! is_object($config)) - { - $config = new AppConfig(); - } - - if (! $getShared) - { - return new \CodeIgniter\Debug\Toolbar($config); - } - - return self::getSharedInstance('security'); - } - - //-------------------------------------------------------------------- - - /** - * The URI class provides a way to model and manipulate URIs. - */ - public static function uri($uri = null, $getShared = false) - { - if (! $getShared) - { - return new \CodeIgniter\HTTP\URI($uri); - } - - return self::getSharedInstance('uri', $uri); - } - - //-------------------------------------------------------------------- - - - //-------------------------------------------------------------------- - // Utility Methods - DO NOT EDIT - //-------------------------------------------------------------------- - - /** - * Returns a shared instance of any of the class' services. - * - * $key must be a name matching a service. - * - * @param string $key - */ - protected static function getSharedInstance(string $key, ...$params) - { - if (! isset(static::$instances[$key])) - { - static::$instances[$key] = self::$key(...$params); - } - - return static::$instances[$key]; - } - - //-------------------------------------------------------------------- - - /** - * Provides the ability to perform case-insensitive calling of service - * names. - * - * @param string $name - * @param array $arguments - */ - public static function __callStatic(string $name, array $arguments) - { - $name = strtolower($name); - - if (method_exists('App\Config\Services', $name)) - { - return Services::$name(...$arguments); - } - } - - //-------------------------------------------------------------------- - - -} diff --git a/application/controllers/Home.php b/application/controllers/Home.php deleted file mode 100644 index 4fb3c75ffd4e..000000000000 --- a/application/controllers/Home.php +++ /dev/null @@ -1,12 +0,0 @@ - - -An uncaught Exception was encountered - -Type: -Message: -Filename: getFile(), "\n"; ?> -Line Number: getLine(); ?> - - - - Backtrace: - getTrace() as $error): ?> - - File: - Line: - Function: - - - - diff --git a/application/views/errors/html/debug.css b/application/views/errors/html/debug.css deleted file mode 100644 index e526b24fd093..000000000000 --- a/application/views/errors/html/debug.css +++ /dev/null @@ -1,176 +0,0 @@ -body { - height: 100%; - background: #fafafa; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #777; - font-weight: 300; - margin: 0; - padding: 0; -} -h1 { - font-weight: lighter; - letter-spacing: 0.8; - font-size: 3rem; - color: #222; - margin: 0; -} -h1.headline { - margin-top: 20%; - font-size: 5rem; -} -.text-center { - text-align: center; -} -p.lead { - font-size: 1.6rem; -} -.container { - max-width: 75rem; - margin: 0 auto; - padding: 1rem; -} -.header { - background: #85271f; - color: #fff; -} -.header h1 { - color: #fff; -} -.header p { - font-size: 1.2rem; - margin: 0; - line-height: 2.5; -} -.header a { - color: rgba(255,255,255,0.5); - margin-left: 2rem; - display: none; - text-decoration: none; -} -.header:hover a { - display: inline; -} - -.footer .container { - border-top: 1px solid #e7e7e7; - margin-top: 1rem; - text-align: center; -} - -.source { - background: #333; - color: #c7c7c7; - padding: 0.5em 1em; - border-radius: 5px; - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - margin: 0; -} -.source span.line { - line-height: 1.4; -} -.source span.line .number { - color: #666; -} -.source .line .highlight { - display: block; - background: #555; - color: #fff; -} -.source span.highlight .number { - color: #fff; -} - -.tabs { - list-style: none; - list-style-position: inside; - margin: 0; - padding: 0; - margin-bottom: -1px; -} -.tabs li { - display: inline; -} -.tabs a:link, -.tabs a:visited { - padding: 0rem 1rem; - line-height: 2.7; - text-decoration: none; - color: #a7a7a7; - background: #f1f1f1; - border: 1px solid #e7e7e7; - border-bottom: 0; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - display: inline-block; -} -.tabs a:hover { - background: #e7e7e7; - border-color: #e1e1e1; -} -.tabs a.active { - background: #fff; -} -.tab-content { - background: #fff; - border: 1px solid #efefef; -} -.content { - padding: 1rem; -} -.hide { - display: none; -} - -.alert { - margin-top: 2rem; - display: block; - text-align: center; - line-height: 3.0; - background: #d9edf7; - border: 1px solid #bcdff1; - border-radius: 5px; - color: #31708f; -} -ul, ol { - line-height: 1.8; -} - -table { - width: 100%; - overflow: hidden; -} -th { - text-align: left; - border-bottom: 1px solid #e7e7e7; - padding-bottom: 0.5rem; -} -td { - padding: 0.2rem 0.5rem 0.2rem 0; -} -tr:hover td { - background: #f1f1f1; -} -td pre { - white-space: pre-wrap; -} - -.trace a { - color: inherit; -} -.trace table { - width: auto; -} -.trace tr td:first-child { - min-width: 5em; - font-weight: bold; -} -.trace td { - background: #e7e7e7; - padding: 0 1rem; -} -.trace td pre { - margin: 0; -} -.args { - display: none; -} \ No newline at end of file diff --git a/application/views/errors/html/debug.js b/application/views/errors/html/debug.js deleted file mode 100644 index 40a598d3c14a..000000000000 --- a/application/views/errors/html/debug.js +++ /dev/null @@ -1,127 +0,0 @@ -//-------------------------------------------------------------------- -// Tabs -//-------------------------------------------------------------------- - -var tabLinks = new Array(); -var contentDivs = new Array(); - -function init () -{ - - // Grab the tab links and content divs from the page - var tabListItems = document.getElementById('tabs').childNodes; - console.log(tabListItems); - for (var i = 0; i < tabListItems.length; i ++) - { - if (tabListItems[i].nodeName == "LI") - { - var tabLink = getFirstChildWithTagName(tabListItems[i], 'A'); - var id = getHash(tabLink.getAttribute('href')); - tabLinks[id] = tabLink; - contentDivs[id] = document.getElementById(id); - } - } - - // Assign onclick events to the tab links, and - // highlight the first tab - var i = 0; - - for (var id in tabLinks) - { - tabLinks[id].onclick = showTab; - tabLinks[id].onfocus = function () { this.blur() }; - if (i == 0) - { - tabLinks[id].className = 'active'; - } - i ++; - } - - // Hide all content divs except the first - var i = 0; - - for (var id in contentDivs) - { - if (i != 0) - { - console.log(contentDivs[id]); - contentDivs[id].className = 'content hide'; - } - i ++; - } -} - -//-------------------------------------------------------------------- - -function showTab () -{ - var selectedId = getHash(this.getAttribute('href')); - - // Highlight the selected tab, and dim all others. - // Also show the selected content div, and hide all others. - for (var id in contentDivs) - { - if (id == selectedId) - { - tabLinks[id].className = 'active'; - contentDivs[id].className = 'content'; - } - else - { - tabLinks[id].className = ''; - contentDivs[id].className = 'content hide'; - } - } - - // Stop the browser following the link - return false; -} - -//-------------------------------------------------------------------- - -function getFirstChildWithTagName (element, tagName) -{ - for (var i = 0; i < element.childNodes.length; i ++) - { - if (element.childNodes[i].nodeName == tagName) - { - return element.childNodes[i]; - } - } -} - -//-------------------------------------------------------------------- - -function getHash (url) -{ - var hashPos = url.lastIndexOf('#'); - return url.substring(hashPos + 1); -} - -//-------------------------------------------------------------------- - -function toggle (elem) -{ - elem = document.getElementById(elem); - - if (elem.style && elem.style['display']) - { - // Only works with the "style" attr - var disp = elem.style['display']; - } - else if (elem.currentStyle) - { - // For MSIE, naturally - var disp = elem.currentStyle['display']; - } - else if (window.getComputedStyle) - { - // For most other browsers - var disp = document.defaultView.getComputedStyle(elem, null).getPropertyValue('display'); - } - - // Toggle the state of the "display" style - elem.style.display = disp == 'block' ? 'none' : 'block'; - - return false; -} \ No newline at end of file diff --git a/application/views/errors/html/error_404.php b/application/views/errors/html/error_404.php deleted file mode 100644 index d4d386ecc58e..000000000000 --- a/application/views/errors/html/error_404.php +++ /dev/null @@ -1,79 +0,0 @@ - - - - - 404 Page Not Found - - - - -
-

404 - File Not Found

-

Sorry! Cannot seem to find the page you were looking for.

-
- - \ No newline at end of file diff --git a/application/views/errors/html/error_exception.php b/application/views/errors/html/error_exception.php deleted file mode 100644 index f6763fe64d6a..000000000000 --- a/application/views/errors/html/error_exception.php +++ /dev/null @@ -1,383 +0,0 @@ - - - - - - - - <?= htmlspecialchars($title, ENT_SUBSTITUTE, 'UTF-8') ?> - - - - - - - -
-
-

getCode() ? ' #'.$exception->getCode() : '') ?>

-

- getMessage(), ENT_SUBSTITUTE) ?> - getMessage())) ?>" - rel="noreferrer" target="_blank">search → -

-
-
- - -
-

at line

- - -
- -
- -
- -
- - - -
- - -
- -
    - $row) : ?> - -
  1. -

    - - - - - {PHP internal code} - - - - -   —   - - - ( arguments ) -

    - - - $value) : ?> - getParameters(); - ?> - - - - - - -
    name : "#$key", ENT_SUBSTITUTE, 'UTF-8') ?>
    -
    - - () - - - - -   —   () - -

    - - - -
    - -
    - -
  2. - - -
- -
- - -
- - - -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - - '.print_r($value, true) ?> - -
- - - - - - -

Constants

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - - '.print_r($value, true) ?> - -
- -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Pathuri ?>
HTTP MethodgetMethod(true) ?>
IP AddressgetIPAddress() ?>
Is AJAX Request?isAJAX() ? 'yes' : 'no' ?>
Is CLI Request?isCLI() ? 'yes' : 'no' ?>
Is Secure Request?isSecure() ? 'yes' : 'no' ?>
User AgentgetUserAgent() ?>
- - - - - - - - -

$

- - - - - - - - - - $value) : ?> - - - - - - -
KeyValue
- - - - '.print_r($value, true) ?> - -
- - - - - -
- No $_GET, $_POST, or $_COOKIE Information to show. -
- - - - getHeaders(); ?> - - - -

Headers

- - - - - - - - - - $value) : ?> - - - - - - -
HeaderValue
getHeaderLine($name), 'html') ?>
- - -
- - - setStatusCode(http_response_code()); - ?> -
- - - - - -
Response StatusgetStatusCode().' - '.$response->getReason() ?>
- - getHeaders(); ?> - - - -

Headers

- - - - - - - - - - $value) : ?> - - - - - - -
HeaderValue
getHeaderLine($name), 'html') ?>
- - -
- - -
- - -
    - -
  1. - -
-
- - -
- - - - - - - - - - - - - - - - -
Memory Usage
Peak Memory Usage:
Memory Limit:
- -
- -
- -
- - - - - \ No newline at end of file diff --git a/application/views/errors/html/production.php b/application/views/errors/html/production.php deleted file mode 100644 index 4628130bc32c..000000000000 --- a/application/views/errors/html/production.php +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - Whoops! - - - - - -
- -

Whoops!

- -

We seem to have hit a snag. Please try again later...

- -
- - - - \ No newline at end of file diff --git a/application/views/welcome_message.php b/application/views/welcome_message.php deleted file mode 100644 index 68c542a8802a..000000000000 --- a/application/views/welcome_message.php +++ /dev/null @@ -1,125 +0,0 @@ - - -
- -

Welcome to CodeIgniter

- -

version

- - - -
-

The page you are looking at is being generated dynamically by CodeIgniter.

- -

If you would like to edit this page you'll find it located at:

- -
-			
-				application/views/welcome_message.php
-			
-		
- -

The corresponding controller for this page is found at:

- -
-			
-				application/controllers/Home.php
-			
-		
- -

If you are exploring CodeIgniter for the very first time, you should start by reading the User Guide.

- -
- - - -
diff --git a/changelogs/CHANGELOG_4.0.md b/changelogs/CHANGELOG_4.0.md new file mode 100644 index 000000000000..57492ecaed6a --- /dev/null +++ b/changelogs/CHANGELOG_4.0.md @@ -0,0 +1,2618 @@ +# Changelog 4.0 + +## [v4.0.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.5) (2021-01-31) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.4...v4.0.5) + +**Deprecations:** + +- `CodeIgniter\Database\ModelFactory` is now deprecated in favor of `CodeIgniter\Config\Factories::models()` +- `CodeIgniter\Config\Config` is now deprecated in favor of `CodeIgniter\Config\Factories::config()` +- HTTP Layer Refactor: Numerous deprecations have been made towards a transition to a PSR-compliant HTTP layer. [See the User Guide](user_guide_src/source/installation/upgrade_405.rst) + +**Mime Type Detection** + +- `Config\Mimes::guessExtensionFromType` now only reverse searches the `$mimes` array if no extension is proposed (i.e., usually not for uploaded files). +- The fallback values of `UploadedFile->getExtension()` and `UploadedFile->guessExtension()` have been changed. `UploadedFile->getExtension()` now returns `$this->getClientExtension()` instead of `''`; `UploadedFile->guessExtension()` now returns `''` instead of `$this->getClientExtension()`. +These changes increase security when handling uploaded files as the client can no longer force a wrong mime type on the application. However, these might affect how file extensions are detected in your application. + +**Implemented enhancements:** + +- Bug: controller routing on modules not working [\#3927](https://github.com/codeigniter4/CodeIgniter4/issues/3927) +- CLI: method prompt should accept array validation rules [\#3766](https://github.com/codeigniter4/CodeIgniter4/issues/3766) +- Validation: permit\_empty, optional arguments [\#3670](https://github.com/codeigniter4/CodeIgniter4/issues/3670) +- php 8 support [\#3498](https://github.com/codeigniter4/CodeIgniter4/issues/3498) +- getRoutesOptions should return the controller and method if available [\#3445](https://github.com/codeigniter4/CodeIgniter4/issues/3445) +- before function in FilterInterface is missing response param [\#2085](https://github.com/codeigniter4/CodeIgniter4/issues/2085) +- Feature Request: Centralized loggedInUser before RC [\#2055](https://github.com/codeigniter4/CodeIgniter4/issues/2055) + +**Fixed bugs:** + +- Bug: UploadedFile::store\(\) can't return null [\#4183](https://github.com/codeigniter4/CodeIgniter4/issues/4183) +- Bug: BaseBuilder::getCompiledDelete\(\) runs real query [\#4180](https://github.com/codeigniter4/CodeIgniter4/issues/4180) +- Bug: Deprecated: Required parameter $userAgent follows optional parameter $body in /opt/lampp/htdocs/framework-4.0.4/system/HTTP/IncomingRequest.php on line 161 [\#4172](https://github.com/codeigniter4/CodeIgniter4/issues/4172) +- Bug: table template closes tbody after tfoot [\#4155](https://github.com/codeigniter4/CodeIgniter4/issues/4155) +- Bug: delete\_cookie\(\) helper not working [\#4149](https://github.com/codeigniter4/CodeIgniter4/issues/4149) +- Bug: Required parameter $userAgent follows optional parameter $body in [\#4148](https://github.com/codeigniter4/CodeIgniter4/issues/4148) +- Bug: spark issue [\#4144](https://github.com/codeigniter4/CodeIgniter4/issues/4144) +- Bug: PostgreSQL driver issues [\#4142](https://github.com/codeigniter4/CodeIgniter4/issues/4142) +- Bug: phpunit coverage report causes `Cannot declare class Config\App, because the name is already in use` [\#4114](https://github.com/codeigniter4/CodeIgniter4/issues/4114) +- Bug: Wrong file/line in exceptions created by "factory" methods [\#4110](https://github.com/codeigniter4/CodeIgniter4/issues/4110) +- Bug: Request::withMethod\(\) uses deprecated code [\#4109](https://github.com/codeigniter4/CodeIgniter4/issues/4109) +- Bug: View rendering, extracted variables overwrites existed variables\(arguments and etc.\) [\#4108](https://github.com/codeigniter4/CodeIgniter4/issues/4108) +- Bug: Loss of escape value and data in the model [\#4087](https://github.com/codeigniter4/CodeIgniter4/issues/4087) +- Bug: classes overwrites parameter defaults, phpDoc's defined in interfaces [\#4086](https://github.com/codeigniter4/CodeIgniter4/issues/4086) +- Bug: getRandomName Return file extension .csv when upload docx or xlsx becouse mimetype application/octet-stream are set [\#4084](https://github.com/codeigniter4/CodeIgniter4/issues/4084) +- Bug: Required parameter $from follows optional parameter $verbs [\#4076](https://github.com/codeigniter4/CodeIgniter4/issues/4076) +- Bug: username input form shows root as a default value [\#4062](https://github.com/codeigniter4/CodeIgniter4/issues/4062) +- Bug: Issue with pagination [\#4045](https://github.com/codeigniter4/CodeIgniter4/issues/4045) +- Bug: Model calls builder before initializing [\#4036](https://github.com/codeigniter4/CodeIgniter4/issues/4036) +- Bug: can't run migrations when CI\_ENVIRONMENT = testing [\#4033](https://github.com/codeigniter4/CodeIgniter4/issues/4033) +- Bug: can't get environment variable with dot [\#4026](https://github.com/codeigniter4/CodeIgniter4/issues/4026) +- Bug: CodeIgniter model not recognizing custom deletedField in find queries [\#3999](https://github.com/codeigniter4/CodeIgniter4/issues/3999) +- Bug: assertSee\(\) can not assert title tag. [\#3984](https://github.com/codeigniter4/CodeIgniter4/issues/3984) +- Bug: RAR file detected as CSV [\#3979](https://github.com/codeigniter4/CodeIgniter4/issues/3979) +- Bug: Session Initialization via DatabaseHandler [\#3978](https://github.com/codeigniter4/CodeIgniter4/issues/3978) +- Bug: required\_with validation rule does not work with arrays [\#3965](https://github.com/codeigniter4/CodeIgniter4/issues/3965) +- Bug: helper cookie not working [\#3939](https://github.com/codeigniter4/CodeIgniter4/issues/3939) +- Bug: Uploaded SRT files are saved as CSV [\#3921](https://github.com/codeigniter4/CodeIgniter4/issues/3921) +- Bug: Pre-commit hook for Phpstan and sniffer stop working after e111f04d74569e413c5aede3ed9bd9fa1ce7dca2 [\#3920](https://github.com/codeigniter4/CodeIgniter4/issues/3920) +- Bug: Documentation Example Leads to Bug [\#3914](https://github.com/codeigniter4/CodeIgniter4/issues/3914) +- Bug: Route filter runs twice [\#3902](https://github.com/codeigniter4/CodeIgniter4/issues/3902) +- Bug: Premature empty check in Model-\>update function. [\#3896](https://github.com/codeigniter4/CodeIgniter4/issues/3896) +- Bug: Sqldrv problems to insert when change DBPrefix [\#3881](https://github.com/codeigniter4/CodeIgniter4/issues/3881) +- Bug: CI4 won't recognise current namespace for language files [\#3867](https://github.com/codeigniter4/CodeIgniter4/issues/3867) +- Unexpected validation bug [\#3859](https://github.com/codeigniter4/CodeIgniter4/issues/3859) +- Bug: single\_service does not set service's arguments correctly [\#3854](https://github.com/codeigniter4/CodeIgniter4/issues/3854) +- Bug: debugbar should not insert code in code tag [\#3847](https://github.com/codeigniter4/CodeIgniter4/issues/3847) +- Parser won't properly parse tags that have a similar name [\#3841](https://github.com/codeigniter4/CodeIgniter4/issues/3841) +- Bug: insertBatch not generating createdField [\#3838](https://github.com/codeigniter4/CodeIgniter4/issues/3838) +- Bug: Parser - Replacements happening even if key does not fully matches { value } [\#3825](https://github.com/codeigniter4/CodeIgniter4/issues/3825) +- Bug: Parser preg\_replace\_callback exception if template has \# [\#3824](https://github.com/codeigniter4/CodeIgniter4/issues/3824) +- Translation function 'lang' does not work as described [\#3822](https://github.com/codeigniter4/CodeIgniter4/issues/3822) +- Bug: File rewrite.php is always lost in large-request unhandled error [\#3818](https://github.com/codeigniter4/CodeIgniter4/issues/3818) +- Bug: Model::builder\(\) ignores parameter [\#3793](https://github.com/codeigniter4/CodeIgniter4/issues/3793) +- Bug: Warning no tests found in CodeIgiter\Tests\... [\#3788](https://github.com/codeigniter4/CodeIgniter4/issues/3788) +- Bug: Class '\CodeIgniter\Database\pdo\Connection' not found [\#3785](https://github.com/codeigniter4/CodeIgniter4/issues/3785) +- Bug: Composer php spark migrate error [\#3771](https://github.com/codeigniter4/CodeIgniter4/issues/3771) +- Bug: ORDER BY RANDOM ON SQLite3 [\#3768](https://github.com/codeigniter4/CodeIgniter4/issues/3768) +- Bug: CLI: generateDimensions fails with a uncaught exception when exec is disabled [\#3762](https://github.com/codeigniter4/CodeIgniter4/issues/3762) +- Bug: createTable if not exists not working [\#3757](https://github.com/codeigniter4/CodeIgniter4/issues/3757) +- Bug: SQLite drop column corrupts table cache [\#3752](https://github.com/codeigniter4/CodeIgniter4/issues/3752) +- Bug: route filters don't apply for different methods [\#3733](https://github.com/codeigniter4/CodeIgniter4/issues/3733) +- /system/Images/Image.php image\(\)-\>copy\(\) mkdir [\#3732](https://github.com/codeigniter4/CodeIgniter4/issues/3732) +- Bug: route bug [\#3731](https://github.com/codeigniter4/CodeIgniter4/issues/3731) +- page not redirecting. when i redirecting by calling function [\#3729](https://github.com/codeigniter4/CodeIgniter4/issues/3729) +- Bug: \Config\Services::image\(\)-\>flatten\(\) [\#3728](https://github.com/codeigniter4/CodeIgniter4/issues/3728) +- Bug: Parser will overwrite the value if the initial variable is same [\#3726](https://github.com/codeigniter4/CodeIgniter4/issues/3726) +- Bug: Validating Json Requests [\#3719](https://github.com/codeigniter4/CodeIgniter4/issues/3719) +- Bug: view caching not work on windows [\#3711](https://github.com/codeigniter4/CodeIgniter4/issues/3711) +- Bug: getRoutesOptions return wrong when I have \>2 routes has same name, but different method \(HTTPVerb\) [\#3700](https://github.com/codeigniter4/CodeIgniter4/issues/3700) +- Bug: Validation with request raw body. [\#3694](https://github.com/codeigniter4/CodeIgniter4/issues/3694) +- Image format webp is not preserve transparent [\#3690](https://github.com/codeigniter4/CodeIgniter4/issues/3690) +- problem: [\#3686](https://github.com/codeigniter4/CodeIgniter4/issues/3686) +- Documentation Error [\#3668](https://github.com/codeigniter4/CodeIgniter4/issues/3668) +- Bug: BaseBuilder's query is wrong [\#3659](https://github.com/codeigniter4/CodeIgniter4/issues/3659) +- Bug: The lifetime of the CSRF check cookie cannot be set to 0 \(Session\). [\#3655](https://github.com/codeigniter4/CodeIgniter4/issues/3655) +- Bug: isRedirect does not respond depending on how redirects are set up. [\#3654](https://github.com/codeigniter4/CodeIgniter4/issues/3654) +- Bug: SQL Error when countAllResults, groupBy and DBPrefix used together [\#3651](https://github.com/codeigniter4/CodeIgniter4/issues/3651) +- Bug: helper current\_url\(\) return wrong protocol [\#3648](https://github.com/codeigniter4/CodeIgniter4/issues/3648) +- Bug: IncomingRequest::setLocale\(\) [\#3640](https://github.com/codeigniter4/CodeIgniter4/issues/3640) +- CodeIgniter\Database\Exceptions\DatabaseException \#8 Unable to connect to the database. [\#3639](https://github.com/codeigniter4/CodeIgniter4/issues/3639) +- Bug: Cannot use view filter on array values [\#3630](https://github.com/codeigniter4/CodeIgniter4/issues/3630) +- Bug: Changelog navigation is not working properly [\#3625](https://github.com/codeigniter4/CodeIgniter4/issues/3625) +- Bug: Routing 404 Override confusion under route group [\#3623](https://github.com/codeigniter4/CodeIgniter4/issues/3623) +- Bug: Target batch not found when running `php spark migrate:rollback` [\#3620](https://github.com/codeigniter4/CodeIgniter4/issues/3620) +- Bug: getHeaders returns Array with no values [\#3616](https://github.com/codeigniter4/CodeIgniter4/issues/3616) +- Ignore this, was a false report [\#3611](https://github.com/codeigniter4/CodeIgniter4/issues/3611) +- Bug: Incorrect type that fails strict\_types=1 [\#3610](https://github.com/codeigniter4/CodeIgniter4/issues/3610) +- The isAJAX method does not exist in filters [\#3604](https://github.com/codeigniter4/CodeIgniter4/issues/3604) +- Bug: `current\_url\(\)` helper returns wrong url with slash between host and port [\#3603](https://github.com/codeigniter4/CodeIgniter4/issues/3603) +- Bug: Connection ID unknown immediately after connection [\#3601](https://github.com/codeigniter4/CodeIgniter4/issues/3601) +- Bug: Join Query is not working in Library [\#3600](https://github.com/codeigniter4/CodeIgniter4/issues/3600) +- Bug: Database cache not working [\#3597](https://github.com/codeigniter4/CodeIgniter4/issues/3597) +- Bug: Issue in the route, redirect to parent domain, if you put / at the end of url [\#3595](https://github.com/codeigniter4/CodeIgniter4/issues/3595) +- Bug: Segments Also Include The Segments In BASE\_URL [\#3594](https://github.com/codeigniter4/CodeIgniter4/issues/3594) +- Bug: Route Filters doesn't restart in FeatureTestCase. [\#3591](https://github.com/codeigniter4/CodeIgniter4/issues/3591) +- Bug: CURL call returns always code 200 in case de server uses HTTP/2 [\#3586](https://github.com/codeigniter4/CodeIgniter4/issues/3586) +- Bug: Language folders inside locale folders NOT Working ! [\#3582](https://github.com/codeigniter4/CodeIgniter4/issues/3582) +- Bug: Uninitialized string offset: 1 CI 4 [\#3573](https://github.com/codeigniter4/CodeIgniter4/issues/3573) +- Deprecated assertArraySubset in PHPUnit8 used in FeatureResponse::assertJSONFragment [\#3562](https://github.com/codeigniter4/CodeIgniter4/issues/3562) +- Wrong HTTP status code [\#3558](https://github.com/codeigniter4/CodeIgniter4/issues/3558) +- Bug: Invalid serialization data for DateTime object [\#3553](https://github.com/codeigniter4/CodeIgniter4/issues/3553) +- Bug: Setting session expires parameter via $this-\>sessionExpiration [\#3543](https://github.com/codeigniter4/CodeIgniter4/issues/3543) +- Bug: Upload file validation, $model-\>validate return true if failed [\#3532](https://github.com/codeigniter4/CodeIgniter4/issues/3532) +- Bug: Inconsistent behavior of view renderer on Windows and Linux [\#3529](https://github.com/codeigniter4/CodeIgniter4/issues/3529) +- Bug: Add dash to parser plugin regex [\#3523](https://github.com/codeigniter4/CodeIgniter4/issues/3523) +- Bug: When Cronjob run the ip address of the request is 0.0.0.0 [\#3512](https://github.com/codeigniter4/CodeIgniter4/issues/3512) +- Bug: Inconsistency in replace\(\) method [\#3510](https://github.com/codeigniter4/CodeIgniter4/issues/3510) +- Bug: setPath function in UploadedFile.php is writing the index.html wrong [\#3506](https://github.com/codeigniter4/CodeIgniter4/issues/3506) +- Bug: Cannot get session data after server redirecting [\#3503](https://github.com/codeigniter4/CodeIgniter4/issues/3503) +- Bug: Database group defined in .env doesn't work [\#3497](https://github.com/codeigniter4/CodeIgniter4/issues/3497) +- Bug: I cant upload mp4 files [\#3494](https://github.com/codeigniter4/CodeIgniter4/issues/3494) +- Bug: Error message for matches rule doesn't support nested params [\#3492](https://github.com/codeigniter4/CodeIgniter4/issues/3492) +- CI\_VERSION = '4.0.0-beta.4'; session 文件file缓存问题失效问题;session\_start\(\): Failed to decode session object. Session has been destroyed [\#3485](https://github.com/codeigniter4/CodeIgniter4/issues/3485) +- Bug: onlyDeleted\(\) conflicts to paginate\(\) [\#3482](https://github.com/codeigniter4/CodeIgniter4/issues/3482) +- Bug: Unable to connect to the database [\#3477](https://github.com/codeigniter4/CodeIgniter4/issues/3477) +- Bug: Argument 1 passed to CodeIgniter\Config\Services::request\(\) must be an instance of Config\App or null, instance of BackEnd\Config\App [\#3475](https://github.com/codeigniter4/CodeIgniter4/issues/3475) +- Bug: Fatal error running [\#3473](https://github.com/codeigniter4/CodeIgniter4/issues/3473) +- Bug: set\(\) doesn't work on DateTime database field types [\#3471](https://github.com/codeigniter4/CodeIgniter4/issues/3471) +- before\(\) Filters that are executed are missing from codeigniter debug toolbar. [\#3470](https://github.com/codeigniter4/CodeIgniter4/issues/3470) +- Bug: Model insert method always insert with current datetime on updatedField [\#3469](https://github.com/codeigniter4/CodeIgniter4/issues/3469) +- Bug: The search function in the documentation is not working [\#3458](https://github.com/codeigniter4/CodeIgniter4/issues/3458) +- Bug: env variable database.default.dsn not working for mysqli [\#3456](https://github.com/codeigniter4/CodeIgniter4/issues/3456) +- Bug: [\#3453](https://github.com/codeigniter4/CodeIgniter4/issues/3453) +- Bug: form\_textarea in form\_helper does not create specified 'rows' [\#3452](https://github.com/codeigniter4/CodeIgniter4/issues/3452) +- Bug: afterUpdate event return array instead of key value [\#3450](https://github.com/codeigniter4/CodeIgniter4/issues/3450) +- Using Cronjob with php line argument while using Crontab redirects the script and never executes [\#3444](https://github.com/codeigniter4/CodeIgniter4/issues/3444) +- Bug: Support for SameSite cookie setting missing [\#3442](https://github.com/codeigniter4/CodeIgniter4/issues/3442) +- Bug: Validation mime\_in SVG files not working correctly [\#3439](https://github.com/codeigniter4/CodeIgniter4/issues/3439) +- Bug: Cannot declare class Config\Paths, because the name is already in use [\#3434](https://github.com/codeigniter4/CodeIgniter4/issues/3434) +- Bug: delete\_cookie\(\) helper function not working [\#3433](https://github.com/codeigniter4/CodeIgniter4/issues/3433) +- Bug: insertBatch not working correctly [\#3432](https://github.com/codeigniter4/CodeIgniter4/issues/3432) +- Feature request : CodeIgniter\File with SplFileInfo does not have a method to rewrite parts of the file only to append an CSV style row [\#3431](https://github.com/codeigniter4/CodeIgniter4/issues/3431) +- Bug: SMTP to port 465 should use TLS from the start [\#3429](https://github.com/codeigniter4/CodeIgniter4/issues/3429) +- Bug: Form data and file is not receiving well formed with PUT Method [\#3417](https://github.com/codeigniter4/CodeIgniter4/issues/3417) +- Bug: form\_textarea helper row and col defaults not overwriting when defined as $extra [\#3412](https://github.com/codeigniter4/CodeIgniter4/issues/3412) +- Encryption Class does not allow to change digest parameter from SHA512 to SHA256 or others [\#3404](https://github.com/codeigniter4/CodeIgniter4/issues/3404) +- Bug: table.Array in first\(\) ... - and complex primary keys! ;-\) [\#3394](https://github.com/codeigniter4/CodeIgniter4/issues/3394) +- Error: Email SMTP configured wrong gives error [\#3390](https://github.com/codeigniter4/CodeIgniter4/issues/3390) +- Bug: CodeIgniter\Router\Router hasLocale returns true even if {locale} is absent [\#3386](https://github.com/codeigniter4/CodeIgniter4/issues/3386) +- Bug: Logs Collector isn't collecting logs for Debug Toolbar [\#3376](https://github.com/codeigniter4/CodeIgniter4/issues/3376) +- Bug: Entity's original is not set [\#3370](https://github.com/codeigniter4/CodeIgniter4/issues/3370) +- Bug: warning in Routes.php [\#3369](https://github.com/codeigniter4/CodeIgniter4/issues/3369) +- Bug: Model can not insert Entity [\#3368](https://github.com/codeigniter4/CodeIgniter4/issues/3368) +- Bug: stringify\_attributes\(\) looks unfinished. [\#3363](https://github.com/codeigniter4/CodeIgniter4/issues/3363) +- Bug: php spark migrate throws wrongful CodeIgniter\Database\Exceptions\DatabaseException Unable to connect to the database [\#3359](https://github.com/codeigniter4/CodeIgniter4/issues/3359) +- Bug: Image watermark after save.. text not align in center middle when resolution lower and higher. [\#3356](https://github.com/codeigniter4/CodeIgniter4/issues/3356) +- Bug: SQL Lite driver with Builder -\> InsertBatch has intermittend lastError reporting issues [\#3350](https://github.com/codeigniter4/CodeIgniter4/issues/3350) +- Bug: isCLI error when calling a controller from a Command Line [\#3342](https://github.com/codeigniter4/CodeIgniter4/issues/3342) +- Bug: missing clear upgrade instructions [\#3332](https://github.com/codeigniter4/CodeIgniter4/issues/3332) +- Bug: API requests with a trailing forward slash [\#3330](https://github.com/codeigniter4/CodeIgniter4/issues/3330) +- Bug: Pager pagination \( page value in url \) [\#3328](https://github.com/codeigniter4/CodeIgniter4/issues/3328) +- Bug: View layout name hierarchy is not displaying correctly on Debugbar Views [\#3327](https://github.com/codeigniter4/CodeIgniter4/issues/3327) +- Bug: php spark migrate on testing environment doesn't work [\#3309](https://github.com/codeigniter4/CodeIgniter4/issues/3309) +- Bug: Empty Entity date attributes receive current timestamp when accessed [\#3251](https://github.com/codeigniter4/CodeIgniter4/issues/3251) +- Bug: no Filter arguments passed [\#3216](https://github.com/codeigniter4/CodeIgniter4/issues/3216) +- Bug: Url Helper have a bug [\#3180](https://github.com/codeigniter4/CodeIgniter4/issues/3180) +- $pager-\>links\(\) not working inside view layouts [\#3164](https://github.com/codeigniter4/CodeIgniter4/issues/3164) +- Bug: AH01075: Error dispatching request on Basic CI4 [\#3110](https://github.com/codeigniter4/CodeIgniter4/issues/3110) +- Bug: Using assertJSONFragment with respond\(\) in ResponseTrait [\#3079](https://github.com/codeigniter4/CodeIgniter4/issues/3079) +- Bug: Filters 'except' option not removing DebugToolbar comment for view [\#3002](https://github.com/codeigniter4/CodeIgniter4/issues/3002) +- Bug: useSoftDeletes with like function [\#2380](https://github.com/codeigniter4/CodeIgniter4/issues/2380) + +**Closed issues:** + +- Seed Command in document Not found [\#4154](https://github.com/codeigniter4/CodeIgniter4/issues/4154) +- I18n/L10n: auto update translations [\#4151](https://github.com/codeigniter4/CodeIgniter4/issues/4151) +- Calling update\(\) with an Entity throws an error [\#4143](https://github.com/codeigniter4/CodeIgniter4/issues/4143) +- BaseConfig property issue [\#4140](https://github.com/codeigniter4/CodeIgniter4/issues/4140) +- Bug: [\#4126](https://github.com/codeigniter4/CodeIgniter4/issues/4126) +- Bug: a new installation of codeignitor 4.0.4 show 404 error Sorry! Cannot seem to find the page you were looking for. [\#4111](https://github.com/codeigniter4/CodeIgniter4/issues/4111) +- Bug PHP SPARK MIGRATE [\#4101](https://github.com/codeigniter4/CodeIgniter4/issues/4101) +- Pagination work in local, but error in server [\#4096](https://github.com/codeigniter4/CodeIgniter4/issues/4096) +- updated\_at field is filled with the current timestamp when inserting new data along with created\_at [\#4038](https://github.com/codeigniter4/CodeIgniter4/issues/4038) +- Config for pager [\#4030](https://github.com/codeigniter4/CodeIgniter4/issues/4030) +- setUpdateBatch [\#4025](https://github.com/codeigniter4/CodeIgniter4/issues/4025) +- Empty html-file created when moving uploaded files with $img-\>move\(\) [\#4020](https://github.com/codeigniter4/CodeIgniter4/issues/4020) +- php spark serve error on php 8 [\#3980](https://github.com/codeigniter4/CodeIgniter4/issues/3980) +- Bug: PHP8 session flush data is not deleted [\#3974](https://github.com/codeigniter4/CodeIgniter4/issues/3974) +- PHP8: Deprecate required param after optional [\#3957](https://github.com/codeigniter4/CodeIgniter4/issues/3957) +- Undefined function [\#3954](https://github.com/codeigniter4/CodeIgniter4/issues/3954) +- Missing function locale\_set\_default\(...\) in Codeigniter 4 [\#3953](https://github.com/codeigniter4/CodeIgniter4/issues/3953) +- Language\en\Language.php is only used in testing [\#3948](https://github.com/codeigniter4/CodeIgniter4/issues/3948) +- Bug: locale\_set\_default causing "Whoops" failure in MacOS PHP 7.3.9 on develop branch [\#3933](https://github.com/codeigniter4/CodeIgniter4/issues/3933) +- Pdo driver not found [\#3922](https://github.com/codeigniter4/CodeIgniter4/issues/3922) +- Mysqli Backup utility? [\#3906](https://github.com/codeigniter4/CodeIgniter4/issues/3906) +- Relationships - Many to many [\#3885](https://github.com/codeigniter4/CodeIgniter4/issues/3885) +- Bug: How can remove Codeinatore default icon [\#3883](https://github.com/codeigniter4/CodeIgniter4/issues/3883) +- How Can remove CodeIgniter4 icon [\#3882](https://github.com/codeigniter4/CodeIgniter4/issues/3882) +- Codeigniter review 2021 [\#3880](https://github.com/codeigniter4/CodeIgniter4/issues/3880) +- url\_title doesn't work with decimal number in title [\#3878](https://github.com/codeigniter4/CodeIgniter4/issues/3878) +- IncomingRequest Class "hasFile" function does not exist as described in the documentation. [\#3852](https://github.com/codeigniter4/CodeIgniter4/issues/3852) +- Dynamically changing supported languages [\#3844](https://github.com/codeigniter4/CodeIgniter4/issues/3844) +- CodeIgniter\Database\Exceptions\DatabaseException \#8 [\#3826](https://github.com/codeigniter4/CodeIgniter4/issues/3826) +- Blank page show [\#3812](https://github.com/codeigniter4/CodeIgniter4/issues/3812) +- Bug: folder tests not found [\#3807](https://github.com/codeigniter4/CodeIgniter4/issues/3807) +- Typo in the doc: cache\_info\(\) [\#3800](https://github.com/codeigniter4/CodeIgniter4/issues/3800) +- only\_full\_group\_by - mysqli\_sql\_exception \#1055 - MySql 5.7.24 [\#3795](https://github.com/codeigniter4/CodeIgniter4/issues/3795) +- Dev: Split ModelTest [\#3792](https://github.com/codeigniter4/CodeIgniter4/issues/3792) +- The formatMessage function of the Language class works intermittently [\#3784](https://github.com/codeigniter4/CodeIgniter4/issues/3784) +- .htaccess problem with syntax [\#3778](https://github.com/codeigniter4/CodeIgniter4/issues/3778) +- The page does not open correctly:\( [\#3770](https://github.com/codeigniter4/CodeIgniter4/issues/3770) +- Restore method for CodeIgniter Model. [\#3767](https://github.com/codeigniter4/CodeIgniter4/issues/3767) +- Bug: Model update\(\) only set first array value [\#3764](https://github.com/codeigniter4/CodeIgniter4/issues/3764) +- Bug: CLI: Exception view shows full path [\#3763](https://github.com/codeigniter4/CodeIgniter4/issues/3763) +- Bug: Maximum execution time exceeds while handling IPv6 Reverse Proxy IPs [\#3760](https://github.com/codeigniter4/CodeIgniter4/issues/3760) +- Fabricators documentation error [\#3743](https://github.com/codeigniter4/CodeIgniter4/issues/3743) +- `is\_unique` is not considering the db prefix in checking for unique values in db [\#3741](https://github.com/codeigniter4/CodeIgniter4/issues/3741) +- Grouping routes should let me pass additional variables within the closure function [\#3691](https://github.com/codeigniter4/CodeIgniter4/issues/3691) +- cannot find Ftp library. [\#3679](https://github.com/codeigniter4/CodeIgniter4/issues/3679) +- Make sessions never expire with $config\['sess\_expiration'\] = -1 [\#3677](https://github.com/codeigniter4/CodeIgniter4/issues/3677) +- Migration trouble [\#3624](https://github.com/codeigniter4/CodeIgniter4/issues/3624) +- Files as optional in validation rules [\#3619](https://github.com/codeigniter4/CodeIgniter4/issues/3619) +- $\_SERVER should not have .env file info [\#3615](https://github.com/codeigniter4/CodeIgniter4/issues/3615) +- Bug pars xml [\#3588](https://github.com/codeigniter4/CodeIgniter4/issues/3588) +- Bug: When have multi filter class, and "before" function return true,other filters after this filter not working [\#3579](https://github.com/codeigniter4/CodeIgniter4/issues/3579) +- can not install on linux os [\#3572](https://github.com/codeigniter4/CodeIgniter4/issues/3572) +- Incorrect grouping function names in documentation [\#3551](https://github.com/codeigniter4/CodeIgniter4/issues/3551) +- New Edge Browser missing from user agents config [\#3513](https://github.com/codeigniter4/CodeIgniter4/issues/3513) +- Form validation does not have a rule to validate if field value differs from a string [\#3462](https://github.com/codeigniter4/CodeIgniter4/issues/3462) +- updateBatch does not accept entities [\#3451](https://github.com/codeigniter4/CodeIgniter4/issues/3451) +- Support PSR4 [\#3405](https://github.com/codeigniter4/CodeIgniter4/issues/3405) +- CodeIgniter4 is ready to work with PHP 7.4? [\#3389](https://github.com/codeigniter4/CodeIgniter4/issues/3389) +- Using CodeIgniter4 with Oracle Database [\#3388](https://github.com/codeigniter4/CodeIgniter4/issues/3388) +- Can't get Controllers subfolder working [\#3347](https://github.com/codeigniter4/CodeIgniter4/issues/3347) +- Why redirect\(\)-\>to\(\) doesn't work inside events Model? [\#3346](https://github.com/codeigniter4/CodeIgniter4/issues/3346) +- Bug: requests PUT type without segment redirects to index method instead update method [\#3343](https://github.com/codeigniter4/CodeIgniter4/issues/3343) +- Migrations not working [\#3317](https://github.com/codeigniter4/CodeIgniter4/issues/3317) +- CURL Error htts SSL [\#3314](https://github.com/codeigniter4/CodeIgniter4/issues/3314) +- codeigniter composer global installer [\#3266](https://github.com/codeigniter4/CodeIgniter4/issues/3266) +- Bug: Migrations Deprecated [\#3195](https://github.com/codeigniter4/CodeIgniter4/issues/3195) +- Managed to reproduce this issue. Was uploading an image with jquery.dm-uploader.min.js, and trying to do some image manipulation, but when I got this error: [\#3174](https://github.com/codeigniter4/CodeIgniter4/issues/3174) +- Make the Model work without auto\_increment primary key [\#3134](https://github.com/codeigniter4/CodeIgniter4/issues/3134) +- Call to undefined function CodeIgniter\Debug\current\_url\(\) [\#3106](https://github.com/codeigniter4/CodeIgniter4/issues/3106) +- Bug: select\(\) & selectSum\(\) not consistent / aligned with docs [\#3019](https://github.com/codeigniter4/CodeIgniter4/issues/3019) +- Remove /public in URL not working if using localhost [\#2930](https://github.com/codeigniter4/CodeIgniter4/issues/2930) +- The problem is in getting the file sharing by Mime [\#2732](https://github.com/codeigniter4/CodeIgniter4/issues/2732) +- Migration on PostgreSQL failes for tables with foreign keys [\#2575](https://github.com/codeigniter4/CodeIgniter4/issues/2575) +- Automatic Entity cast for Parser [\#2317](https://github.com/codeigniter4/CodeIgniter4/issues/2317) +- Feature: Cache Driver - Serialize/Unserialize Objects [\#2111](https://github.com/codeigniter4/CodeIgniter4/issues/2111) +- Model set $escape problem [\#1929](https://github.com/codeigniter4/CodeIgniter4/issues/1929) +- \I18n\Time setTimezone does not work as expected [\#1807](https://github.com/codeigniter4/CodeIgniter4/issues/1807) +- TODO Database utility backup builder [\#1257](https://github.com/codeigniter4/CodeIgniter4/issues/1257) +- Port FTP library from CI3 [\#506](https://github.com/codeigniter4/CodeIgniter4/issues/506) +- Port MSSql Database driver from CI3 [\#503](https://github.com/codeigniter4/CodeIgniter4/issues/503) + +**Merged pull requests:** + +- Finishing touches to generator refactor [\#4197](https://github.com/codeigniter4/CodeIgniter4/pull/4197) ([paulbalandan](https://github.com/paulbalandan)) +- Add additional empty checks after field protection for update/insert. [\#4195](https://github.com/codeigniter4/CodeIgniter4/pull/4195) ([sfadschm](https://github.com/sfadschm)) +- Minor fixes in Common.php [\#4192](https://github.com/codeigniter4/CodeIgniter4/pull/4192) ([kenjis](https://github.com/kenjis)) +- Fix Parser file path in ViewException message is empty [\#4191](https://github.com/codeigniter4/CodeIgniter4/pull/4191) ([kenjis](https://github.com/kenjis)) +- docs: Fix double "the" [\#4190](https://github.com/codeigniter4/CodeIgniter4/pull/4190) ([kenjis](https://github.com/kenjis)) +- Fix typo in IncomingRequestTest [\#4189](https://github.com/codeigniter4/CodeIgniter4/pull/4189) ([kenjis](https://github.com/kenjis)) +- feat: add methods to get page numbers in PagerRenderer [\#4188](https://github.com/codeigniter4/CodeIgniter4/pull/4188) ([kenjis](https://github.com/kenjis)) +- fix: UploadedFile::store\(\) return type inconsistancy [\#4187](https://github.com/codeigniter4/CodeIgniter4/pull/4187) ([kenjis](https://github.com/kenjis)) +- Add STL mime support [\#4186](https://github.com/codeigniter4/CodeIgniter4/pull/4186) ([MGatner](https://github.com/MGatner)) +- \[Rector\] Run Rector when composer.json updated [\#4185](https://github.com/codeigniter4/CodeIgniter4/pull/4185) ([samsonasik](https://github.com/samsonasik)) +- new array helper: array\_flatten\_with\_dots [\#4184](https://github.com/codeigniter4/CodeIgniter4/pull/4184) ([paulbalandan](https://github.com/paulbalandan)) +- fix: BaseBuilder::getCompiledDelete\(\) runs real query [\#4181](https://github.com/codeigniter4/CodeIgniter4/pull/4181) ([kenjis](https://github.com/kenjis)) +- fix the missing / hidden userguide [\#4175](https://github.com/codeigniter4/CodeIgniter4/pull/4175) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Revert "Update phpstan/phpstan requirement from 0.12.69 to 0.12.70" [\#4173](https://github.com/codeigniter4/CodeIgniter4/pull/4173) ([samsonasik](https://github.com/samsonasik)) +- Add missing escaping in error\_exception.php [\#4171](https://github.com/codeigniter4/CodeIgniter4/pull/4171) ([kenjis](https://github.com/kenjis)) +- Update phpstan/phpstan requirement from 0.12.69 to 0.12.70 [\#4170](https://github.com/codeigniter4/CodeIgniter4/pull/4170) ([dependabot[bot]](https://github.com/apps/dependabot)) +- add phpunit.xml.dist to .gitattributes for admin/framework [\#4163](https://github.com/codeigniter4/CodeIgniter4/pull/4163) ([samsonasik](https://github.com/samsonasik)) +- Fix strict checking for SQLite3 memory filename [\#4161](https://github.com/codeigniter4/CodeIgniter4/pull/4161) ([paulbalandan](https://github.com/paulbalandan)) +- Discuss dbcreate in userguide [\#4160](https://github.com/codeigniter4/CodeIgniter4/pull/4160) ([paulbalandan](https://github.com/paulbalandan)) +- Fix misplaced closing tbody [\#4159](https://github.com/codeigniter4/CodeIgniter4/pull/4159) ([paulbalandan](https://github.com/paulbalandan)) +- \[Develop\] Fixes \#4114 Cannot declare class Config\App error on running PHPUnit [\#4157](https://github.com/codeigniter4/CodeIgniter4/pull/4157) ([samsonasik](https://github.com/samsonasik)) +- Specifically exclude migrations from class mapping [\#4156](https://github.com/codeigniter4/CodeIgniter4/pull/4156) ([paulbalandan](https://github.com/paulbalandan)) +- config: add logger.threshold in env as comment [\#4153](https://github.com/codeigniter4/CodeIgniter4/pull/4153) ([kenjis](https://github.com/kenjis)) +- Update phpstan/phpstan requirement from 0.12.68 to 0.12.69 [\#4152](https://github.com/codeigniter4/CodeIgniter4/pull/4152) ([dependabot[bot]](https://github.com/apps/dependabot)) +- convert indentation from tabs to spaces & update code-block \[changelogs, database\] [\#4150](https://github.com/codeigniter4/CodeIgniter4/pull/4150) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Fix for retrieving migration history [\#4147](https://github.com/codeigniter4/CodeIgniter4/pull/4147) ([michalsn](https://github.com/michalsn)) +- Fix phpstan notice [\#4146](https://github.com/codeigniter4/CodeIgniter4/pull/4146) ([samsonasik](https://github.com/samsonasik)) +- Update docs for Entity [\#4145](https://github.com/codeigniter4/CodeIgniter4/pull/4145) ([michalsn](https://github.com/michalsn)) +- update faker [\#4139](https://github.com/codeigniter4/CodeIgniter4/pull/4139) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- docs: add table of contents in 2 libs [\#4138](https://github.com/codeigniter4/CodeIgniter4/pull/4138) ([kenjis](https://github.com/kenjis)) +- PHPStan Fixes [\#4136](https://github.com/codeigniter4/CodeIgniter4/pull/4136) ([MGatner](https://github.com/MGatner)) +- prep\_url\(\) with https:// [\#4135](https://github.com/codeigniter4/CodeIgniter4/pull/4135) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Update phpstan/phpstan requirement from 0.12.65 to 0.12.68 [\#4134](https://github.com/codeigniter4/CodeIgniter4/pull/4134) ([dependabot[bot]](https://github.com/apps/dependabot)) +- set uppercase [\#4132](https://github.com/codeigniter4/CodeIgniter4/pull/4132) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- remove useless "raw html" [\#4131](https://github.com/codeigniter4/CodeIgniter4/pull/4131) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Fix errors/html/error\_exception.php [\#4128](https://github.com/codeigniter4/CodeIgniter4/pull/4128) ([kenjis](https://github.com/kenjis)) +- set note-block for some notes [\#4127](https://github.com/codeigniter4/CodeIgniter4/pull/4127) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Fix `@var` in SessionTestCase. [\#4124](https://github.com/codeigniter4/CodeIgniter4/pull/4124) ([kenjis](https://github.com/kenjis)) +- Fix Session phpdoc [\#4123](https://github.com/codeigniter4/CodeIgniter4/pull/4123) ([kenjis](https://github.com/kenjis)) +- Refactor Generators [\#4121](https://github.com/codeigniter4/CodeIgniter4/pull/4121) ([mostafakhudair](https://github.com/mostafakhudair)) +- Fix few typos. [\#4119](https://github.com/codeigniter4/CodeIgniter4/pull/4119) ([npwsamarasinghe](https://github.com/npwsamarasinghe)) +- Precise exception trace [\#4118](https://github.com/codeigniter4/CodeIgniter4/pull/4118) ([paulbalandan](https://github.com/paulbalandan)) +- Wrap extract calls in IIFEs in View [\#4113](https://github.com/codeigniter4/CodeIgniter4/pull/4113) ([paulbalandan](https://github.com/paulbalandan)) +- Fix Request::withMethod\(\) [\#4112](https://github.com/codeigniter4/CodeIgniter4/pull/4112) ([paulbalandan](https://github.com/paulbalandan)) +- Cache remember [\#4107](https://github.com/codeigniter4/CodeIgniter4/pull/4107) ([agungsugiarto](https://github.com/agungsugiarto)) +- docs: change sample code of redirect\(\) to be more common [\#4106](https://github.com/codeigniter4/CodeIgniter4/pull/4106) ([kenjis](https://github.com/kenjis)) +- Add Cache File mode [\#4103](https://github.com/codeigniter4/CodeIgniter4/pull/4103) ([MGatner](https://github.com/MGatner)) +- Clarify Renderer discrepancy [\#4102](https://github.com/codeigniter4/CodeIgniter4/pull/4102) ([MGatner](https://github.com/MGatner)) +- Catch DateTime failure [\#4097](https://github.com/codeigniter4/CodeIgniter4/pull/4097) ([MGatner](https://github.com/MGatner)) +- Fix URL type. [\#4095](https://github.com/codeigniter4/CodeIgniter4/pull/4095) ([npwsamarasinghe](https://github.com/npwsamarasinghe)) +- Fixed a bug where a newline was treated as a valid value even if it was included at the end. [\#4094](https://github.com/codeigniter4/CodeIgniter4/pull/4094) ([ytetsuro](https://github.com/ytetsuro)) +- BaseModel/Model - Removed $escape from doUpdate [\#4090](https://github.com/codeigniter4/CodeIgniter4/pull/4090) ([najdanovicivan](https://github.com/najdanovicivan)) +- BaseConnection - Added automatic handling of query class for 3rd party drivers [\#4089](https://github.com/codeigniter4/CodeIgniter4/pull/4089) ([najdanovicivan](https://github.com/najdanovicivan)) +- Fix loss of escape value and data in the model [\#4088](https://github.com/codeigniter4/CodeIgniter4/pull/4088) ([michalsn](https://github.com/michalsn)) +- Use getMimeType instead of getClientMimeType. [\#4085](https://github.com/codeigniter4/CodeIgniter4/pull/4085) ([sfadschm](https://github.com/sfadschm)) +- fix codeblock in installing\_composer.rst [\#4083](https://github.com/codeigniter4/CodeIgniter4/pull/4083) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Fixing a bug in Message::isJSON [\#4081](https://github.com/codeigniter4/CodeIgniter4/pull/4081) ([caswell-wc](https://github.com/caswell-wc)) +- Get JSON Variable [\#4080](https://github.com/codeigniter4/CodeIgniter4/pull/4080) ([caswell-wc](https://github.com/caswell-wc)) +- Fix userguide indent [\#4078](https://github.com/codeigniter4/CodeIgniter4/pull/4078) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- DebugToolbar - Handle Query display in Query class [\#4077](https://github.com/codeigniter4/CodeIgniter4/pull/4077) ([najdanovicivan](https://github.com/najdanovicivan)) +- Update userguide indentation [\#4075](https://github.com/codeigniter4/CodeIgniter4/pull/4075) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- docs: fix models indentation [\#4073](https://github.com/codeigniter4/CodeIgniter4/pull/4073) ([kenjis](https://github.com/kenjis)) +- BaseModel/Model - Attempt to rework escape parameter [\#4072](https://github.com/codeigniter4/CodeIgniter4/pull/4072) ([najdanovicivan](https://github.com/najdanovicivan)) +- Model/BaseModel - Fix primary key and add @throws for builder method [\#4071](https://github.com/codeigniter4/CodeIgniter4/pull/4071) ([najdanovicivan](https://github.com/najdanovicivan)) +- Fix DOMParser rules to search also outside the body tag [\#4070](https://github.com/codeigniter4/CodeIgniter4/pull/4070) ([michalsn](https://github.com/michalsn)) +- Warn users on system messages being for internal use [\#4068](https://github.com/codeigniter4/CodeIgniter4/pull/4068) ([paulbalandan](https://github.com/paulbalandan)) +- Remove discussion on LoggerAwareTrait [\#4067](https://github.com/codeigniter4/CodeIgniter4/pull/4067) ([paulbalandan](https://github.com/paulbalandan)) +- PHPStan Ignore File [\#4065](https://github.com/codeigniter4/CodeIgniter4/pull/4065) ([MGatner](https://github.com/MGatner)) +- site\_url tests [\#4063](https://github.com/codeigniter4/CodeIgniter4/pull/4063) ([MGatner](https://github.com/MGatner)) +- Use full table name with schema for SQLSRV [\#4058](https://github.com/codeigniter4/CodeIgniter4/pull/4058) ([michalsn](https://github.com/michalsn)) +- fix userguide config path [\#4057](https://github.com/codeigniter4/CodeIgniter4/pull/4057) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Solidate mime type guessing. [\#4056](https://github.com/codeigniter4/CodeIgniter4/pull/4056) ([sfadschm](https://github.com/sfadschm)) +- Add mime type for rar files [\#4054](https://github.com/codeigniter4/CodeIgniter4/pull/4054) ([michalsn](https://github.com/michalsn)) +- Fix for deleting session flash data in php8 [\#4053](https://github.com/codeigniter4/CodeIgniter4/pull/4053) ([michalsn](https://github.com/michalsn)) +- Added omitted function parameter description. [\#4052](https://github.com/codeigniter4/CodeIgniter4/pull/4052) ([francis94c](https://github.com/francis94c)) +- New Year 2021 [\#4051](https://github.com/codeigniter4/CodeIgniter4/pull/4051) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Fix countAll\(\) docs [\#4050](https://github.com/codeigniter4/CodeIgniter4/pull/4050) ([kenjis](https://github.com/kenjis)) +- adds BaseResult::getNumRows\(\). adds getNumRows to various DBMS Result classes [\#4049](https://github.com/codeigniter4/CodeIgniter4/pull/4049) ([sneakyimp](https://github.com/sneakyimp)) +- \[UG\] a comma after use: i.e., and e.g., [\#4042](https://github.com/codeigniter4/CodeIgniter4/pull/4042) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Fix database test case test [\#4041](https://github.com/codeigniter4/CodeIgniter4/pull/4041) ([kenjis](https://github.com/kenjis)) +- Add initDriver Method [\#4040](https://github.com/codeigniter4/CodeIgniter4/pull/4040) ([mostafakhudair](https://github.com/mostafakhudair)) +- docs: fix general indentation [\#4039](https://github.com/codeigniter4/CodeIgniter4/pull/4039) ([kenjis](https://github.com/kenjis)) +- fix codeblock [\#4037](https://github.com/codeigniter4/CodeIgniter4/pull/4037) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Remove 'tests' [\#4034](https://github.com/codeigniter4/CodeIgniter4/pull/4034) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Alias Forge with use operator [\#4032](https://github.com/codeigniter4/CodeIgniter4/pull/4032) ([mostafakhudair](https://github.com/mostafakhudair)) +- New line for License copyright [\#4029](https://github.com/codeigniter4/CodeIgniter4/pull/4029) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Rename Sqlsrv driver [\#4023](https://github.com/codeigniter4/CodeIgniter4/pull/4023) ([mostafakhudair](https://github.com/mostafakhudair)) +- PHP 8 Actions [\#4012](https://github.com/codeigniter4/CodeIgniter4/pull/4012) ([MGatner](https://github.com/MGatner)) +- feat: make migration/seed settings flexible on database testing [\#3993](https://github.com/codeigniter4/CodeIgniter4/pull/3993) ([kenjis](https://github.com/kenjis)) +- Deprecate redundant HTTP keys [\#3973](https://github.com/codeigniter4/CodeIgniter4/pull/3973) ([paulbalandan](https://github.com/paulbalandan)) +- Replace Core Services [\#3943](https://github.com/codeigniter4/CodeIgniter4/pull/3943) ([MGatner](https://github.com/MGatner)) +- Handling requests sent back from filters [\#3900](https://github.com/codeigniter4/CodeIgniter4/pull/3900) ([caswell-wc](https://github.com/caswell-wc)) +- DX: Split Model testing into several subunits [\#3891](https://github.com/codeigniter4/CodeIgniter4/pull/3891) ([paulbalandan](https://github.com/paulbalandan)) + +## [v4.0.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.4) (2020-07-15) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.3...v4.0.4) + +**Implemented enhancements:** + +- Bug: incorrect type - system/Database/MYSQLi/Connection.php [\#2996](https://github.com/codeigniter4/CodeIgniter4/issues/2996) +- Bug: CI\_DEBUG incorrect type and fails strict validation [\#2975](https://github.com/codeigniter4/CodeIgniter4/issues/2975) + +**Fixed bugs:** + +- Bug: Fix protocol version for DownloadResponse [\#3320](https://github.com/codeigniter4/CodeIgniter4/pull/3320) +- Bug: Add Honeypot::$container to env file [\#3318](https://github.com/codeigniter4/CodeIgniter4/pull/3318) +- Bug: Add multibyte support on DOMParser::see() [\#3324](https://github.com/codeigniter4/CodeIgniter4/pull/3324) +- Bug: Image width debug-bar has conflict [\#3323](https://github.com/codeigniter4/CodeIgniter4/pull/3323) +- Bug: Setting Encryption Service Key in app/Config/Encyption.php [\#3297](https://github.com/codeigniter4/CodeIgniter4/issues/3297) +- Bug: CodeIgniter\I18n -\> Class 'Locale' not found [\#3294](https://github.com/codeigniter4/CodeIgniter4/issues/3294) +- bug cURL - Parse header and Body [\#3261](https://github.com/codeigniter4/CodeIgniter4/issues/3261) +- Bug: Forced HTTPS wrong redirect [\#3260](https://github.com/codeigniter4/CodeIgniter4/issues/3260) +- Bug: [\#3255](https://github.com/codeigniter4/CodeIgniter4/issues/3255) +- Testing ajax header problems [\#3246](https://github.com/codeigniter4/CodeIgniter4/issues/3246) +- Bug: Url's not working [\#3245](https://github.com/codeigniter4/CodeIgniter4/issues/3245) +- Missing code formatting for migration block in documentation [\#3243](https://github.com/codeigniter4/CodeIgniter4/issues/3243) +- Hint message in tutorial does not correspond to the example code shown [\#3242](https://github.com/codeigniter4/CodeIgniter4/issues/3242) +- How to call resource route [\#3239](https://github.com/codeigniter4/CodeIgniter4/issues/3239) +- Can't successfully validate CLI::prompt when field param has periods [\#3233](https://github.com/codeigniter4/CodeIgniter4/issues/3233) +- Bug: set\_checkbox, set\_radio not working default true. [\#3228](https://github.com/codeigniter4/CodeIgniter4/issues/3228) +- Bug: Validation wrong behaviour - Session mixed with internal validator state [\#3210](https://github.com/codeigniter4/CodeIgniter4/issues/3210) +- Bug: CLI: Smell on reading parameters [\#3205](https://github.com/codeigniter4/CodeIgniter4/issues/3205) +- Bug: MySQL Errors are not reported [\#3204](https://github.com/codeigniter4/CodeIgniter4/issues/3204) +- Bug: Error exception layout when its an error inside a view where code can go outside the limits [\#3199](https://github.com/codeigniter4/CodeIgniter4/issues/3199) +- Bug: MessageFormatter argType of "String" isn't valid [\#3191](https://github.com/codeigniter4/CodeIgniter4/issues/3191) +- Bug: pagination not working with segment [\#3188](https://github.com/codeigniter4/CodeIgniter4/issues/3188) +- Bug: is\_image validation is vulnerable [\#3184](https://github.com/codeigniter4/CodeIgniter4/issues/3184) +- Bug: model-\>save\(\) insert/update data [\#3177](https://github.com/codeigniter4/CodeIgniter4/issues/3177) +- Bug: The CLI::color cannot create multicolored strings if ordering of strings is reversed [\#3173](https://github.com/codeigniter4/CodeIgniter4/issues/3173) +- Bug: Missing function locale\_set\_default\(...\) [\#3171](https://github.com/codeigniter4/CodeIgniter4/issues/3171) +- Router RegEx not working. [\#3169](https://github.com/codeigniter4/CodeIgniter4/issues/3169) +- Bug: BaseBuilder::insertBatch\(\) has 3 params and not 4 [\#3158](https://github.com/codeigniter4/CodeIgniter4/issues/3158) +- Bug: Issue using image library when original image and resized are the same size [\#3146](https://github.com/codeigniter4/CodeIgniter4/issues/3146) +- Bug: cannot call constructor on controller. [\#3145](https://github.com/codeigniter4/CodeIgniter4/issues/3145) +- Bug: CodeIgniter 4.0.3 Controller will not display pdf files in browser [\#3144](https://github.com/codeigniter4/CodeIgniter4/issues/3144) +- Bug: $request-\>getVar\('test', FILTER\_VALIDATE\_INT\) does not work if the input is an array [\#3128](https://github.com/codeigniter4/CodeIgniter4/issues/3128) +- Bug: A wrong escape on BaseBuilder::set\(\) [\#3127](https://github.com/codeigniter4/CodeIgniter4/issues/3127) +- Bug: Can't override Translations Package Files / Keys [\#3125](https://github.com/codeigniter4/CodeIgniter4/issues/3125) +- Bug: Validation rules max\_size [\#3122](https://github.com/codeigniter4/CodeIgniter4/issues/3122) +- Bug: Pagination not working with soft deleted items [\#3121](https://github.com/codeigniter4/CodeIgniter4/issues/3121) +- Bug: SQLite3 database file created in /public folder [\#3113](https://github.com/codeigniter4/CodeIgniter4/issues/3113) +- Bug: RedisHandler does not working on sessionExpiration is zero [\#3111](https://github.com/codeigniter4/CodeIgniter4/issues/3111) +- Bug: Implicit controller methods failing [\#3105](https://github.com/codeigniter4/CodeIgniter4/issues/3105) +- Bug: Custom Validation Error Messages [\#3097](https://github.com/codeigniter4/CodeIgniter4/issues/3097) +- Bug: API\ResponseTrait::respondNoContent return content-type header [\#3087](https://github.com/codeigniter4/CodeIgniter4/issues/3087) +- Bug: Running Feature Tests on multiple endpoints using filters [\#3085](https://github.com/codeigniter4/CodeIgniter4/issues/3085) +- Bug: FeatureResponse::isOk and redirects [\#3072](https://github.com/codeigniter4/CodeIgniter4/issues/3072) +- Documentation: "First Application" form validating before submitting [\#3071](https://github.com/codeigniter4/CodeIgniter4/issues/3071) +- Bug: Fatal error: Cannot declare class CodeIgniter\Exceptions\PageNotFoundException [\#3067](https://github.com/codeigniter4/CodeIgniter4/issues/3067) +- Bug: Risky feature tests with Controller returns [\#3063](https://github.com/codeigniter4/CodeIgniter4/issues/3063) +- Bug: Documentation news app project [\#3054](https://github.com/codeigniter4/CodeIgniter4/issues/3054) +- Bug: ClassMethod [\#3050](https://github.com/codeigniter4/CodeIgniter4/issues/3050) +- Bug: CodeIgniter\Router\RouteCollection-\>fillRouteParams\(\) ErrorException on 'from' parameter containing regex pattern with '|' symbol [\#3048](https://github.com/codeigniter4/CodeIgniter4/issues/3048) +- Bug: Database connection [\#3043](https://github.com/codeigniter4/CodeIgniter4/issues/3043) +- Bug: Route redirection not working [\#3041](https://github.com/codeigniter4/CodeIgniter4/issues/3041) +- Bug: Model::getValidationRules\(\) cant handle ::$validationRules if its a string [\#3039](https://github.com/codeigniter4/CodeIgniter4/issues/3039) +- Bug: Body data from curlrequest become unreadable when endpoint return long data [\#3034](https://github.com/codeigniter4/CodeIgniter4/issues/3034) +- Bug: File Validation not validate correctly. [\#3032](https://github.com/codeigniter4/CodeIgniter4/issues/3032) +- Bug: Fatal error when no user-agent is available [\#3029](https://github.com/codeigniter4/CodeIgniter4/issues/3029) +- Bug: Not possible manipulate results of renderSection\(\) on View Layouts [\#3028](https://github.com/codeigniter4/CodeIgniter4/issues/3028) +- Bug: Helpers at non-default locations are not found [\#3026](https://github.com/codeigniter4/CodeIgniter4/issues/3026) +- Bug: Most validation rules enforce requirement of the field [\#3025](https://github.com/codeigniter4/CodeIgniter4/issues/3025) +- Bug: [\#3021](https://github.com/codeigniter4/CodeIgniter4/issues/3021) +- Bug: getPostGet\($index\) return $\_POST if there is no $index in post and get [\#3020](https://github.com/codeigniter4/CodeIgniter4/issues/3020) +- Bug: Multiple File Uploads validation rule "uploaded\[inputName\]" does not work when the input name is an Array [\#3018](https://github.com/codeigniter4/CodeIgniter4/issues/3018) +- Bug: delete\_files\(\) and hidden directories [\#3015](https://github.com/codeigniter4/CodeIgniter4/issues/3015) +- Bug: Model::Insert\(\) does not throw exception when object without data is passed as parameter [\#2998](https://github.com/codeigniter4/CodeIgniter4/issues/2998) +- Bug: Force download [\#2995](https://github.com/codeigniter4/CodeIgniter4/issues/2995) +- Bug: The example of "The Test Class“ is not working in Documets [\#2993](https://github.com/codeigniter4/CodeIgniter4/issues/2993) +- Bug: Difference in behaviour of native DateTime::setTimezone\(\) and CI's Time:setTimezone\(\) [\#2989](https://github.com/codeigniter4/CodeIgniter4/issues/2989) +- Bug: Blank Page on Production Server [\#2980](https://github.com/codeigniter4/CodeIgniter4/issues/2980) +- Bug: Mistake in uri\_string\(\) documentation [\#2972](https://github.com/codeigniter4/CodeIgniter4/issues/2972) +- Bug: route\_to\(\) does not return relative part of the path [\#2971](https://github.com/codeigniter4/CodeIgniter4/issues/2971) +- Bug: Encrypter-\>decrypt issue? [\#2970](https://github.com/codeigniter4/CodeIgniter4/issues/2970) +- Bug: form\_upload second parameter VALUE is not used in source code [\#2967](https://github.com/codeigniter4/CodeIgniter4/issues/2967) +- Bug: There's no way to change default spark serve port using code [\#2966](https://github.com/codeigniter4/CodeIgniter4/issues/2966) +- Bug: 'method' is an empty string in PATH\_INFO causes Unhandled Exception [\#2965](https://github.com/codeigniter4/CodeIgniter4/issues/2965) +- Bug: URI class - working with segments is really strange [\#2962](https://github.com/codeigniter4/CodeIgniter4/issues/2962) +- Bug: Documentation Issue [\#2960](https://github.com/codeigniter4/CodeIgniter4/issues/2960) +- Query Builder set update not working correctly [\#2959](https://github.com/codeigniter4/CodeIgniter4/issues/2959) +- Bug: Validation permit\_empty does no work together with required\_with and required\_without [\#2953](https://github.com/codeigniter4/CodeIgniter4/issues/2953) +- Bug: Validation tries to validate inputs even for empty values with no `required` parameter [\#2951](https://github.com/codeigniter4/CodeIgniter4/issues/2951) +- Bug: Request URI segment is our of range CI 4.0.3 [\#2949](https://github.com/codeigniter4/CodeIgniter4/issues/2949) +- Bug: start migration in manual mode [\#2942](https://github.com/codeigniter4/CodeIgniter4/issues/2942) +- valid\_url rule from validation not working correctly same as valid\_url|required [\#2941](https://github.com/codeigniter4/CodeIgniter4/issues/2941) +- redirect route when route is more complex [\#2937](https://github.com/codeigniter4/CodeIgniter4/issues/2937) +- set\_value does not work with NULL as second parameter [\#2935](https://github.com/codeigniter4/CodeIgniter4/issues/2935) +- Bug: init of $data arrays in controllers [\#2933](https://github.com/codeigniter4/CodeIgniter4/issues/2933) +- Bug: Translation key separated by dot [\#2932](https://github.com/codeigniter4/CodeIgniter4/issues/2932) +- Bug: Model Instantiation [\#2924](https://github.com/codeigniter4/CodeIgniter4/issues/2924) +- Model [\#2923](https://github.com/codeigniter4/CodeIgniter4/issues/2923) +- Bug: CURLRequest baseURI option in user guide [\#2922](https://github.com/codeigniter4/CodeIgniter4/issues/2922) +- Bug: Not bug, but Fix Documentation, please [\#2920](https://github.com/codeigniter4/CodeIgniter4/issues/2920) +- Bug: Postgresql API call delete use -\>connID-\>affected\_rows after Model::delete\(\) got error [\#2918](https://github.com/codeigniter4/CodeIgniter4/issues/2918) +- Multiple table query Model first [\#2885](https://github.com/codeigniter4/CodeIgniter4/issues/2885) +- Bug: pager "prev" and "next" links pointing to wrong URIs [\#2881](https://github.com/codeigniter4/CodeIgniter4/issues/2881) +- Bug: Automatic no CLI colors for Windows terminals [\#2849](https://github.com/codeigniter4/CodeIgniter4/issues/2849) +- Bug: $format in ResourceController is ignored [\#2828](https://github.com/codeigniter4/CodeIgniter4/issues/2828) +- Bug: "Type is not supported" in Postgresql POST restful [\#2812](https://github.com/codeigniter4/CodeIgniter4/issues/2812) +- Bug: Cookie Helper and Response class issue [\#2783](https://github.com/codeigniter4/CodeIgniter4/issues/2783) +- Bug: Models, useSoftDeletes not found in findAll [\#2658](https://github.com/codeigniter4/CodeIgniter4/issues/2658) +- Feature: About the SameSite COOKIE RFC changes for PHP \> 7.3 [\#2374](https://github.com/codeigniter4/CodeIgniter4/issues/2374) + +**Closed issues:** + +- Language folders inside locale folders [\#3300](https://github.com/codeigniter4/CodeIgniter4/issues/3300) +- Encryption Class - Decrypting: authentication failed [\#3258](https://github.com/codeigniter4/CodeIgniter4/issues/3258) +- form\_upload second parameter VALUE was not used in source code, i found a way to do it. [\#3256](https://github.com/codeigniter4/CodeIgniter4/issues/3256) +- erorr line in resize image using fit in visual studio code [\#3249](https://github.com/codeigniter4/CodeIgniter4/issues/3249) +- Testing withSession\(\) generates an error. [\#3190](https://github.com/codeigniter4/CodeIgniter4/issues/3190) +- Why is getGetPost\(\) returning all data instead of null when index not found? [\#3187](https://github.com/codeigniter4/CodeIgniter4/issues/3187) +- Request setGlobal not works [\#3186](https://github.com/codeigniter4/CodeIgniter4/issues/3186) +- Cannot extend core HTTPException class [\#3178](https://github.com/codeigniter4/CodeIgniter4/issues/3178) +- Add this relationship in model. [\#3170](https://github.com/codeigniter4/CodeIgniter4/issues/3170) +- Execute bootstrapEnvironment\(\) & detectEnvironment\(\) before Services::exceptions\(\) [\#3138](https://github.com/codeigniter4/CodeIgniter4/issues/3138) +- Bag pars array to xml [\#3092](https://github.com/codeigniter4/CodeIgniter4/issues/3092) +- utf-8 slug character doesn't work in url [\#3089](https://github.com/codeigniter4/CodeIgniter4/issues/3089) +- Grammar in comment [\#3064](https://github.com/codeigniter4/CodeIgniter4/issues/3064) +- line 374 in system/Entity.php json\_encode add JSON\_UNESCAPED\_UNICODE [\#3059](https://github.com/codeigniter4/CodeIgniter4/issues/3059) +- Link to User Guide on README.md [\#3053](https://github.com/codeigniter4/CodeIgniter4/issues/3053) +- How to set private properties in CodeIgniter\Database\BaseResult::getCustomResultObject\(\) [\#3051](https://github.com/codeigniter4/CodeIgniter4/issues/3051) +- url\_title not detecting some special characteres [\#3038](https://github.com/codeigniter4/CodeIgniter4/issues/3038) +- Error: No input file specified. [\#3030](https://github.com/codeigniter4/CodeIgniter4/issues/3030) +- Can findAll function in modeling data accept 3rd parameter reset [\#3024](https://github.com/codeigniter4/CodeIgniter4/issues/3024) +- Class 'App\Models\UserModel' not found [\#3014](https://github.com/codeigniter4/CodeIgniter4/issues/3014) +- Image reorient with exif [\#3006](https://github.com/codeigniter4/CodeIgniter4/issues/3006) +- set cookieHTTPOnly not work [\#2999](https://github.com/codeigniter4/CodeIgniter4/issues/2999) +- \[Feature request\] Slugs Link [\#2988](https://github.com/codeigniter4/CodeIgniter4/issues/2988) +- \[Feature request\] End processing app [\#2982](https://github.com/codeigniter4/CodeIgniter4/issues/2982) +- Pager should have getTotalResults method function [\#2954](https://github.com/codeigniter4/CodeIgniter4/issues/2954) +- URL rewrite problem [\#2925](https://github.com/codeigniter4/CodeIgniter4/issues/2925) +- Translation file not get correct locale in 4.0.3 [\#2921](https://github.com/codeigniter4/CodeIgniter4/issues/2921) +- Why this route does not work ? [\#2919](https://github.com/codeigniter4/CodeIgniter4/issues/2919) +- Cache redis or memcached [\#2909](https://github.com/codeigniter4/CodeIgniter4/issues/2909) +- Cookie helper not woking [\#2848](https://github.com/codeigniter4/CodeIgniter4/issues/2848) +- Bug: Routing group filter not working on nested or complex routes [\#2390](https://github.com/codeigniter4/CodeIgniter4/issues/2390) + +**Merged pull requests:** + +- Changelog update [\#3322](https://github.com/codeigniter4/CodeIgniter4/pull/3322) ([michalsn](https://github.com/michalsn)) +- Changelog for 4.0.4 update [\#3321](https://github.com/codeigniter4/CodeIgniter4/pull/3321) ([michalsn](https://github.com/michalsn)) +- Fix protocol version for DownloadResponse [\#3320](https://github.com/codeigniter4/CodeIgniter4/pull/3320) ([michalsn](https://github.com/michalsn)) +- Add Honeypot::$container to env file [\#3318](https://github.com/codeigniter4/CodeIgniter4/pull/3318) ([paulbalandan](https://github.com/paulbalandan)) +- Pass filter arguments to after\(\) and before\(\) methods [\#3316](https://github.com/codeigniter4/CodeIgniter4/pull/3316) ([tangix](https://github.com/tangix)) +- count on \Config\Services [\#3308](https://github.com/codeigniter4/CodeIgniter4/pull/3308) ([mostafakhudair](https://github.com/mostafakhudair)) +- Add hex2bin prefix handling for encryption key [\#3307](https://github.com/codeigniter4/CodeIgniter4/pull/3307) ([michalsn](https://github.com/michalsn)) +- add break; in foreach at Time::getDst\(\) when daylightSaving set [\#3305](https://github.com/codeigniter4/CodeIgniter4/pull/3305) ([samsonasik](https://github.com/samsonasik)) +- New command: cache:clear [\#3304](https://github.com/codeigniter4/CodeIgniter4/pull/3304) ([lonnieezell](https://github.com/lonnieezell)) +- force\_https didn't force https [\#3302](https://github.com/codeigniter4/CodeIgniter4/pull/3302) ([colethorsen](https://github.com/colethorsen)) +- add test for CommandRunner::\_remap\(\) with empty first params [\#3301](https://github.com/codeigniter4/CodeIgniter4/pull/3301) ([samsonasik](https://github.com/samsonasik)) +- FieldData -\> add typeName field and length field [\#3299](https://github.com/codeigniter4/CodeIgniter4/pull/3299) ([devorama](https://github.com/devorama)) +- reduce repetitive getDefaultNamespace\(\) and controllerName\(\) function call in Router [\#3298](https://github.com/codeigniter4/CodeIgniter4/pull/3298) ([samsonasik](https://github.com/samsonasik)) +- Fix PHPDocs for Filters [\#3296](https://github.com/codeigniter4/CodeIgniter4/pull/3296) ([paulbalandan](https://github.com/paulbalandan)) +- Fix PHPDocs for HTTP [\#3295](https://github.com/codeigniter4/CodeIgniter4/pull/3295) ([paulbalandan](https://github.com/paulbalandan)) +- Update phpdoc.dist.xml [\#3293](https://github.com/codeigniter4/CodeIgniter4/pull/3293) ([paulbalandan](https://github.com/paulbalandan)) +- Fix for force\_https\(\) function [\#3292](https://github.com/codeigniter4/CodeIgniter4/pull/3292) ([michalsn](https://github.com/michalsn)) +- Fix PHPDocs for I18n [\#3291](https://github.com/codeigniter4/CodeIgniter4/pull/3291) ([paulbalandan](https://github.com/paulbalandan)) +- Fix PHPDocs for Router [\#3290](https://github.com/codeigniter4/CodeIgniter4/pull/3290) ([paulbalandan](https://github.com/paulbalandan)) +- Fix PHPDocs for CLI [\#3289](https://github.com/codeigniter4/CodeIgniter4/pull/3289) ([paulbalandan](https://github.com/paulbalandan)) +- add JSON\_NUMERIC\_CHECK to json encode options [\#3288](https://github.com/codeigniter4/CodeIgniter4/pull/3288) ([devorama](https://github.com/devorama)) +- typo fix s/Memcached/Redis in RedisHandler cache [\#3285](https://github.com/codeigniter4/CodeIgniter4/pull/3285) ([samsonasik](https://github.com/samsonasik)) +- optimize Session : use foreach instead of for with count when possible [\#3284](https://github.com/codeigniter4/CodeIgniter4/pull/3284) ([samsonasik](https://github.com/samsonasik)) +- using strpos instead of substr when possible [\#3283](https://github.com/codeigniter4/CodeIgniter4/pull/3283) ([samsonasik](https://github.com/samsonasik)) +- optimize Database BaseBuilder : use foreach instead of for with count when possible [\#3282](https://github.com/codeigniter4/CodeIgniter4/pull/3282) ([samsonasik](https://github.com/samsonasik)) +- optimize RouteCollection : use foreach instead of for with count when possible [\#3281](https://github.com/codeigniter4/CodeIgniter4/pull/3281) ([samsonasik](https://github.com/samsonasik)) +- optimize FileLocator autoloader : use foreach instead of for with count when possible [\#3280](https://github.com/codeigniter4/CodeIgniter4/pull/3280) ([samsonasik](https://github.com/samsonasik)) +- Fix "100 Continue" header handling in CURLRequest class [\#3274](https://github.com/codeigniter4/CodeIgniter4/pull/3274) ([michalsn](https://github.com/michalsn)) +- cs fix in Session class [\#3272](https://github.com/codeigniter4/CodeIgniter4/pull/3272) ([samsonasik](https://github.com/samsonasik)) +- Throttler code style update [\#3271](https://github.com/codeigniter4/CodeIgniter4/pull/3271) ([michalsn](https://github.com/michalsn)) +- cs : remove unused import use statements and sort use statements [\#3270](https://github.com/codeigniter4/CodeIgniter4/pull/3270) ([samsonasik](https://github.com/samsonasik)) +- Add more URI class tests to fully illustrate current behavior [\#3269](https://github.com/codeigniter4/CodeIgniter4/pull/3269) ([michalsn](https://github.com/michalsn)) +- Fix Image::save\(\) when target value is null [\#3268](https://github.com/codeigniter4/CodeIgniter4/pull/3268) ([michalsn](https://github.com/michalsn)) +- Use named variable in honeypot container [\#3267](https://github.com/codeigniter4/CodeIgniter4/pull/3267) ([michalsn](https://github.com/michalsn)) +- Check server headers via Request class [\#3265](https://github.com/codeigniter4/CodeIgniter4/pull/3265) ([michalsn](https://github.com/michalsn)) +- PHPUnit-annotate untestable code in CLI [\#3264](https://github.com/codeigniter4/CodeIgniter4/pull/3264) ([paulbalandan](https://github.com/paulbalandan)) +- Update phpdocs of Cache library [\#3263](https://github.com/codeigniter4/CodeIgniter4/pull/3263) ([paulbalandan](https://github.com/paulbalandan)) +- Update htaccess [\#3262](https://github.com/codeigniter4/CodeIgniter4/pull/3262) ([paulbalandan](https://github.com/paulbalandan)) +- Fixes \#3125 : add ability to override existing translation en in system language from App [\#3254](https://github.com/codeigniter4/CodeIgniter4/pull/3254) ([samsonasik](https://github.com/samsonasik)) +- Add Fabricator model error [\#3253](https://github.com/codeigniter4/CodeIgniter4/pull/3253) ([MGatner](https://github.com/MGatner)) +- Implement model callback overrides [\#3252](https://github.com/codeigniter4/CodeIgniter4/pull/3252) ([MGatner](https://github.com/MGatner)) +- Fix PHPDocBlock of Images library [\#3250](https://github.com/codeigniter4/CodeIgniter4/pull/3250) ([paulbalandan](https://github.com/paulbalandan)) +- Update html\_helper.rst [\#3248](https://github.com/codeigniter4/CodeIgniter4/pull/3248) ([avegacms](https://github.com/avegacms)) +- Update html\_helper.php [\#3247](https://github.com/codeigniter4/CodeIgniter4/pull/3247) ([avegacms](https://github.com/avegacms)) +- Syntax fix for migration.rst [\#3244](https://github.com/codeigniter4/CodeIgniter4/pull/3244) ([paulbalandan](https://github.com/paulbalandan)) +- Run apidocs action only when system files are changed [\#3241](https://github.com/codeigniter4/CodeIgniter4/pull/3241) ([paulbalandan](https://github.com/paulbalandan)) +- Fix formatting issue in userguide/outgoing/view\_parser [\#3240](https://github.com/codeigniter4/CodeIgniter4/pull/3240) ([Connum](https://github.com/Connum)) +- Add ability to call commands programatically. [\#3238](https://github.com/codeigniter4/CodeIgniter4/pull/3238) ([lonnieezell](https://github.com/lonnieezell)) +- Reset Filters between feature tests [\#3237](https://github.com/codeigniter4/CodeIgniter4/pull/3237) ([MGatner](https://github.com/MGatner)) +- Fix CLI::validate\(\) usage when using dot sign [\#3236](https://github.com/codeigniter4/CodeIgniter4/pull/3236) ([michalsn](https://github.com/michalsn)) +- Remove cilexer from gitignore [\#3235](https://github.com/codeigniter4/CodeIgniter4/pull/3235) ([paulbalandan](https://github.com/paulbalandan)) +- Feature seed command [\#3234](https://github.com/codeigniter4/CodeIgniter4/pull/3234) ([abilioposada](https://github.com/abilioposada)) +- more test Files\File class [\#3232](https://github.com/codeigniter4/CodeIgniter4/pull/3232) ([samsonasik](https://github.com/samsonasik)) +- more tests on Autoloader\FileLocator [\#3231](https://github.com/codeigniter4/CodeIgniter4/pull/3231) ([samsonasik](https://github.com/samsonasik)) +- Fix set\_checkbox\(\) and set\_radio\(\) when default is set to true [\#3229](https://github.com/codeigniter4/CodeIgniter4/pull/3229) ([michalsn](https://github.com/michalsn)) +- Use Throwable in catch block of filesystem helper [\#3227](https://github.com/codeigniter4/CodeIgniter4/pull/3227) ([paulbalandan](https://github.com/paulbalandan)) +- Check color support for STDERR for CLI::error [\#3226](https://github.com/codeigniter4/CodeIgniter4/pull/3226) ([paulbalandan](https://github.com/paulbalandan)) +- Fix 'exit code 23' in apidocs action [\#3225](https://github.com/codeigniter4/CodeIgniter4/pull/3225) ([paulbalandan](https://github.com/paulbalandan)) +- Fix result object handling in Model class [\#3224](https://github.com/codeigniter4/CodeIgniter4/pull/3224) ([michalsn](https://github.com/michalsn)) +- Fix update query to return false on error [\#3223](https://github.com/codeigniter4/CodeIgniter4/pull/3223) ([michalsn](https://github.com/michalsn)) +- Fix insert Entity object on postgres [\#3222](https://github.com/codeigniter4/CodeIgniter4/pull/3222) ([MashinaMashina](https://github.com/MashinaMashina)) +- DatabaseTestCase migrations [\#3221](https://github.com/codeigniter4/CodeIgniter4/pull/3221) ([MGatner](https://github.com/MGatner)) +- Allow ignoring \*.db inside folders [\#3220](https://github.com/codeigniter4/CodeIgniter4/pull/3220) ([paulbalandan](https://github.com/paulbalandan)) +- SQLite3 escapeChar fix [\#3219](https://github.com/codeigniter4/CodeIgniter4/pull/3219) ([michalsn](https://github.com/michalsn)) +- Remove underscore in UG build path [\#3218](https://github.com/codeigniter4/CodeIgniter4/pull/3218) ([paulbalandan](https://github.com/paulbalandan)) +- add test for Filters with empty except [\#3215](https://github.com/codeigniter4/CodeIgniter4/pull/3215) ([samsonasik](https://github.com/samsonasik)) +- remove unnecessary foreach in RouteCollection::checkSubdomains\(\) [\#3214](https://github.com/codeigniter4/CodeIgniter4/pull/3214) ([samsonasik](https://github.com/samsonasik)) +- Add Fabricator counts [\#3213](https://github.com/codeigniter4/CodeIgniter4/pull/3213) ([MGatner](https://github.com/MGatner)) +- Cast currentPage value to integer in Pager class [\#3209](https://github.com/codeigniter4/CodeIgniter4/pull/3209) ([michalsn](https://github.com/michalsn)) +- Allow dashes in the CLI segment [\#3208](https://github.com/codeigniter4/CodeIgniter4/pull/3208) ([michalsn](https://github.com/michalsn)) +- Fix for getting database error [\#3207](https://github.com/codeigniter4/CodeIgniter4/pull/3207) ([michalsn](https://github.com/michalsn)) +- Fixed \#3199 [\#3203](https://github.com/codeigniter4/CodeIgniter4/pull/3203) ([mpmont](https://github.com/mpmont)) +- Fix extra slash in URI [\#3202](https://github.com/codeigniter4/CodeIgniter4/pull/3202) ([MGatner](https://github.com/MGatner)) +- Update for FabricatorLiveTest [\#3201](https://github.com/codeigniter4/CodeIgniter4/pull/3201) ([michalsn](https://github.com/michalsn)) +- Add closing parenthesis [\#3200](https://github.com/codeigniter4/CodeIgniter4/pull/3200) ([abilioposada](https://github.com/abilioposada)) +- Hide in bootstrap4 [\#3197](https://github.com/codeigniter4/CodeIgniter4/pull/3197) ([Sosko](https://github.com/Sosko)) +- The permissions of the new file should be modified, not the old file [\#3196](https://github.com/codeigniter4/CodeIgniter4/pull/3196) ([wangyupeng](https://github.com/wangyupeng)) +- Fix default value for page in Model::paginate\(\) [\#3194](https://github.com/codeigniter4/CodeIgniter4/pull/3194) ([michalsn](https://github.com/michalsn)) +- Test Case Mocking [\#3193](https://github.com/codeigniter4/CodeIgniter4/pull/3193) ([MGatner](https://github.com/MGatner)) +- ArgType 'String' is not valid in MessageFormatter class \(fixes \#3191\) [\#3192](https://github.com/codeigniter4/CodeIgniter4/pull/3192) ([HughieW](https://github.com/HughieW)) +- Bugfix: Multipart Content-Length [\#3189](https://github.com/codeigniter4/CodeIgniter4/pull/3189) ([MGatner](https://github.com/MGatner)) +- Add apidocs action [\#3183](https://github.com/codeigniter4/CodeIgniter4/pull/3183) ([paulbalandan](https://github.com/paulbalandan)) +- Improve CLI clear screen [\#3182](https://github.com/codeigniter4/CodeIgniter4/pull/3182) ([paulbalandan](https://github.com/paulbalandan)) +- Refactor color detection in CLI [\#3181](https://github.com/codeigniter4/CodeIgniter4/pull/3181) ([paulbalandan](https://github.com/paulbalandan)) +- Use explicit 'PHP\_EOL' in wordwrap [\#3179](https://github.com/codeigniter4/CodeIgniter4/pull/3179) ([paulbalandan](https://github.com/paulbalandan)) +- Add webp support to Image class [\#3176](https://github.com/codeigniter4/CodeIgniter4/pull/3176) ([michalsn](https://github.com/michalsn)) +- Fix for multicolored strings in CLI [\#3175](https://github.com/codeigniter4/CodeIgniter4/pull/3175) ([michalsn](https://github.com/michalsn)) +- Add Unicode support for regular expressions in router [\#3172](https://github.com/codeigniter4/CodeIgniter4/pull/3172) ([michalsn](https://github.com/michalsn)) +- Add the upload artifact action [\#3167](https://github.com/codeigniter4/CodeIgniter4/pull/3167) ([paulbalandan](https://github.com/paulbalandan)) +- More robust color support detection in CLI [\#3165](https://github.com/codeigniter4/CodeIgniter4/pull/3165) ([paulbalandan](https://github.com/paulbalandan)) +- Fix testMode\(\) for batch methods in Model [\#3163](https://github.com/codeigniter4/CodeIgniter4/pull/3163) ([michalsn](https://github.com/michalsn)) +- Update delete\_files\(\) helper function [\#3162](https://github.com/codeigniter4/CodeIgniter4/pull/3162) ([michalsn](https://github.com/michalsn)) +- Refresh Composer files [\#3153](https://github.com/codeigniter4/CodeIgniter4/pull/3153) ([MGatner](https://github.com/MGatner)) +- use writable directory for sqlite default location [\#3151](https://github.com/codeigniter4/CodeIgniter4/pull/3151) ([samsonasik](https://github.com/samsonasik)) +- Add webp support to Image Manipulation Class [\#3084](https://github.com/codeigniter4/CodeIgniter4/pull/3084) ([nicojmb](https://github.com/nicojmb)) +- Bug fix in Throttler class check validation [\#2873](https://github.com/codeigniter4/CodeIgniter4/pull/2873) ([jlamim](https://github.com/jlamim)) + +## [v4.0.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.3) (2020-05-01) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/4.0.2...v4.0.3) + +**Fixed bugs:** + +- Bug: is\_unique validation rule with model-\>save\(\) fails [\#2906](https://github.com/codeigniter4/CodeIgniter4/issues/2906) +- Bug: Debugging toolbar not showing [\#2893](https://github.com/codeigniter4/CodeIgniter4/issues/2893) +- Bug: database query bug [\#2890](https://github.com/codeigniter4/CodeIgniter4/issues/2890) +- Bug: Routes - missing \(:id\) [\#2889](https://github.com/codeigniter4/CodeIgniter4/issues/2889) +- Bug: Typo example "Using Named Routes" [\#2888](https://github.com/codeigniter4/CodeIgniter4/issues/2888) +- Bug: I am able to make POST request to a GET route [\#2880](https://github.com/codeigniter4/CodeIgniter4/issues/2880) +- Bug: Argument 1 passed to CodeIgniter\Database\BaseResult::getFirstRow\(\) must be of the type string, null given, called in /system/Model.php on line 383 [\#2877](https://github.com/codeigniter4/CodeIgniter4/issues/2877) +- Bug: Can't override already defined language in pager [\#2875](https://github.com/codeigniter4/CodeIgniter4/issues/2875) +- Installation Problem: Composer installation halts while installing kint-php/kint [\#2863](https://github.com/codeigniter4/CodeIgniter4/issues/2863) +- Bug: `composer require codeigniter4/translations` without stable version [\#2862](https://github.com/codeigniter4/CodeIgniter4/issues/2862) +- Bug: Method Spoofing Validation [\#2855](https://github.com/codeigniter4/CodeIgniter4/issues/2855) +- Bug: You made it just as impossible to get going as Laravel – BUMMER! [\#2850](https://github.com/codeigniter4/CodeIgniter4/issues/2850) +- Bug: localised validation messages [\#2845](https://github.com/codeigniter4/CodeIgniter4/issues/2845) +- Bug: Commands discovery in custom namespaces [\#2840](https://github.com/codeigniter4/CodeIgniter4/issues/2840) +- Bug: When the $index parameter of getGetPost or getPostGet is null, you will get an error result [\#2839](https://github.com/codeigniter4/CodeIgniter4/issues/2839) +- Bug: delete\_cookie\(\) doesn't work [\#2836](https://github.com/codeigniter4/CodeIgniter4/issues/2836) +- Bug: Model save method not working if PRIMARY KEY is VARCHAR [\#2835](https://github.com/codeigniter4/CodeIgniter4/issues/2835) +- Bug: Alias Filter with Multiple Filter Class Doesn't Work [\#2831](https://github.com/codeigniter4/CodeIgniter4/issues/2831) +- Bug: Kint Config isnt loaded [\#2830](https://github.com/codeigniter4/CodeIgniter4/issues/2830) +- Bug: RouteCollection::resource\(\) doesn't work with grouped rules [\#2829](https://github.com/codeigniter4/CodeIgniter4/issues/2829) +- Bug: $forge Property in Seeder Class Never Initialize [\#2825](https://github.com/codeigniter4/CodeIgniter4/issues/2825) +- Bug: getSegments\(\) returns an array with 2 empty strings when accessing / [\#2822](https://github.com/codeigniter4/CodeIgniter4/issues/2822) +- Bug: Cell Caching in View Cells and SOLVE! [\#2821](https://github.com/codeigniter4/CodeIgniter4/issues/2821) +- Bug: saveData option doesn't work in Views [\#2818](https://github.com/codeigniter4/CodeIgniter4/issues/2818) +- Bug: Validation placeholder not being replaced [\#2817](https://github.com/codeigniter4/CodeIgniter4/issues/2817) +- Bug: Problems with QueryBuilder when run multiple queries one by one [\#2800](https://github.com/codeigniter4/CodeIgniter4/issues/2800) +- Bug: Routing placeholder in "controller"part of route doesn't work [\#2787](https://github.com/codeigniter4/CodeIgniter4/issues/2787) +- Bug: session\(\)-\>push\(\) Strange behavior [\#2786](https://github.com/codeigniter4/CodeIgniter4/issues/2786) +- Bug: php spark serve [\#2784](https://github.com/codeigniter4/CodeIgniter4/issues/2784) +- Bug: Can't paginate query with group by [\#2776](https://github.com/codeigniter4/CodeIgniter4/issues/2776) +- Bug: negotiateLocale bug in Safari with fr-ca locale [\#2774](https://github.com/codeigniter4/CodeIgniter4/issues/2774) +- Bug: Controller in Sub Directory is not working [\#2764](https://github.com/codeigniter4/CodeIgniter4/issues/2764) +- Bug: rename release By "v" [\#2763](https://github.com/codeigniter4/CodeIgniter4/issues/2763) +- Bug: db query '?' bind is not working when use sql with ':=' operator. [\#2762](https://github.com/codeigniter4/CodeIgniter4/issues/2762) +- Bug: Multiple select validation problem [\#2757](https://github.com/codeigniter4/CodeIgniter4/issues/2757) +- Bug: Official Site is not working [\#2749](https://github.com/codeigniter4/CodeIgniter4/issues/2749) +- Bug: Logger context placeholders {file} and {line} are wrong [\#2743](https://github.com/codeigniter4/CodeIgniter4/issues/2743) +- Bug: Decimal validation fails without leading digit [\#2740](https://github.com/codeigniter4/CodeIgniter4/issues/2740) +- Bug: Model insert Created\_at and updated\_at get when new record added [\#2737](https://github.com/codeigniter4/CodeIgniter4/issues/2737) +- Bug: appendHeader 500 error if header does not exist [\#2730](https://github.com/codeigniter4/CodeIgniter4/issues/2730) +- Bug: codeigniter4 download link 404 resource not found [\#2727](https://github.com/codeigniter4/CodeIgniter4/issues/2727) +- Bug: Logger `path` property ignored [\#2725](https://github.com/codeigniter4/CodeIgniter4/issues/2725) +- Bug: $this-\>request-\>getPost\(\) is empty when json is send by postman [\#2720](https://github.com/codeigniter4/CodeIgniter4/issues/2720) +- Bug: open path /0 at uri got error "Class Home does not exist" on development environment [\#2716](https://github.com/codeigniter4/CodeIgniter4/issues/2716) +- Bug: calling countAllResults after find\($id\) produce wrong result [\#2705](https://github.com/codeigniter4/CodeIgniter4/issues/2705) +- Bug: $routes-\>cli\(\) accessible via web browser if autoroute is true [\#2704](https://github.com/codeigniter4/CodeIgniter4/issues/2704) +- Bug: Controllers and Views in subdirectories not working [\#2701](https://github.com/codeigniter4/CodeIgniter4/issues/2701) +- Bug: undefined model method should throw exception [\#2688](https://github.com/codeigniter4/CodeIgniter4/issues/2688) +- Bug: The custom error config of validation is not working [\#2678](https://github.com/codeigniter4/CodeIgniter4/issues/2678) +- Bug: Can't test redirect\(\)-\>route\('routename'\), while redirect\(\)-\>to\('path'\) is working with ControllerTester [\#2676](https://github.com/codeigniter4/CodeIgniter4/issues/2676) +- Bug: php spark migrate:create File -n NameSpace doesn't create migration class under NameSpace/Database/Migrations directory with composer autoload [\#2664](https://github.com/codeigniter4/CodeIgniter4/issues/2664) +- Bug: \I18n\Time object displaying +1 year when object date is set to 2021-12-31 [\#2663](https://github.com/codeigniter4/CodeIgniter4/issues/2663) +- Bug: Route options filter didn't working [\#2654](https://github.com/codeigniter4/CodeIgniter4/issues/2654) +- Bug: Error in Seeder [\#2653](https://github.com/codeigniter4/CodeIgniter4/issues/2653) +- Bug: spark no longer lists function when used by without any parameters [\#2645](https://github.com/codeigniter4/CodeIgniter4/issues/2645) +- Bug: Number Helper, Currency Fraction issue [\#2634](https://github.com/codeigniter4/CodeIgniter4/issues/2634) +- Bug: forceHTTPS method ignores baseURL configuration when redirecting [\#2633](https://github.com/codeigniter4/CodeIgniter4/issues/2633) +- While serving Application on CLI using different port debugbar is still using a default 8080 port [\#2630](https://github.com/codeigniter4/CodeIgniter4/issues/2630) +- Bug: spark migrate -all with appstarter [\#2627](https://github.com/codeigniter4/CodeIgniter4/issues/2627) +- Bug: Problem when compiled vendor as PHAR file [\#2623](https://github.com/codeigniter4/CodeIgniter4/issues/2623) +- Bug: debugbar javascript error [\#2621](https://github.com/codeigniter4/CodeIgniter4/issues/2621) +- Bug: ResourceController json response always empty [\#2617](https://github.com/codeigniter4/CodeIgniter4/issues/2617) +- Bug: Chrome logger does not work. [\#2616](https://github.com/codeigniter4/CodeIgniter4/issues/2616) +- Bug: [\#2608](https://github.com/codeigniter4/CodeIgniter4/issues/2608) +- User Guide is not in HTML in the download file [\#2607](https://github.com/codeigniter4/CodeIgniter4/issues/2607) +- Unnecessary files in the download installation [\#2606](https://github.com/codeigniter4/CodeIgniter4/issues/2606) +- Bug: Class 'Kint\Renderer\Renderer' not found [\#2605](https://github.com/codeigniter4/CodeIgniter4/issues/2605) +- Bug: Codeigniter4/framework composer.json not updated [\#2601](https://github.com/codeigniter4/CodeIgniter4/issues/2601) +- \[Docs\] Loading Environment into Configuration documentation described wrong [\#2554](https://github.com/codeigniter4/CodeIgniter4/issues/2554) +- Bug: Sessions dont work on PostgreSQL [\#2546](https://github.com/codeigniter4/CodeIgniter4/issues/2546) +- Bug: router service adds backslash to controllername if route is configured [\#2520](https://github.com/codeigniter4/CodeIgniter4/issues/2520) +- Bug: JSONFormatter-\>format\(\) cannot handle errordata, only outputs it's own error [\#2434](https://github.com/codeigniter4/CodeIgniter4/issues/2434) +- Bug: HTTP Feature Testing only runs the FIRST test [\#2393](https://github.com/codeigniter4/CodeIgniter4/issues/2393) +- Bug: Spark issue with PHP install location [\#2367](https://github.com/codeigniter4/CodeIgniter4/issues/2367) +- spark route issue [\#2194](https://github.com/codeigniter4/CodeIgniter4/issues/2194) + +**Closed issues:** + +- Modular MVP on CI4 [\#2900](https://github.com/codeigniter4/CodeIgniter4/issues/2900) +- About javascript: void \(0\); [\#2887](https://github.com/codeigniter4/CodeIgniter4/issues/2887) +- Entity returns null when used on the model [\#2838](https://github.com/codeigniter4/CodeIgniter4/issues/2838) +- php spark migrate -g does not work [\#2832](https://github.com/codeigniter4/CodeIgniter4/issues/2832) +- Bug: Namespacing of app/Config folder vs. app/Controller [\#2826](https://github.com/codeigniter4/CodeIgniter4/issues/2826) +- Controller Call to a member function getPost\(\) on null [\#2823](https://github.com/codeigniter4/CodeIgniter4/issues/2823) +- QueryBuilder - Does not support JOIN in UPDATE [\#2799](https://github.com/codeigniter4/CodeIgniter4/issues/2799) +- Database model error when limiting delete\(\) [\#2780](https://github.com/codeigniter4/CodeIgniter4/issues/2780) +- codeigniter4/codeigniter4 package not exists in packagist [\#2753](https://github.com/codeigniter4/CodeIgniter4/issues/2753) +- datamap Entities not works! [\#2747](https://github.com/codeigniter4/CodeIgniter4/issues/2747) +- Error: Call to undefined function CodeIgniter\CLI\mb\_strpos\(\) [\#2746](https://github.com/codeigniter4/CodeIgniter4/issues/2746) +- CodeIgniter\Log\Logger::logPath property is never used. [\#2738](https://github.com/codeigniter4/CodeIgniter4/issues/2738) +- Bug: set\_radio\(\) in Form Helper does not work when radio button value equals "0" [\#2728](https://github.com/codeigniter4/CodeIgniter4/issues/2728) +- Array validation has a problem [\#2714](https://github.com/codeigniter4/CodeIgniter4/issues/2714) +- delete cookie not working [\#2700](https://github.com/codeigniter4/CodeIgniter4/issues/2700) +- remove default language local from url [\#2682](https://github.com/codeigniter4/CodeIgniter4/issues/2682) +- OpenSSLHandler: Encrypt/Decrypt [\#2680](https://github.com/codeigniter4/CodeIgniter4/issues/2680) +- RESTFUL API with CORS problem [\#2667](https://github.com/codeigniter4/CodeIgniter4/issues/2667) +- I guess there's no the third parameter [\#2657](https://github.com/codeigniter4/CodeIgniter4/issues/2657) +- set ci4 repo default branch = master [\#2643](https://github.com/codeigniter4/CodeIgniter4/issues/2643) +- BUG: 4.0.2 Kint not found [\#2639](https://github.com/codeigniter4/CodeIgniter4/issues/2639) +- Feature: Migrate:Rollback/Refresh confirmation in production environment [\#2385](https://github.com/codeigniter4/CodeIgniter4/issues/2385) + +**Merged pull requests:** + +- 4.0.3 release [\#2912](https://github.com/codeigniter4/CodeIgniter4/pull/2912) ([lonnieezell](https://github.com/lonnieezell)) +- url\_title\(\) used CI3 style in user guide [\#2911](https://github.com/codeigniter4/CodeIgniter4/pull/2911) ([jreklund](https://github.com/jreklund)) +- fix undefined class 'CodeIgniter' [\#2910](https://github.com/codeigniter4/CodeIgniter4/pull/2910) ([PingZii](https://github.com/PingZii)) +- Improved subjects in Controller and Routing chapter [\#2908](https://github.com/codeigniter4/CodeIgniter4/pull/2908) ([jreklund](https://github.com/jreklund)) +- Fix Model::first\(\) only use orderBy\(\) when group by is not empty [\#2907](https://github.com/codeigniter4/CodeIgniter4/pull/2907) ([samsonasik](https://github.com/samsonasik)) +- Allow bypassing content negotiation during API responses. [\#2904](https://github.com/codeigniter4/CodeIgniter4/pull/2904) ([lonnieezell](https://github.com/lonnieezell)) +- Ugtweaks [\#2903](https://github.com/codeigniter4/CodeIgniter4/pull/2903) ([lonnieezell](https://github.com/lonnieezell)) +- Carbonads [\#2902](https://github.com/codeigniter4/CodeIgniter4/pull/2902) ([lonnieezell](https://github.com/lonnieezell)) +- Added information about the new features of the Pagination library [\#2901](https://github.com/codeigniter4/CodeIgniter4/pull/2901) ([jlamim](https://github.com/jlamim)) +- New features for pagination [\#2899](https://github.com/codeigniter4/CodeIgniter4/pull/2899) ([jlamim](https://github.com/jlamim)) +- Fixed lang\(\) example in user guide [\#2898](https://github.com/codeigniter4/CodeIgniter4/pull/2898) ([nmolinos](https://github.com/nmolinos)) +- Make validation placeholders always available [\#2897](https://github.com/codeigniter4/CodeIgniter4/pull/2897) ([jreklund](https://github.com/jreklund)) +- \[ci skip\] Add `make.bat` for Windows users [\#2895](https://github.com/codeigniter4/CodeIgniter4/pull/2895) ([paulbalandan](https://github.com/paulbalandan)) +- Added ability to delete row with string primary key via Model::delete\($id\) [\#2894](https://github.com/codeigniter4/CodeIgniter4/pull/2894) ([samsonasik](https://github.com/samsonasik)) +- Update of the pagination template to make the correct use of the locale [\#2892](https://github.com/codeigniter4/CodeIgniter4/pull/2892) ([jlamim](https://github.com/jlamim)) +- \[ci skip\] route placeholders 'id' to 'num' [\#2891](https://github.com/codeigniter4/CodeIgniter4/pull/2891) ([Instrye](https://github.com/Instrye)) +- \[ci skip\] fix warnings on compiling user guide [\#2886](https://github.com/codeigniter4/CodeIgniter4/pull/2886) ([paulbalandan](https://github.com/paulbalandan)) +- Added more Common functions and improved rendering in userguide [\#2884](https://github.com/codeigniter4/CodeIgniter4/pull/2884) ([jreklund](https://github.com/jreklund)) +- Build Your First Application used url\_title incorrectly [\#2883](https://github.com/codeigniter4/CodeIgniter4/pull/2883) ([jreklund](https://github.com/jreklund)) +- \[User guide\] Correcting some details in the part that talks about model and entities [\#2878](https://github.com/codeigniter4/CodeIgniter4/pull/2878) ([jlamim](https://github.com/jlamim)) +- Shifted basic URI Routing examples down [\#2874](https://github.com/codeigniter4/CodeIgniter4/pull/2874) ([nmolinos](https://github.com/nmolinos)) +- Better locale matching against broad groups. Fixes \#2774 [\#2872](https://github.com/codeigniter4/CodeIgniter4/pull/2872) ([lonnieezell](https://github.com/lonnieezell)) +- Fixes session active detection on force\_https function and add more test CodeIgniter::forceSecureAccess\(\) run force\_https\(\) [\#2871](https://github.com/codeigniter4/CodeIgniter4/pull/2871) ([samsonasik](https://github.com/samsonasik)) +- clean up use statements: remove unused and sort [\#2870](https://github.com/codeigniter4/CodeIgniter4/pull/2870) ([samsonasik](https://github.com/samsonasik)) +- more test for View::renderString\(\) for null tempData [\#2869](https://github.com/codeigniter4/CodeIgniter4/pull/2869) ([samsonasik](https://github.com/samsonasik)) +- Localized label in validation rules [\#2868](https://github.com/codeigniter4/CodeIgniter4/pull/2868) ([michalsn](https://github.com/michalsn)) +- \[ci skip\] update translations version [\#2867](https://github.com/codeigniter4/CodeIgniter4/pull/2867) ([Instrye](https://github.com/Instrye)) +- Initialize $forge property in Seeder Class - fixes \#2825 [\#2864](https://github.com/codeigniter4/CodeIgniter4/pull/2864) ([jlamim](https://github.com/jlamim)) +- fix. saveData not work [\#2861](https://github.com/codeigniter4/CodeIgniter4/pull/2861) ([Instrye](https://github.com/Instrye)) +- fix. getGetPost and getPostGet can't work in index empty [\#2860](https://github.com/codeigniter4/CodeIgniter4/pull/2860) ([Instrye](https://github.com/Instrye)) +- \[ci skip\]fix. getHeader return header object [\#2859](https://github.com/codeigniter4/CodeIgniter4/pull/2859) ([Instrye](https://github.com/Instrye)) +- fix. filters alias multiple [\#2857](https://github.com/codeigniter4/CodeIgniter4/pull/2857) ([Instrye](https://github.com/Instrye)) +- \[ci skip\] typo fix Initial Configuration & Set Up [\#2856](https://github.com/codeigniter4/CodeIgniter4/pull/2856) ([samsonasik](https://github.com/samsonasik)) +- Enclose file paths in double quotes to capture spaces [\#2853](https://github.com/codeigniter4/CodeIgniter4/pull/2853) ([paulbalandan](https://github.com/paulbalandan)) +- Strip directory separators from auto-generated cell cache name. Fixes… [\#2851](https://github.com/codeigniter4/CodeIgniter4/pull/2851) ([lonnieezell](https://github.com/lonnieezell)) +- Normalize dir separator of Exceptions::cleanPath and added more paths to clean [\#2847](https://github.com/codeigniter4/CodeIgniter4/pull/2847) ([paulbalandan](https://github.com/paulbalandan)) +- Improve readability in the userguide with a fixed size [\#2846](https://github.com/codeigniter4/CodeIgniter4/pull/2846) ([jreklund](https://github.com/jreklund)) +- Fixed Issue \#2840 on discovery of classes by FileLocator [\#2844](https://github.com/codeigniter4/CodeIgniter4/pull/2844) ([paulbalandan](https://github.com/paulbalandan)) +- add $segment parameter in pager call by Model.php [\#2843](https://github.com/codeigniter4/CodeIgniter4/pull/2843) ([paul45](https://github.com/paul45)) +- Improve flash of unstyled content in userguide [\#2842](https://github.com/codeigniter4/CodeIgniter4/pull/2842) ([jreklund](https://github.com/jreklund)) +- Add English message for "string" validation rule [\#2841](https://github.com/codeigniter4/CodeIgniter4/pull/2841) ([rmilecki](https://github.com/rmilecki)) +- more tests for Common functions [\#2837](https://github.com/codeigniter4/CodeIgniter4/pull/2837) ([samsonasik](https://github.com/samsonasik)) +- Pagination: open page \> pageCount get last page [\#2834](https://github.com/codeigniter4/CodeIgniter4/pull/2834) ([samsonasik](https://github.com/samsonasik)) +- add ability for nested language definition [\#2833](https://github.com/codeigniter4/CodeIgniter4/pull/2833) ([samsonasik](https://github.com/samsonasik)) +- Documentation fixes [\#2827](https://github.com/codeigniter4/CodeIgniter4/pull/2827) ([pjio](https://github.com/pjio)) +- fix. URI path is empty [\#2824](https://github.com/codeigniter4/CodeIgniter4/pull/2824) ([Instrye](https://github.com/Instrye)) +- ignore coverage on exit and die [\#2820](https://github.com/codeigniter4/CodeIgniter4/pull/2820) ([samsonasik](https://github.com/samsonasik)) +- add respondUpdated\(\) method into API\ResponseTrait [\#2816](https://github.com/codeigniter4/CodeIgniter4/pull/2816) ([samsonasik](https://github.com/samsonasik)) +- ignore coverage on !CI\_DEBUG [\#2814](https://github.com/codeigniter4/CodeIgniter4/pull/2814) ([samsonasik](https://github.com/samsonasik)) +- Fix missing InvalidArgumentException in Database\BaseBuilder [\#2813](https://github.com/codeigniter4/CodeIgniter4/pull/2813) ([samsonasik](https://github.com/samsonasik)) +- Ensure $\_SERVER\['SCRIPT\_NAME'\] ends with PHP [\#2810](https://github.com/codeigniter4/CodeIgniter4/pull/2810) ([willnode](https://github.com/willnode)) +- make named constructor in Exception classes consistent: use return instead of throw [\#2809](https://github.com/codeigniter4/CodeIgniter4/pull/2809) ([samsonasik](https://github.com/samsonasik)) +- Check if dataset is empty before Model update. [\#2808](https://github.com/codeigniter4/CodeIgniter4/pull/2808) ([vibbow](https://github.com/vibbow)) +- test Controller::validate\(\) with string rules [\#2807](https://github.com/codeigniter4/CodeIgniter4/pull/2807) ([samsonasik](https://github.com/samsonasik)) +- clean up buffer tweak in FeatureTestCaseTest [\#2805](https://github.com/codeigniter4/CodeIgniter4/pull/2805) ([samsonasik](https://github.com/samsonasik)) +- using realpath\(\) for define $pathsPath in index.php [\#2804](https://github.com/codeigniter4/CodeIgniter4/pull/2804) ([samsonasik](https://github.com/samsonasik)) +- add ext-mbstring to required and update regex that sanitize file name [\#2803](https://github.com/codeigniter4/CodeIgniter4/pull/2803) ([samsonasik](https://github.com/samsonasik)) +- Add resetting QBFrom part [\#2802](https://github.com/codeigniter4/CodeIgniter4/pull/2802) ([michalsn](https://github.com/michalsn)) +- Update Routes.php [\#2801](https://github.com/codeigniter4/CodeIgniter4/pull/2801) ([mostafakhudair](https://github.com/mostafakhudair)) +- add more test for Entity : 100% tested [\#2798](https://github.com/codeigniter4/CodeIgniter4/pull/2798) ([samsonasik](https://github.com/samsonasik)) +- \[ci skip\] Fix download badge total shows [\#2797](https://github.com/codeigniter4/CodeIgniter4/pull/2797) ([samsonasik](https://github.com/samsonasik)) +- test for I18n\Time::toFormattedDateString [\#2796](https://github.com/codeigniter4/CodeIgniter4/pull/2796) ([samsonasik](https://github.com/samsonasik)) +- test Logger::determineFile\(\) for no stack trace [\#2795](https://github.com/codeigniter4/CodeIgniter4/pull/2795) ([samsonasik](https://github.com/samsonasik)) +- test CLI\CLI::strlen\(null\) [\#2794](https://github.com/codeigniter4/CodeIgniter4/pull/2794) ([samsonasik](https://github.com/samsonasik)) +- test for API\ResponseTrait::format\(\) with format is not json or xml [\#2793](https://github.com/codeigniter4/CodeIgniter4/pull/2793) ([samsonasik](https://github.com/samsonasik)) +- test for View\Cell::render\(\) with class has initController\(\) method [\#2792](https://github.com/codeigniter4/CodeIgniter4/pull/2792) ([samsonasik](https://github.com/samsonasik)) +- test Autoloader::initialize\(\) with composer path not found [\#2791](https://github.com/codeigniter4/CodeIgniter4/pull/2791) ([samsonasik](https://github.com/samsonasik)) +- add ability to replace {locale} to request-\>getLocale\(\) in form\_open\('action'\) [\#2790](https://github.com/codeigniter4/CodeIgniter4/pull/2790) ([samsonasik](https://github.com/samsonasik)) +- test for IncomingRequest::getFileMultiple\(\) [\#2789](https://github.com/codeigniter4/CodeIgniter4/pull/2789) ([samsonasik](https://github.com/samsonasik)) +- add MockEmail class [\#2788](https://github.com/codeigniter4/CodeIgniter4/pull/2788) ([samsonasik](https://github.com/samsonasik)) +- test for CodeIgniter\Config\Services::email\(\) [\#2785](https://github.com/codeigniter4/CodeIgniter4/pull/2785) ([samsonasik](https://github.com/samsonasik)) +- make Model::paginate\(\) use default perPage from Config\Pager-\>perPage if $perPage parameter not passed [\#2782](https://github.com/codeigniter4/CodeIgniter4/pull/2782) ([samsonasik](https://github.com/samsonasik)) +- \#2780 - LIMIT. [\#2781](https://github.com/codeigniter4/CodeIgniter4/pull/2781) ([nowackipawel](https://github.com/nowackipawel)) +- \[ci skip\] \_remap method must have return [\#2779](https://github.com/codeigniter4/CodeIgniter4/pull/2779) ([Instrye](https://github.com/Instrye)) +- Rework get\_filenames [\#2778](https://github.com/codeigniter4/CodeIgniter4/pull/2778) ([MGatner](https://github.com/MGatner)) +- Fix \#2776 add ability to paginate\(\) query with group by [\#2777](https://github.com/codeigniter4/CodeIgniter4/pull/2777) ([samsonasik](https://github.com/samsonasik)) +- Update on "Build Your First Application" [\#2775](https://github.com/codeigniter4/CodeIgniter4/pull/2775) ([jreklund](https://github.com/jreklund)) +- Fix ? bind with := bind [\#2773](https://github.com/codeigniter4/CodeIgniter4/pull/2773) ([musmanikram](https://github.com/musmanikram)) +- Fixed some styling in "Installation" chapter \[ci skip\] [\#2772](https://github.com/codeigniter4/CodeIgniter4/pull/2772) ([jreklund](https://github.com/jreklund)) +- Uncommented tests, mistake in my previous PR :\( [\#2767](https://github.com/codeigniter4/CodeIgniter4/pull/2767) ([musmanikram](https://github.com/musmanikram)) +- fix. MYSQLI::DBDebug can't woker [\#2755](https://github.com/codeigniter4/CodeIgniter4/pull/2755) ([Instrye](https://github.com/Instrye)) +- fix. delete\_cookite can't delete alreday set Cookie [\#2709](https://github.com/codeigniter4/CodeIgniter4/pull/2709) ([Instrye](https://github.com/Instrye)) +- Re-write userguide to support Python 3 and future proofing Sphinx [\#2671](https://github.com/codeigniter4/CodeIgniter4/pull/2671) ([jreklund](https://github.com/jreklund)) +- Colored table in CLI [\#2624](https://github.com/codeigniter4/CodeIgniter4/pull/2624) ([enix-app](https://github.com/enix-app)) + +## [4.0.2](https://github.com/codeigniter4/CodeIgniter4/tree/4.0.2) (2020-02-25) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.1...4.0.2) + +**Fixed bugs:** + +- Bug: Your requirements could not be resolved to an installable set of packages. [\#2613](https://github.com/codeigniter4/CodeIgniter4/issues/2613) + +**Merged pull requests:** + +- Removed unused test class that was causing appstarter not to work from CLI. [\#2614](https://github.com/codeigniter4/CodeIgniter4/pull/2614) ([lonnieezell](https://github.com/lonnieezell)) +- \[UG\] Fix all Sphinx warnings [\#2611](https://github.com/codeigniter4/CodeIgniter4/pull/2611) ([LittleJ](https://github.com/LittleJ)) +- \[UG\] Sphinx\_rtd\_theme fixes and improvements [\#2610](https://github.com/codeigniter4/CodeIgniter4/pull/2610) ([LittleJ](https://github.com/LittleJ)) + +## [v4.0.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.1) (2020-02-24) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/4.0.0...v4.0.1) + +**Fixed bugs:** + +- Bug: Difficult architecture of the codeigniter4 project [\#2602](https://github.com/codeigniter4/CodeIgniter4/issues/2602) +- Bug: mentioned rc4 in the changelog file of V4 userguide [\#2599](https://github.com/codeigniter4/CodeIgniter4/issues/2599) + +**Merged pull requests:** + +- \[doc\] Removal of the 'rc' parameter from the installation / update co… [\#2604](https://github.com/codeigniter4/CodeIgniter4/pull/2604) ([jlamim](https://github.com/jlamim)) + +## [4.0.0](https://github.com/codeigniter4/CodeIgniter4/tree/4.0.0) (2020-02-24) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-rc.4...4.0.0) + +**Fixed bugs:** + +- Bug: CI cant display error with API Response Trait [\#2586](https://github.com/codeigniter4/CodeIgniter4/issues/2586) +- Bug: testOrHavingBy\(\) requires a select [\#2584](https://github.com/codeigniter4/CodeIgniter4/issues/2584) +- Bug: Email-\>validateEmail\(\) on wrong email address leads to TypeError [\#2580](https://github.com/codeigniter4/CodeIgniter4/issues/2580) +- Bug: $forge-\>dropColumn not allowing arrays [\#2576](https://github.com/codeigniter4/CodeIgniter4/issues/2576) +- Bug: CI4 set ID to NULL when I try insert a row [\#2557](https://github.com/codeigniter4/CodeIgniter4/issues/2557) +- "Required" rules are not being enforced at all by the model [\#2555](https://github.com/codeigniter4/CodeIgniter4/issues/2555) +- Bug: Debug Bar showing before the head tag [\#2545](https://github.com/codeigniter4/CodeIgniter4/issues/2545) +- Bug: Docs / Library Reference / Validation: missing important information on reset\(\) method [\#2535](https://github.com/codeigniter4/CodeIgniter4/issues/2535) +- Bug: system/Config/BaseService.php getSharedInstance\(\) will not return mocks with non-lowercase key [\#2534](https://github.com/codeigniter4/CodeIgniter4/issues/2534) +- Bug: multi-column WHERE not prefixed [\#2532](https://github.com/codeigniter4/CodeIgniter4/issues/2532) +- Bug: CodeIgniter\Files\File::getSize\(\) - wrong type of result [\#2476](https://github.com/codeigniter4/CodeIgniter4/issues/2476) +- Bug: Move work with $\_GET\['page'\] from Model to Pager [\#2467](https://github.com/codeigniter4/CodeIgniter4/issues/2467) +- Bug: ImageHandler has no reality checks [\#2421](https://github.com/codeigniter4/CodeIgniter4/issues/2421) +- Bug: No default HTTP protocol version is set when creating a new Response [\#2383](https://github.com/codeigniter4/CodeIgniter4/issues/2383) +- Bug: Filter wildcards ignore default methods [\#2455](https://github.com/codeigniter4/CodeIgniter4/issues/2455) +- trailing slash cause redirect to root [\#2445](https://github.com/codeigniter4/CodeIgniter4/issues/2445) +- Bug: Commands cannot accept many paths [\#2148](https://github.com/codeigniter4/CodeIgniter4/issues/2148) + +**Closed issues:** + +- Add an 'alpha\_numeric\_punct' rule to FormatRules [\#2549](https://github.com/codeigniter4/CodeIgniter4/issues/2549) +- Feature: Forge, Adding Foreign key for table already created [\#2543](https://github.com/codeigniter4/CodeIgniter4/issues/2543) +- Error In UserGuide [\#2530](https://github.com/codeigniter4/CodeIgniter4/issues/2530) +- Lack of test on Travis for PHP 7.4 and tests don't run on upcoming PHP versions [\#2293](https://github.com/codeigniter4/CodeIgniter4/issues/2293) +- Feature: model helper [\#2292](https://github.com/codeigniter4/CodeIgniter4/issues/2292) +- Issue with layouts renderer and sections =\> merge sections in one view [\#2491](https://github.com/codeigniter4/CodeIgniter4/issues/2491) +- Update package dependency version for kint-php/kint [\#2373](https://github.com/codeigniter4/CodeIgniter4/issues/2373) + +**Merged pull requests:** + +- Deprecate Devstarter, add `builds` [\#2598](https://github.com/codeigniter4/CodeIgniter4/pull/2598) ([MGatner](https://github.com/MGatner)) +- Fix typo issues causing Sphinx warnings + Update "Welcome page" screenshot [\#2597](https://github.com/codeigniter4/CodeIgniter4/pull/2597) ([LittleJ](https://github.com/LittleJ)) +- Update sphinx\_rtd\_theme from version 0.2.4 to version 0.4.3 [\#2596](https://github.com/codeigniter4/CodeIgniter4/pull/2596) ([LittleJ](https://github.com/LittleJ)) +- Fixed Bug: testOrHavingBy\(\) requires a select | \#2584 [\#2595](https://github.com/codeigniter4/CodeIgniter4/pull/2595) ([jlamim](https://github.com/jlamim)) +- Add Slack to the "Support" section of the documentation [\#2594](https://github.com/codeigniter4/CodeIgniter4/pull/2594) ([LittleJ](https://github.com/LittleJ)) +- Fixed links to the AJAX Requests details page [\#2593](https://github.com/codeigniter4/CodeIgniter4/pull/2593) ([jlamim](https://github.com/jlamim)) +- Add color scheme information in the documentation [\#2592](https://github.com/codeigniter4/CodeIgniter4/pull/2592) ([LittleJ](https://github.com/LittleJ)) +- User Guide authentication recommendations [\#2591](https://github.com/codeigniter4/CodeIgniter4/pull/2591) ([MGatner](https://github.com/MGatner)) +- Add a "Go further" section on the welcome page [\#2590](https://github.com/codeigniter4/CodeIgniter4/pull/2590) ([LittleJ](https://github.com/LittleJ)) +- Add DotEnv::parse\(\) [\#2588](https://github.com/codeigniter4/CodeIgniter4/pull/2588) ([MGatner](https://github.com/MGatner)) +- Better debug routes [\#2587](https://github.com/codeigniter4/CodeIgniter4/pull/2587) ([atishamte](https://github.com/atishamte)) +- Typos change in contributing.md [\#2583](https://github.com/codeigniter4/CodeIgniter4/pull/2583) ([atishamte](https://github.com/atishamte)) +- Fix type error in email validation [\#2582](https://github.com/codeigniter4/CodeIgniter4/pull/2582) ([musmanikram](https://github.com/musmanikram)) +- Fixed Markdown heading [\#2581](https://github.com/codeigniter4/CodeIgniter4/pull/2581) ([ImMaax](https://github.com/ImMaax)) +- Cache FileHandler unlink exception [\#2579](https://github.com/codeigniter4/CodeIgniter4/pull/2579) ([MGatner](https://github.com/MGatner)) +- Fix drop column with array [\#2578](https://github.com/codeigniter4/CodeIgniter4/pull/2578) ([musmanikram](https://github.com/musmanikram)) +- Refactor Tests [\#2577](https://github.com/codeigniter4/CodeIgniter4/pull/2577) ([MGatner](https://github.com/MGatner)) +- Subfolder base\_url\(\) with parameter [\#2574](https://github.com/codeigniter4/CodeIgniter4/pull/2574) ([MGatner](https://github.com/MGatner)) +- Image verification [\#2573](https://github.com/codeigniter4/CodeIgniter4/pull/2573) ([MGatner](https://github.com/MGatner)) +- Use default protocol if unspecified [\#2572](https://github.com/codeigniter4/CodeIgniter4/pull/2572) ([MGatner](https://github.com/MGatner)) +- Retain CLI segments [\#2571](https://github.com/codeigniter4/CodeIgniter4/pull/2571) ([MGatner](https://github.com/MGatner)) +- Model's set method should accept not only string [\#2570](https://github.com/codeigniter4/CodeIgniter4/pull/2570) ([nowackipawel](https://github.com/nowackipawel)) +- Use lowercase service names [\#2569](https://github.com/codeigniter4/CodeIgniter4/pull/2569) ([MGatner](https://github.com/MGatner)) +- Apply User Guide code style to Errors [\#2567](https://github.com/codeigniter4/CodeIgniter4/pull/2567) ([MGatner](https://github.com/MGatner)) +- Move debug toolbar after head tag. Fixes \#2545 [\#2566](https://github.com/codeigniter4/CodeIgniter4/pull/2566) ([MGatner](https://github.com/MGatner)) +- Updates To Kint Loading [\#2565](https://github.com/codeigniter4/CodeIgniter4/pull/2565) ([najdanovicivan](https://github.com/najdanovicivan)) +- Updated loader and composer script to use Kint 3.3 for \#2373 [\#2564](https://github.com/codeigniter4/CodeIgniter4/pull/2564) ([lonnieezell](https://github.com/lonnieezell)) +- Added rule "alpha\_numeric\_punct" [\#2562](https://github.com/codeigniter4/CodeIgniter4/pull/2562) ([dafriend](https://github.com/dafriend)) +- Fix - Add ajax to docs "General " page \[ci skip\] [\#2561](https://github.com/codeigniter4/CodeIgniter4/pull/2561) ([dafriend](https://github.com/dafriend)) +- MySQLi: Incorrect DBDebug flag used for connection charset [\#2558](https://github.com/codeigniter4/CodeIgniter4/pull/2558) ([jreklund](https://github.com/jreklund)) +- Update File.php [\#2552](https://github.com/codeigniter4/CodeIgniter4/pull/2552) ([thanhtaivtt](https://github.com/thanhtaivtt)) +- disable buffer check on "testing" environment [\#2551](https://github.com/codeigniter4/CodeIgniter4/pull/2551) ([samsonasik](https://github.com/samsonasik)) +- Improved view: welcome\_message.php [\#2550](https://github.com/codeigniter4/CodeIgniter4/pull/2550) ([Vizzielli](https://github.com/Vizzielli)) +- Add retry creation server when the port is used [\#2544](https://github.com/codeigniter4/CodeIgniter4/pull/2544) ([thanhtaivtt](https://github.com/thanhtaivtt)) +- New "welcome" page [\#2541](https://github.com/codeigniter4/CodeIgniter4/pull/2541) ([LittleJ](https://github.com/LittleJ)) +- valid\_ip removed $data which was causing exception [\#2540](https://github.com/codeigniter4/CodeIgniter4/pull/2540) ([nowackipawel](https://github.com/nowackipawel)) +- explanation of reset\(\) method in section Working With Validation fixes \#2535 [\#2539](https://github.com/codeigniter4/CodeIgniter4/pull/2539) ([bivanbi](https://github.com/bivanbi)) +- Update TravisCI config for PHP7.4 [\#2537](https://github.com/codeigniter4/CodeIgniter4/pull/2537) ([musmanikram](https://github.com/musmanikram)) +- Fix multi-column WHERE not prefixed with DBPrefix [\#2533](https://github.com/codeigniter4/CodeIgniter4/pull/2533) ([musmanikram](https://github.com/musmanikram)) +- Update images.rst [\#2529](https://github.com/codeigniter4/CodeIgniter4/pull/2529) ([avegacms](https://github.com/avegacms)) +- Added new model helper method. [\#2514](https://github.com/codeigniter4/CodeIgniter4/pull/2514) ([lonnieezell](https://github.com/lonnieezell)) +- Debug bar: Dark/light mode + Complete CSS refactoring [\#2478](https://github.com/codeigniter4/CodeIgniter4/pull/2478) ([LittleJ](https://github.com/LittleJ)) +- WIP Update Validation.php [\#2083](https://github.com/codeigniter4/CodeIgniter4/pull/2083) ([MohKari](https://github.com/MohKari)) + +## [v4.0.0-rc.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-rc.4) (2020-02-07) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-rc.3...v4.0.0-rc.4) + +**Fixed bugs:** + +- Bug: Class 'Locale' not found when I try to use Time::parse\(\) [\#2519](https://github.com/codeigniter4/CodeIgniter4/issues/2519) +- Bug: Wrong Links for pager when having subfolders [\#2505](https://github.com/codeigniter4/CodeIgniter4/issues/2505) +- View Cell render not be call initController [\#2500](https://github.com/codeigniter4/CodeIgniter4/issues/2500) +- Bug: user guide compile failed. [\#2492](https://github.com/codeigniter4/CodeIgniter4/issues/2492) +- Bug: setAutoRoute to false not working [\#2480](https://github.com/codeigniter4/CodeIgniter4/issues/2480) +- Bug: CodeIgniter\Router\Router-\>checkRoutes\(\) strpos\(\) expects parameter 1 to be string, array given [\#2479](https://github.com/codeigniter4/CodeIgniter4/issues/2479) +- Bug: Unable to use ul\(\) helper with out modification [\#2473](https://github.com/codeigniter4/CodeIgniter4/issues/2473) +- Bug: Cannot pass value in the function in ControllerTest [\#2470](https://github.com/codeigniter4/CodeIgniter4/issues/2470) +- Bug: $useTimestamps cause insert\(\) exception [\#2469](https://github.com/codeigniter4/CodeIgniter4/issues/2469) +- Bug: prototype of setBody\($data\); method in CodeIgniter\HTTP\Message should be changed to setBody\(string $data\); [\#2466](https://github.com/codeigniter4/CodeIgniter4/issues/2466) +- Don't update it. It's rubbishBug: [\#2463](https://github.com/codeigniter4/CodeIgniter4/issues/2463) +- Bug: Autoload over Composer [\#2461](https://github.com/codeigniter4/CodeIgniter4/issues/2461) +- Bug: The docs say `composer install` instead of `composer required` [\#2457](https://github.com/codeigniter4/CodeIgniter4/issues/2457) +- Bug: if not CSPEnaled but i have some [\#2456](https://github.com/codeigniter4/CodeIgniter4/issues/2456) +- Bug: IsAJAX\(\) relies on inconsistent headers [\#2454](https://github.com/codeigniter4/CodeIgniter4/issues/2454) +- Bug: [\#2448](https://github.com/codeigniter4/CodeIgniter4/issues/2448) +- Bug: Double use where and etc [\#2444](https://github.com/codeigniter4/CodeIgniter4/issues/2444) +- Bug: Double use esc function with form\_input and etc... [\#2443](https://github.com/codeigniter4/CodeIgniter4/issues/2443) +- Bug: Entity casts do not cast the original data [\#2441](https://github.com/codeigniter4/CodeIgniter4/issues/2441) +- Bug: namespace view returns empty value [\#2440](https://github.com/codeigniter4/CodeIgniter4/issues/2440) +- Bug: php spark Call to undefined function CodeIgniter\Autoloader\get\_filenames\(\) [\#2439](https://github.com/codeigniter4/CodeIgniter4/issues/2439) +- Bug: Curly brace deprecation [\#2430](https://github.com/codeigniter4/CodeIgniter4/issues/2430) +- Bug: Routes and Namespace [\#2423](https://github.com/codeigniter4/CodeIgniter4/issues/2423) +- Bug: Validation not working [\#2418](https://github.com/codeigniter4/CodeIgniter4/issues/2418) +- Bug: Baseservice - getSharedInstance [\#2414](https://github.com/codeigniter4/CodeIgniter4/issues/2414) +- Bug: base\_url\(\) and redirect\(\) not honoring baseURL with paths [\#2409](https://github.com/codeigniter4/CodeIgniter4/issues/2409) +- form\_input double escaping data why ? [\#2405](https://github.com/codeigniter4/CodeIgniter4/issues/2405) +- Bug: initController not called in ResourceController [\#2404](https://github.com/codeigniter4/CodeIgniter4/issues/2404) +- Bug: [\#2397](https://github.com/codeigniter4/CodeIgniter4/issues/2397) +- URL Helper safe\_mailto UTF8Bug: [\#2396](https://github.com/codeigniter4/CodeIgniter4/issues/2396) +- CSRF Filter redirect back not working [\#2395](https://github.com/codeigniter4/CodeIgniter4/issues/2395) +- Bug: 404 error page override with cache [\#2391](https://github.com/codeigniter4/CodeIgniter4/issues/2391) +- Bug: Mixed migration formats don't order [\#2386](https://github.com/codeigniter4/CodeIgniter4/issues/2386) +- Bug: \CodeIgniter\Model::validate\(\) returns TRUE if $data is empty [\#2384](https://github.com/codeigniter4/CodeIgniter4/issues/2384) +- Bug: Usage of `static::methodName` in CodeIgniter\Config\Services prevents Service overriding [\#2376](https://github.com/codeigniter4/CodeIgniter4/issues/2376) +- Bug: Duplicate headers in response [\#2375](https://github.com/codeigniter4/CodeIgniter4/issues/2375) +- Bug: Nothing work with minimal config \(DIRECTORY SEPARATOR\) [\#2370](https://github.com/codeigniter4/CodeIgniter4/issues/2370) +- Bug: current\_url function not working as expected. [\#2365](https://github.com/codeigniter4/CodeIgniter4/issues/2365) +- Bug: localhost development server after edit the content not updated or reloaded [\#2363](https://github.com/codeigniter4/CodeIgniter4/issues/2363) +- Bug: with the parser, nl2br in a foreach duplicates entries. [\#2360](https://github.com/codeigniter4/CodeIgniter4/issues/2360) +- Bug: Prevents the use of global functions with parameters \[Validation\] [\#2357](https://github.com/codeigniter4/CodeIgniter4/issues/2357) +- Bug: lang\('app.name'\) should prefer APPPATH.Language/Validation/en/app.php over installed packages [\#2354](https://github.com/codeigniter4/CodeIgniter4/issues/2354) +- Bug: Inappropriate delimiter used in fillRouteParams [\#2353](https://github.com/codeigniter4/CodeIgniter4/issues/2353) +- Bug: Please there is issue on the time and date guide in codeigniter 4 [\#2351](https://github.com/codeigniter4/CodeIgniter4/issues/2351) +- Bug: Model\(\)-\>find\(null\) should return null value [\#2350](https://github.com/codeigniter4/CodeIgniter4/issues/2350) +- Bug: URL, Redirect and Pagination misbehave [\#2347](https://github.com/codeigniter4/CodeIgniter4/issues/2347) +- Bug: Toolbar ErrorException Division by zero [\#2340](https://github.com/codeigniter4/CodeIgniter4/issues/2340) +- Bug: Cannot pass a string param with a space to a custom parser plugin [\#2318](https://github.com/codeigniter4/CodeIgniter4/issues/2318) +- Bug: Logger Path Duplicated [\#2286](https://github.com/codeigniter4/CodeIgniter4/issues/2286) +- Bug: Email: SMTP Protocol Implementation @ Data Termination [\#2274](https://github.com/codeigniter4/CodeIgniter4/issues/2274) +- Bug: Redirect to route ignores path set in baseurl [\#2119](https://github.com/codeigniter4/CodeIgniter4/issues/2119) + +**Closed issues:** + +- $routes not do the job as well [\#2531](https://github.com/codeigniter4/CodeIgniter4/issues/2531) +- Multiple composer.json handling in codeigniter!! [\#2528](https://github.com/codeigniter4/CodeIgniter4/issues/2528) +- Mention about events in the upgrading doc pages [\#2521](https://github.com/codeigniter4/CodeIgniter4/issues/2521) +- Missing Constant in E-Mail [\#2512](https://github.com/codeigniter4/CodeIgniter4/issues/2512) +- Image Manipulation Class [\#2498](https://github.com/codeigniter4/CodeIgniter4/issues/2498) +- Schema param in the .env file [\#2483](https://github.com/codeigniter4/CodeIgniter4/issues/2483) +- system\Database\MigrationRunner-\>regress\(\) resets instance variable $namespace to null [\#2474](https://github.com/codeigniter4/CodeIgniter4/issues/2474) +- Issue passing data to views [\#2464](https://github.com/codeigniter4/CodeIgniter4/issues/2464) +- currentURL & previousURL doesnt work in parser [\#2460](https://github.com/codeigniter4/CodeIgniter4/issues/2460) +- Double quotes [\#2459](https://github.com/codeigniter4/CodeIgniter4/issues/2459) +- Feature about Localization [\#2419](https://github.com/codeigniter4/CodeIgniter4/issues/2419) +- Documentation Fix Needed [\#2412](https://github.com/codeigniter4/CodeIgniter4/issues/2412) +- No such file or Directory found In Ubuntu 19.10 [\#2394](https://github.com/codeigniter4/CodeIgniter4/issues/2394) +- previous\_url\(\) not loading the base path together [\#2378](https://github.com/codeigniter4/CodeIgniter4/issues/2378) +- Wrong Logo on GitHub page [\#2372](https://github.com/codeigniter4/CodeIgniter4/issues/2372) +- How to use the pagination with view parser? [\#2371](https://github.com/codeigniter4/CodeIgniter4/issues/2371) +- Feature Request: Validation: in\_db\[table.field\] [\#2366](https://github.com/codeigniter4/CodeIgniter4/issues/2366) +- Feature request [\#2361](https://github.com/codeigniter4/CodeIgniter4/issues/2361) +- Feature: AJAX route option [\#2310](https://github.com/codeigniter4/CodeIgniter4/issues/2310) +- Return value of CodeIgniter\Database\BaseConnection::getConnectStart\(\) must be of the type float, null returned [\#2158](https://github.com/codeigniter4/CodeIgniter4/issues/2158) +- Create Security Guideline [\#73](https://github.com/codeigniter4/CodeIgniter4/issues/73) + +**Merged pull requests:** + +- Update manual.rst [\#2527](https://github.com/codeigniter4/CodeIgniter4/pull/2527) ([avegacms](https://github.com/avegacms)) +- Page in the official documentation on ajax requests with iSAJAX\(\) fixes \#2454 [\#2526](https://github.com/codeigniter4/CodeIgniter4/pull/2526) ([jlamim](https://github.com/jlamim)) +- Remove incorrect inline doc type [\#2525](https://github.com/codeigniter4/CodeIgniter4/pull/2525) ([MGatner](https://github.com/MGatner)) +- Restore namespace after regress. Fixes \#2474 [\#2524](https://github.com/codeigniter4/CodeIgniter4/pull/2524) ([MGatner](https://github.com/MGatner)) +- Replace legacy CI3 constant. Fixes \#2512 [\#2523](https://github.com/codeigniter4/CodeIgniter4/pull/2523) ([MGatner](https://github.com/MGatner)) +- Adding Events information in the 'Upgrading from 3.x to 4.x' section [\#2522](https://github.com/codeigniter4/CodeIgniter4/pull/2522) ([jlamim](https://github.com/jlamim)) +- Fix pager URI to work in subfolders. [\#2518](https://github.com/codeigniter4/CodeIgniter4/pull/2518) ([lonnieezell](https://github.com/lonnieezell)) +- HTML Helper - Fix attribute type for lists [\#2516](https://github.com/codeigniter4/CodeIgniter4/pull/2516) ([najdanovicivan](https://github.com/najdanovicivan)) +- Layout Renderer Fix [\#2515](https://github.com/codeigniter4/CodeIgniter4/pull/2515) ([najdanovicivan](https://github.com/najdanovicivan)) +- \[ci skip\] Typo in userguide "Entity Classes - Business Logic" [\#2513](https://github.com/codeigniter4/CodeIgniter4/pull/2513) ([jreklund](https://github.com/jreklund)) +- Database add highlight [\#2511](https://github.com/codeigniter4/CodeIgniter4/pull/2511) ([MashinaMashina](https://github.com/MashinaMashina)) +- Revert Renderer section reset [\#2509](https://github.com/codeigniter4/CodeIgniter4/pull/2509) ([MGatner](https://github.com/MGatner)) +- Update ordering of search locations for better prioritization. Fixes \#2354 [\#2507](https://github.com/codeigniter4/CodeIgniter4/pull/2507) ([lonnieezell](https://github.com/lonnieezell)) +- Proposal: HTTP Response - Fix crash on CSP methods CSP is disabled [\#2506](https://github.com/codeigniter4/CodeIgniter4/pull/2506) ([najdanovicivan](https://github.com/najdanovicivan)) +- BaseConnection - Nullable return type in getConnectStart\(\) [\#2504](https://github.com/codeigniter4/CodeIgniter4/pull/2504) ([najdanovicivan](https://github.com/najdanovicivan)) +- View Renderer - Reset sections after generating the ouput [\#2502](https://github.com/codeigniter4/CodeIgniter4/pull/2502) ([najdanovicivan](https://github.com/najdanovicivan)) +- view\_cell call controller on initController method. [\#2501](https://github.com/codeigniter4/CodeIgniter4/pull/2501) ([byazrail](https://github.com/byazrail)) +- View Parser - Fix ParsePair\(\) with filter [\#2499](https://github.com/codeigniter4/CodeIgniter4/pull/2499) ([najdanovicivan](https://github.com/najdanovicivan)) +- Fix splitQueryPart\(\) [\#2497](https://github.com/codeigniter4/CodeIgniter4/pull/2497) ([MashinaMashina](https://github.com/MashinaMashina)) +- Use site\_url for RedirectResponse. Fixes \#2119 [\#2496](https://github.com/codeigniter4/CodeIgniter4/pull/2496) ([lonnieezell](https://github.com/lonnieezell)) +- \[ci skip\] update toolbar userguide [\#2495](https://github.com/codeigniter4/CodeIgniter4/pull/2495) ([Instrye](https://github.com/Instrye)) +- Debug Toolbar - Fix Debugbar-Time header, Render in \ [\#2494](https://github.com/codeigniter4/CodeIgniter4/pull/2494) ([najdanovicivan](https://github.com/najdanovicivan)) +- fix sphinx version. [\#2493](https://github.com/codeigniter4/CodeIgniter4/pull/2493) ([ytetsuro](https://github.com/ytetsuro)) +- fix. Toolbar init view Error [\#2490](https://github.com/codeigniter4/CodeIgniter4/pull/2490) ([Instrye](https://github.com/Instrye)) +- Fix pager [\#2489](https://github.com/codeigniter4/CodeIgniter4/pull/2489) ([MashinaMashina](https://github.com/MashinaMashina)) +- Update current\_url and previous\_url in the docs for View Parser. Fixes \#2460 [\#2486](https://github.com/codeigniter4/CodeIgniter4/pull/2486) ([lonnieezell](https://github.com/lonnieezell)) +- Typo in user guide "Running via the Command Line" [\#2485](https://github.com/codeigniter4/CodeIgniter4/pull/2485) ([jreklund](https://github.com/jreklund)) +- Services request add URI Core System extend support [\#2482](https://github.com/codeigniter4/CodeIgniter4/pull/2482) ([byazrail](https://github.com/byazrail)) +- Fix \#2479. Priority Redirection. [\#2481](https://github.com/codeigniter4/CodeIgniter4/pull/2481) ([Instrye](https://github.com/Instrye)) +- ControllerTest should work without URI specified. Fixes \#2470 [\#2472](https://github.com/codeigniter4/CodeIgniter4/pull/2472) ([lonnieezell](https://github.com/lonnieezell)) +- Transition from Zend Escaper to Laminas Escaper [\#2471](https://github.com/codeigniter4/CodeIgniter4/pull/2471) ([lonnieezell](https://github.com/lonnieezell)) +- Fix impossible length for migration table id. [\#2462](https://github.com/codeigniter4/CodeIgniter4/pull/2462) ([ytetsuro](https://github.com/ytetsuro)) +- Replace `composer install` by `composer require` [\#2458](https://github.com/codeigniter4/CodeIgniter4/pull/2458) ([SteeveDroz](https://github.com/SteeveDroz)) +- \[ci skip\] Error correction in reference to Query Builder emptyTable m… [\#2452](https://github.com/codeigniter4/CodeIgniter4/pull/2452) ([jlamim](https://github.com/jlamim)) +- CRITICAL when $\_SESSION is null / Argument 2 passed to dot\_array\_search\(\) must be \[\] [\#2450](https://github.com/codeigniter4/CodeIgniter4/pull/2450) ([nowackipawel](https://github.com/nowackipawel)) +- User Guide: Query Builder selectCount - error correction in example [\#2449](https://github.com/codeigniter4/CodeIgniter4/pull/2449) ([jlamim](https://github.com/jlamim)) +- Existing File checks \(Nowackipawel/patch-69\) [\#2447](https://github.com/codeigniter4/CodeIgniter4/pull/2447) ([MGatner](https://github.com/MGatner)) +- DB Insert Ignore \(Tada5hi/database-feature\) [\#2446](https://github.com/codeigniter4/CodeIgniter4/pull/2446) ([MGatner](https://github.com/MGatner)) +- Nice array view in debug toolbar [\#2438](https://github.com/codeigniter4/CodeIgniter4/pull/2438) ([MashinaMashina](https://github.com/MashinaMashina)) +- \[ci skip\] Fix Message method reference [\#2436](https://github.com/codeigniter4/CodeIgniter4/pull/2436) ([MGatner](https://github.com/MGatner)) +- Inserting through a model should respect all validation rules. Fixes \#2384 [\#2433](https://github.com/codeigniter4/CodeIgniter4/pull/2433) ([lonnieezell](https://github.com/lonnieezell)) +- Fix curly brace deprecation in php 7.4 [\#2432](https://github.com/codeigniter4/CodeIgniter4/pull/2432) ([musmanikram](https://github.com/musmanikram)) +- fix. safe\_mailto multi-byte safe [\#2429](https://github.com/codeigniter4/CodeIgniter4/pull/2429) ([Instrye](https://github.com/Instrye)) +- Add $recipients property to Config\Email [\#2427](https://github.com/codeigniter4/CodeIgniter4/pull/2427) ([dafriend](https://github.com/dafriend)) +- Add hex validation rule, test, Guide [\#2426](https://github.com/codeigniter4/CodeIgniter4/pull/2426) ([MGatner](https://github.com/MGatner)) +- fix: Router setDefaultNameSpace can't worker [\#2425](https://github.com/codeigniter4/CodeIgniter4/pull/2425) ([Instrye](https://github.com/Instrye)) +- Don't show duplicate Date headers when running under PHPs server. Fixes \#2375 [\#2422](https://github.com/codeigniter4/CodeIgniter4/pull/2422) ([lonnieezell](https://github.com/lonnieezell)) +- Change current\_url\(\) to use cloned URI [\#2420](https://github.com/codeigniter4/CodeIgniter4/pull/2420) ([MGatner](https://github.com/MGatner)) +- Revise Encryption Service Documentation \[ci skip\] [\#2417](https://github.com/codeigniter4/CodeIgniter4/pull/2417) ([dafriend](https://github.com/dafriend)) +- Add missing closing braces of condition 'hasError\(\)' under Check If… [\#2416](https://github.com/codeigniter4/CodeIgniter4/pull/2416) ([musmanikram](https://github.com/musmanikram)) +- Add 'nullable' to MySQL field data [\#2415](https://github.com/codeigniter4/CodeIgniter4/pull/2415) ([MGatner](https://github.com/MGatner)) +- fix. toolbar file 301 [\#2413](https://github.com/codeigniter4/CodeIgniter4/pull/2413) ([Instrye](https://github.com/Instrye)) +- \#2318 - fix parse params of plugin [\#2411](https://github.com/codeigniter4/CodeIgniter4/pull/2411) ([oleg1540](https://github.com/oleg1540)) +- Looks like a typo. [\#2410](https://github.com/codeigniter4/CodeIgniter4/pull/2410) ([AndiKod](https://github.com/AndiKod)) +- Ensure previous\_url\(\) gets accurate URI. [\#2408](https://github.com/codeigniter4/CodeIgniter4/pull/2408) ([lonnieezell](https://github.com/lonnieezell)) +- Fix url helper functions to work when site hosted in subfolders. [\#2407](https://github.com/codeigniter4/CodeIgniter4/pull/2407) ([lonnieezell](https://github.com/lonnieezell)) +- Fix issue \#2391 CodeIgniter::display404errors\(\) [\#2406](https://github.com/codeigniter4/CodeIgniter4/pull/2406) ([dafriend](https://github.com/dafriend)) +- Removed pointless isset\(\) check [\#2402](https://github.com/codeigniter4/CodeIgniter4/pull/2402) ([dafriend](https://github.com/dafriend)) +- Remove pointless check from conditional [\#2401](https://github.com/codeigniter4/CodeIgniter4/pull/2401) ([dafriend](https://github.com/dafriend)) +- Remove redundant check in conditionals [\#2400](https://github.com/codeigniter4/CodeIgniter4/pull/2400) ([dafriend](https://github.com/dafriend)) +- Revise Controllers Documentation \[ci skip\] [\#2399](https://github.com/codeigniter4/CodeIgniter4/pull/2399) ([dafriend](https://github.com/dafriend)) +- Edit .htaccess [\#2398](https://github.com/codeigniter4/CodeIgniter4/pull/2398) ([MashinaMashina](https://github.com/MashinaMashina)) +- Add validation function `is\_not\_unique` [\#2392](https://github.com/codeigniter4/CodeIgniter4/pull/2392) ([kennylajara](https://github.com/kennylajara)) +- Confer silent status to nested seeders [\#2389](https://github.com/codeigniter4/CodeIgniter4/pull/2389) ([MGatner](https://github.com/MGatner)) +- Fix copypaste command comment [\#2388](https://github.com/codeigniter4/CodeIgniter4/pull/2388) ([MGatner](https://github.com/MGatner)) +- Use only digits for migrations order [\#2387](https://github.com/codeigniter4/CodeIgniter4/pull/2387) ([MGatner](https://github.com/MGatner)) +- quick fix postgresql insert id [\#2382](https://github.com/codeigniter4/CodeIgniter4/pull/2382) ([iam-adty](https://github.com/iam-adty)) +- Fix: Use of CodeIgniter\Config\Services prevents Service overriding [\#2381](https://github.com/codeigniter4/CodeIgniter4/pull/2381) ([dafriend](https://github.com/dafriend)) +- Replace null log file extension check [\#2379](https://github.com/codeigniter4/CodeIgniter4/pull/2379) ([MGatner](https://github.com/MGatner)) +- Docs Rev: Replacing Core Classes \[ci skip\] [\#2377](https://github.com/codeigniter4/CodeIgniter4/pull/2377) ([dafriend](https://github.com/dafriend)) +- Remove LoggerAwareTrait from Email class [\#2369](https://github.com/codeigniter4/CodeIgniter4/pull/2369) ([dafriend](https://github.com/dafriend)) +- Remove log\_message from Email::\_\_construct [\#2368](https://github.com/codeigniter4/CodeIgniter4/pull/2368) ([dafriend](https://github.com/dafriend)) +- Email config doesn't incorporate .env items [\#2364](https://github.com/codeigniter4/CodeIgniter4/pull/2364) ([dafriend](https://github.com/dafriend)) +- Fix SMTP protocol problem [\#2362](https://github.com/codeigniter4/CodeIgniter4/pull/2362) ([jim-parry](https://github.com/jim-parry)) +- Bugfix Model after event data [\#2359](https://github.com/codeigniter4/CodeIgniter4/pull/2359) ([MGatner](https://github.com/MGatner)) +- Fix Logger config [\#2358](https://github.com/codeigniter4/CodeIgniter4/pull/2358) ([jim-parry](https://github.com/jim-parry)) +- Fix typo in comments of Services.php [\#2356](https://github.com/codeigniter4/CodeIgniter4/pull/2356) ([mladoux](https://github.com/mladoux)) +- Fix method name to 'toDateString\(\)' in Date and Times user guide [\#2352](https://github.com/codeigniter4/CodeIgniter4/pull/2352) ([musmanikram](https://github.com/musmanikram)) +- Inccorectly formated JSON response , if body is string [\#2276](https://github.com/codeigniter4/CodeIgniter4/pull/2276) ([nowackipawel](https://github.com/nowackipawel)) + +## [v4.0.0-rc.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-rc.3) (2019-10-19) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-rc.2b...v4.0.0-rc.3) + +**Fixed bugs:** + +- Route can not work: Controller or its method is not found [\#2299](https://github.com/codeigniter4/CodeIgniter4/issues/2299) +- CURLRequest - supplied argument is not a valid File-Handle resource \#Windows x64 [\#2202](https://github.com/codeigniter4/CodeIgniter4/issues/2202) +- FilterExceptions result in blank page [\#2077](https://github.com/codeigniter4/CodeIgniter4/issues/2077) + +**Closed issues:** + +- Codeigniter 4 not redirecting well. is not including the baseUrl well on redirect [\#2342](https://github.com/codeigniter4/CodeIgniter4/issues/2342) +- Session variable value set to 0 [\#2334](https://github.com/codeigniter4/CodeIgniter4/issues/2334) +- Undefined variable: errors SYSTEMPATH/Validation/Validation.php at line 651 [\#2331](https://github.com/codeigniter4/CodeIgniter4/issues/2331) +- Router Regex not working with controllers [\#2330](https://github.com/codeigniter4/CodeIgniter4/issues/2330) +- --host,--port and others are not working [\#2329](https://github.com/codeigniter4/CodeIgniter4/issues/2329) +- Type juggling can be eliminated if these three small changes are made [\#2326](https://github.com/codeigniter4/CodeIgniter4/issues/2326) +- url\_title doesn't handle diacritics [\#2323](https://github.com/codeigniter4/CodeIgniter4/issues/2323) +- View Cell Feature ? [\#2322](https://github.com/codeigniter4/CodeIgniter4/issues/2322) +- autoRoute function issue in case of sub-directory [\#2319](https://github.com/codeigniter4/CodeIgniter4/issues/2319) +- Can't store multidimensional data with Session Library [\#2309](https://github.com/codeigniter4/CodeIgniter4/issues/2309) +- Model\(\)-\>find\(\) return NULL for existing row [\#2306](https://github.com/codeigniter4/CodeIgniter4/issues/2306) +- Requesting Model::getValidationRules\(\) documentation [\#2304](https://github.com/codeigniter4/CodeIgniter4/issues/2304) +- Routes Not working [\#2301](https://github.com/codeigniter4/CodeIgniter4/issues/2301) +- ViewPath cannot be moved only another path added [\#2291](https://github.com/codeigniter4/CodeIgniter4/issues/2291) +- Version not updated? [\#2287](https://github.com/codeigniter4/CodeIgniter4/issues/2287) +- \_remap is not working [\#2277](https://github.com/codeigniter4/CodeIgniter4/issues/2277) +- Debug Toolbar error not found tpl error and fix [\#2275](https://github.com/codeigniter4/CodeIgniter4/issues/2275) +- cURL request returns 404 [\#2250](https://github.com/codeigniter4/CodeIgniter4/issues/2250) +- Problem with renaming deleted\_at column inside model [\#2248](https://github.com/codeigniter4/CodeIgniter4/issues/2248) +- App\Config\Routes loaded twice [\#2203](https://github.com/codeigniter4/CodeIgniter4/issues/2203) +- Feature idea: Model results by key [\#2167](https://github.com/codeigniter4/CodeIgniter4/issues/2167) +- Remove "separator" comment between function declarations? [\#2146](https://github.com/codeigniter4/CodeIgniter4/issues/2146) +- find\(\) is returning one character string instead of boolean [\#2096](https://github.com/codeigniter4/CodeIgniter4/issues/2096) +- Database Groups in Migrations [\#2087](https://github.com/codeigniter4/CodeIgniter4/issues/2087) +- "Cannot call session save handler in a recursive manner" [\#2056](https://github.com/codeigniter4/CodeIgniter4/issues/2056) +- Model afterInsert return originals? [\#2045](https://github.com/codeigniter4/CodeIgniter4/issues/2045) +- debug toolbar renderTimeline couses a non well formed numeric value encountered error [\#2034](https://github.com/codeigniter4/CodeIgniter4/issues/2034) +- Mysql update , affectedRows return bug [\#2003](https://github.com/codeigniter4/CodeIgniter4/issues/2003) +- Add validation on exists database before created [\#1759](https://github.com/codeigniter4/CodeIgniter4/issues/1759) + +**Merged pull requests:** + +- Prep changelog for RC.3 \[ci skip\] [\#2349](https://github.com/codeigniter4/CodeIgniter4/pull/2349) ([jim-parry](https://github.com/jim-parry)) +- CodeIgniter Foundation gets copyright \[ci skip\] [\#2348](https://github.com/codeigniter4/CodeIgniter4/pull/2348) ([jim-parry](https://github.com/jim-parry)) +- Fix FilerHandlerTest.php wierdness [\#2346](https://github.com/codeigniter4/CodeIgniter4/pull/2346) ([dafriend](https://github.com/dafriend)) +- Tests readme polish [\#2345](https://github.com/codeigniter4/CodeIgniter4/pull/2345) ([dafriend](https://github.com/dafriend)) +- Setup vs Set Up [\#2344](https://github.com/codeigniter4/CodeIgniter4/pull/2344) ([dafriend](https://github.com/dafriend)) +- User guide minor fixes. Fix class names and code area. [\#2343](https://github.com/codeigniter4/CodeIgniter4/pull/2343) ([natanfelles](https://github.com/natanfelles)) +- Simplify Validation::getErrors\(\) [\#2341](https://github.com/codeigniter4/CodeIgniter4/pull/2341) ([dafriend](https://github.com/dafriend)) +- Fix Session::get\('key'\) returns null when value is \(int\) 0 [\#2339](https://github.com/codeigniter4/CodeIgniter4/pull/2339) ([dafriend](https://github.com/dafriend)) +- Revert RedirectException change [\#2338](https://github.com/codeigniter4/CodeIgniter4/pull/2338) ([MGatner](https://github.com/MGatner)) +- \[ci skip\] Guide: Minor grammar corrections [\#2337](https://github.com/codeigniter4/CodeIgniter4/pull/2337) ([dafriend](https://github.com/dafriend)) +- Correct cleaning of namespaces in FileLocater for better Windows compatibility. See \#2203 [\#2336](https://github.com/codeigniter4/CodeIgniter4/pull/2336) ([lonnieezell](https://github.com/lonnieezell)) +- \[ci skip\] Guide: RESTful table formatting [\#2333](https://github.com/codeigniter4/CodeIgniter4/pull/2333) ([MGatner](https://github.com/MGatner)) +- Change after methods to use actual data [\#2332](https://github.com/codeigniter4/CodeIgniter4/pull/2332) ([MGatner](https://github.com/MGatner)) +- Update Application Structure [\#2328](https://github.com/codeigniter4/CodeIgniter4/pull/2328) ([kenjis](https://github.com/kenjis)) +- Correct the routing UG page [\#2327](https://github.com/codeigniter4/CodeIgniter4/pull/2327) ([jim-parry](https://github.com/jim-parry)) +- Fix bug in url\_title\(\) function with diacritics [\#2325](https://github.com/codeigniter4/CodeIgniter4/pull/2325) ([michalsn](https://github.com/michalsn)) +- Renderer Toolbar Debug Toggle [\#2324](https://github.com/codeigniter4/CodeIgniter4/pull/2324) ([MGatner](https://github.com/MGatner)) +- \[ci skip\] Update RESTful User Guide [\#2321](https://github.com/codeigniter4/CodeIgniter4/pull/2321) ([MGatner](https://github.com/MGatner)) +- Add getValidationRules\(\) to model UG page [\#2316](https://github.com/codeigniter4/CodeIgniter4/pull/2316) ([jim-parry](https://github.com/jim-parry)) +- Enhance Toolbar::renderTimeline [\#2315](https://github.com/codeigniter4/CodeIgniter4/pull/2315) ([jim-parry](https://github.com/jim-parry)) +- RESTful User Guide cleanup [\#2313](https://github.com/codeigniter4/CodeIgniter4/pull/2313) ([MGatner](https://github.com/MGatner)) +- BaseBuilder variable type fix [\#2312](https://github.com/codeigniter4/CodeIgniter4/pull/2312) ([TysiacSzescset](https://github.com/TysiacSzescset)) +- Convert all language returns to single quote [\#2311](https://github.com/codeigniter4/CodeIgniter4/pull/2311) ([MGatner](https://github.com/MGatner)) +- Bugfix extra autoroute slashes [\#2308](https://github.com/codeigniter4/CodeIgniter4/pull/2308) ([MGatner](https://github.com/MGatner)) +- Resolve session save handler issue [\#2307](https://github.com/codeigniter4/CodeIgniter4/pull/2307) ([jim-parry](https://github.com/jim-parry)) +- Fix curl debug bug [\#2305](https://github.com/codeigniter4/CodeIgniter4/pull/2305) ([michalsn](https://github.com/michalsn)) +- Use DBGroup variable from migration class if defined [\#2303](https://github.com/codeigniter4/CodeIgniter4/pull/2303) ([michalsn](https://github.com/michalsn)) +- Fix MySql \_fromTables\(\) [\#2302](https://github.com/codeigniter4/CodeIgniter4/pull/2302) ([pjsde](https://github.com/pjsde)) +- \[ci skip\] Routes collector for toolbar should not die when a method name is calculated through \_remap [\#2300](https://github.com/codeigniter4/CodeIgniter4/pull/2300) ([lonnieezell](https://github.com/lonnieezell)) +- fix issue on session\_regenerate. [\#2298](https://github.com/codeigniter4/CodeIgniter4/pull/2298) ([pjsde](https://github.com/pjsde)) +- Add counted\(\) to Inflector Helper [\#2296](https://github.com/codeigniter4/CodeIgniter4/pull/2296) ([MGatner](https://github.com/MGatner)) +- Test set\(\) method in Builder class more [\#2295](https://github.com/codeigniter4/CodeIgniter4/pull/2295) ([michalsn](https://github.com/michalsn)) +- Fix Code Modules documentation for psr4 namespace configuration [\#2290](https://github.com/codeigniter4/CodeIgniter4/pull/2290) ([romaven](https://github.com/romaven)) +- Don't restrict model's access to properties in a read-only manner [\#2289](https://github.com/codeigniter4/CodeIgniter4/pull/2289) ([lonnieezell](https://github.com/lonnieezell)) +- Fix line numbering in Debug/Exceptions class [\#2288](https://github.com/codeigniter4/CodeIgniter4/pull/2288) ([michalsn](https://github.com/michalsn)) +- Fix error with Host header for CURLRequest class [\#2285](https://github.com/codeigniter4/CodeIgniter4/pull/2285) ([michalsn](https://github.com/michalsn)) +- Fix getErrors\(\) for validation with redirect [\#2284](https://github.com/codeigniter4/CodeIgniter4/pull/2284) ([michalsn](https://github.com/michalsn)) +- Rename collectors \_\*.tpl.php to \_\*.tpl [\#2283](https://github.com/codeigniter4/CodeIgniter4/pull/2283) ([MGatner](https://github.com/MGatner)) +- Bug in CSRF parameter cleanup [\#2279](https://github.com/codeigniter4/CodeIgniter4/pull/2279) ([michalsn](https://github.com/michalsn)) +- WIP fix store\(\) default value bug [\#2123](https://github.com/codeigniter4/CodeIgniter4/pull/2123) ([s-proj](https://github.com/s-proj)) +- WIP Added validation on exists database before created for MySQLi… [\#2100](https://github.com/codeigniter4/CodeIgniter4/pull/2100) ([oleg1540](https://github.com/oleg1540)) + +## [v4.0.0-rc.2b](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-rc.2b) (2019-09-28) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-rc.2.1...v4.0.0-rc.2b) + +**Merged pull requests:** + +- Fix user guide for Message class [\#2282](https://github.com/codeigniter4/CodeIgniter4/pull/2282) ([michalsn](https://github.com/michalsn)) +- Handle X-CSRF-TOKEN - CSRF [\#2272](https://github.com/codeigniter4/CodeIgniter4/pull/2272) ([nowackipawel](https://github.com/nowackipawel)) +- QUICKFIX Batch Update Where Reset [\#2252](https://github.com/codeigniter4/CodeIgniter4/pull/2252) ([searchy2](https://github.com/searchy2)) + +## [v4.0.0-rc.2.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-rc.2.1) (2019-09-28) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-rc.2...v4.0.0-rc.2.1) + +**Closed issues:** + +- listTables\(\) failing to use correct prefix [\#2210](https://github.com/codeigniter4/CodeIgniter4/issues/2210) +- Query Builder Class documentation [\#2140](https://github.com/codeigniter4/CodeIgniter4/issues/2140) + +## [v4.0.0-rc.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-rc.2) (2019-09-27) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-rc.1...v4.0.0-rc.2) + +**Fixed bugs:** + +- Add magic \_\_isset to classes with \_\_get [\#2219](https://github.com/codeigniter4/CodeIgniter4/issues/2219) +- CIDatabaseTestCase double-deletes tables [\#2206](https://github.com/codeigniter4/CodeIgniter4/issues/2206) +- Locals problems [\#2195](https://github.com/codeigniter4/CodeIgniter4/issues/2195) +- Translations are not loaded from composer repository [\#2120](https://github.com/codeigniter4/CodeIgniter4/issues/2120) + +**Closed issues:** + +- Query grouping not working for HAVING clause [\#2247](https://github.com/codeigniter4/CodeIgniter4/issues/2247) +- $builder-\>like\(\) not support for HAVING clause [\#2242](https://github.com/codeigniter4/CodeIgniter4/issues/2242) +- invalid switch parameter [\#2239](https://github.com/codeigniter4/CodeIgniter4/issues/2239) +- DateTime::createFromFormat\(\) parameter incorrect [\#2238](https://github.com/codeigniter4/CodeIgniter4/issues/2238) +- strlen\(\) expects parameter string, integer given [\#2237](https://github.com/codeigniter4/CodeIgniter4/issues/2237) +- ini\_set\(\) expects parameter 2 to be string, integer given [\#2236](https://github.com/codeigniter4/CodeIgniter4/issues/2236) +- ini\_set\(\) expects parameter 2 to be string, integer given [\#2235](https://github.com/codeigniter4/CodeIgniter4/issues/2235) +- \# ini\_set\(\) expects parameter 2 to be string, integer given [\#2234](https://github.com/codeigniter4/CodeIgniter4/issues/2234) +- Extending The Model [\#2223](https://github.com/codeigniter4/CodeIgniter4/issues/2223) +- BUG curl\_setopt\_array\(\): supplied argument is not a valid file-handle resource [\#2222](https://github.com/codeigniter4/CodeIgniter4/issues/2222) +- How do I dynamically modify the configuration? [\#2214](https://github.com/codeigniter4/CodeIgniter4/issues/2214) +- Document the "whoops" error page [\#2198](https://github.com/codeigniter4/CodeIgniter4/issues/2198) +- Fail to open system/bootstrap.php [\#2193](https://github.com/codeigniter4/CodeIgniter4/issues/2193) +- Function lang\(\) / Type of return value [\#2192](https://github.com/codeigniter4/CodeIgniter4/issues/2192) +- Can we use codeigniter 4 on live server?? [\#2188](https://github.com/codeigniter4/CodeIgniter4/issues/2188) +- Custom query in model CI4 [\#2187](https://github.com/codeigniter4/CodeIgniter4/issues/2187) +- conflict between php zlib.output\_compression and output buffering [\#2182](https://github.com/codeigniter4/CodeIgniter4/issues/2182) +- API Trait documentation fix - failValidationError [\#2176](https://github.com/codeigniter4/CodeIgniter4/issues/2176) +- Validation issue on multiple file upload [\#2175](https://github.com/codeigniter4/CodeIgniter4/issues/2175) +- exif\_read\_data [\#2161](https://github.com/codeigniter4/CodeIgniter4/issues/2161) +- Database count methods [\#2159](https://github.com/codeigniter4/CodeIgniter4/issues/2159) +- Devstarter $salt [\#2156](https://github.com/codeigniter4/CodeIgniter4/issues/2156) +- Migration migrate, rollback and create problems [\#2147](https://github.com/codeigniter4/CodeIgniter4/issues/2147) +- Query Builder getWhere Crash [\#2143](https://github.com/codeigniter4/CodeIgniter4/issues/2143) +- View: $parser-\>render\(\); [\#2086](https://github.com/codeigniter4/CodeIgniter4/issues/2086) +- Return value of lang\(\) must be of the type string, array returned [\#2075](https://github.com/codeigniter4/CodeIgniter4/issues/2075) +- Wrong links for pager [\#2016](https://github.com/codeigniter4/CodeIgniter4/issues/2016) +- base\_url\(\) value dropped between namespaces [\#1942](https://github.com/codeigniter4/CodeIgniter4/issues/1942) +- Unable to use \_remap without default method in controller [\#1928](https://github.com/codeigniter4/CodeIgniter4/issues/1928) +- RESTful resources [\#1765](https://github.com/codeigniter4/CodeIgniter4/issues/1765) + +**Merged pull requests:** + +- Fix changelog \[ci skip\] [\#2273](https://github.com/codeigniter4/CodeIgniter4/pull/2273) ([jim-parry](https://github.com/jim-parry)) +- fix ResourcePresenter::setModel\(\) [\#2271](https://github.com/codeigniter4/CodeIgniter4/pull/2271) ([pjsde](https://github.com/pjsde)) +- groupStart\(\) refactorization [\#2270](https://github.com/codeigniter4/CodeIgniter4/pull/2270) ([michalsn](https://github.com/michalsn)) +- testMode\(\) method for BaseBuilder [\#2269](https://github.com/codeigniter4/CodeIgniter4/pull/2269) ([michalsn](https://github.com/michalsn)) +- Validation session use only if exists [\#2268](https://github.com/codeigniter4/CodeIgniter4/pull/2268) ([jim-parry](https://github.com/jim-parry)) +- Tests setUp and tearDown: void [\#2267](https://github.com/codeigniter4/CodeIgniter4/pull/2267) ([MGatner](https://github.com/MGatner)) +- RC.2 release prep [\#2266](https://github.com/codeigniter4/CodeIgniter4/pull/2266) ([jim-parry](https://github.com/jim-parry)) +- Fix a validation issue on multiple file upload [\#2265](https://github.com/codeigniter4/CodeIgniter4/pull/2265) ([pjsde](https://github.com/pjsde)) +- fix. Parser allow other extension [\#2264](https://github.com/codeigniter4/CodeIgniter4/pull/2264) ([Instrye](https://github.com/Instrye)) +- Fix parameter type in Debug/Exceptions [\#2262](https://github.com/codeigniter4/CodeIgniter4/pull/2262) ([jim-parry](https://github.com/jim-parry)) +- Fix lang\(\) signature [\#2261](https://github.com/codeigniter4/CodeIgniter4/pull/2261) ([jim-parry](https://github.com/jim-parry)) +- Explain the whoops page [\#2260](https://github.com/codeigniter4/CodeIgniter4/pull/2260) ([jim-parry](https://github.com/jim-parry)) +- Add URI & url\_helper tests [\#2259](https://github.com/codeigniter4/CodeIgniter4/pull/2259) ([jim-parry](https://github.com/jim-parry)) +- Several updates to the HAVING clauses [\#2257](https://github.com/codeigniter4/CodeIgniter4/pull/2257) ([michalsn](https://github.com/michalsn)) +- Fix invalid parameters [\#2253](https://github.com/codeigniter4/CodeIgniter4/pull/2253) ([pjsde](https://github.com/pjsde)) +- EXIF not supported for GIF [\#2246](https://github.com/codeigniter4/CodeIgniter4/pull/2246) ([jim-parry](https://github.com/jim-parry)) +- Fix class ref parameter types [\#2245](https://github.com/codeigniter4/CodeIgniter4/pull/2245) ([jim-parry](https://github.com/jim-parry)) +- Fix ini\_set parameter type [\#2241](https://github.com/codeigniter4/CodeIgniter4/pull/2241) ([jim-parry](https://github.com/jim-parry)) +- Handle JSON POSTs in CSRF [\#2240](https://github.com/codeigniter4/CodeIgniter4/pull/2240) ([nowackipawel](https://github.com/nowackipawel)) +- Fixes BaseBuilder getWhere\(\) bug [\#2232](https://github.com/codeigniter4/CodeIgniter4/pull/2232) ([michalsn](https://github.com/michalsn)) +- Add magic \_\_isset to classes with \_\_get [\#2231](https://github.com/codeigniter4/CodeIgniter4/pull/2231) ([MGatner](https://github.com/MGatner)) +- Add escape to SQLite \_listTables\(\) [\#2230](https://github.com/codeigniter4/CodeIgniter4/pull/2230) ([MGatner](https://github.com/MGatner)) +- MySQLi escapeLikeStringDirect\(\) [\#2229](https://github.com/codeigniter4/CodeIgniter4/pull/2229) ([MGatner](https://github.com/MGatner)) +- Exclude `sqlite\_%` from listTables\(\) [\#2228](https://github.com/codeigniter4/CodeIgniter4/pull/2228) ([MGatner](https://github.com/MGatner)) +- fix. CONTRIBUTING.md link [\#2226](https://github.com/codeigniter4/CodeIgniter4/pull/2226) ([Instrye](https://github.com/Instrye)) +- \[ci skip\] Fix malformed table in view\_parser.rst [\#2225](https://github.com/codeigniter4/CodeIgniter4/pull/2225) ([jim-parry](https://github.com/jim-parry)) +- change new \Config\Database\(\) to config\('Database'\) [\#2224](https://github.com/codeigniter4/CodeIgniter4/pull/2224) ([techoner](https://github.com/techoner)) +- Documentation fixes [\#2221](https://github.com/codeigniter4/CodeIgniter4/pull/2221) ([najdanovicivan](https://github.com/najdanovicivan)) +- Typo corrected [\#2218](https://github.com/codeigniter4/CodeIgniter4/pull/2218) ([dangereyes88](https://github.com/dangereyes88)) +- Update uri.rst [\#2216](https://github.com/codeigniter4/CodeIgniter4/pull/2216) ([dangereyes88](https://github.com/dangereyes88)) +- Filter listTables cache response on constrainPrefix [\#2213](https://github.com/codeigniter4/CodeIgniter4/pull/2213) ([MGatner](https://github.com/MGatner)) +- Add listTable\(\) tests [\#2211](https://github.com/codeigniter4/CodeIgniter4/pull/2211) ([MGatner](https://github.com/MGatner)) +- Add trace\(\) [\#2209](https://github.com/codeigniter4/CodeIgniter4/pull/2209) ([MGatner](https://github.com/MGatner)) +- Add $db-\>getPrefix\(\) [\#2208](https://github.com/codeigniter4/CodeIgniter4/pull/2208) ([MGatner](https://github.com/MGatner)) +- Fix empty\(\) bug on DBPrefix [\#2205](https://github.com/codeigniter4/CodeIgniter4/pull/2205) ([MGatner](https://github.com/MGatner)) +- Foreign key columns [\#2201](https://github.com/codeigniter4/CodeIgniter4/pull/2201) ([MGatner](https://github.com/MGatner)) +- Notify Kint of dd alias [\#2200](https://github.com/codeigniter4/CodeIgniter4/pull/2200) ([MGatner](https://github.com/MGatner)) +- Add getForeignKeyData to User Guide [\#2199](https://github.com/codeigniter4/CodeIgniter4/pull/2199) ([MGatner](https://github.com/MGatner)) +- Update Session.php [\#2197](https://github.com/codeigniter4/CodeIgniter4/pull/2197) ([cstechsandesh](https://github.com/cstechsandesh)) +- Migration rollback reverse [\#2191](https://github.com/codeigniter4/CodeIgniter4/pull/2191) ([MGatner](https://github.com/MGatner)) +- \[ci skip\] Fix name of ForeignKeyChecks [\#2190](https://github.com/codeigniter4/CodeIgniter4/pull/2190) ([MGatner](https://github.com/MGatner)) +- missing return [\#2189](https://github.com/codeigniter4/CodeIgniter4/pull/2189) ([titounnes](https://github.com/titounnes)) +- Fix case on "Seeds/" directory [\#2184](https://github.com/codeigniter4/CodeIgniter4/pull/2184) ([MGatner](https://github.com/MGatner)) +- Check `defined` for constants [\#2183](https://github.com/codeigniter4/CodeIgniter4/pull/2183) ([MGatner](https://github.com/MGatner)) +- Remove copy-paste extraneous text [\#2181](https://github.com/codeigniter4/CodeIgniter4/pull/2181) ([MGatner](https://github.com/MGatner)) +- Fix \_fromTables\(\) [\#2174](https://github.com/codeigniter4/CodeIgniter4/pull/2174) ([pjsde](https://github.com/pjsde)) +- Fix for CURL for 'debug' option [\#2168](https://github.com/codeigniter4/CodeIgniter4/pull/2168) ([MGatner](https://github.com/MGatner)) + +## [v4.0.0-rc.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-rc.1) (2019-09-03) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-beta.4...v4.0.0-rc.1) + +**Implemented enhancements:** + +- BaseConfig should support array values with dot syntax [\#454](https://github.com/codeigniter4/CodeIgniter4/issues/454) + +**Closed issues:** + +- \[internal function\]: CodeIgniter\Debug\Exceptions-\>shutdownHandler\(\) [\#2173](https://github.com/codeigniter4/CodeIgniter4/issues/2173) +- Message-\>setHeader allowing duplicates [\#2170](https://github.com/codeigniter4/CodeIgniter4/issues/2170) +- CLI: Exit status [\#2163](https://github.com/codeigniter4/CodeIgniter4/issues/2163) +- QB countAllResults shouldn't use LIMIT settings [\#2152](https://github.com/codeigniter4/CodeIgniter4/issues/2152) +- BaseBuilder::get\(\) resets query even if reset = false [\#2141](https://github.com/codeigniter4/CodeIgniter4/issues/2141) +- Some migrations not running [\#2139](https://github.com/codeigniter4/CodeIgniter4/issues/2139) +- Migrations Refactor Namespaces [\#2138](https://github.com/codeigniter4/CodeIgniter4/issues/2138) +- $primaryKey forcefully 'needs' to be auto\_increment [\#2133](https://github.com/codeigniter4/CodeIgniter4/issues/2133) +- response data not set [\#2124](https://github.com/codeigniter4/CodeIgniter4/issues/2124) +- RESTful behaviour [\#2122](https://github.com/codeigniter4/CodeIgniter4/issues/2122) +- Redis [\#2121](https://github.com/codeigniter4/CodeIgniter4/issues/2121) +- Toolbar download bug [\#2117](https://github.com/codeigniter4/CodeIgniter4/issues/2117) +- Packagist not updated with latest release? [\#2115](https://github.com/codeigniter4/CodeIgniter4/issues/2115) +- Fatal error Installing using composer [\#2114](https://github.com/codeigniter4/CodeIgniter4/issues/2114) +- Allow loading Common.php function overrides [\#2101](https://github.com/codeigniter4/CodeIgniter4/issues/2101) +- Result from database was auto encoded when using Entity [\#2088](https://github.com/codeigniter4/CodeIgniter4/issues/2088) +- Honeypot does not close the form [\#2084](https://github.com/codeigniter4/CodeIgniter4/issues/2084) +- Imagick Image library handler return array instead of boolean [\#2029](https://github.com/codeigniter4/CodeIgniter4/issues/2029) +- Migrations command should use the UTC datetime when creating new migrations [\#2018](https://github.com/codeigniter4/CodeIgniter4/issues/2018) +- FileLocator-\>getNamespaces with parameter [\#1866](https://github.com/codeigniter4/CodeIgniter4/issues/1866) + +**Merged pull requests:** + +- Fix query builder user guide page [\#2180](https://github.com/codeigniter4/CodeIgniter4/pull/2180) ([jim-parry](https://github.com/jim-parry)) +- RC.1 prep [\#2179](https://github.com/codeigniter4/CodeIgniter4/pull/2179) ([jim-parry](https://github.com/jim-parry)) +- Add fallback for missing finfo\_open [\#2178](https://github.com/codeigniter4/CodeIgniter4/pull/2178) ([MGatner](https://github.com/MGatner)) +- Fix missing form close tag [\#2177](https://github.com/codeigniter4/CodeIgniter4/pull/2177) ([jim-parry](https://github.com/jim-parry)) +- Base FeatureTestCase on CIUnitTestCase [\#2172](https://github.com/codeigniter4/CodeIgniter4/pull/2172) ([jim-parry](https://github.com/jim-parry)) +- Setheader dupes [\#2171](https://github.com/codeigniter4/CodeIgniter4/pull/2171) ([MGatner](https://github.com/MGatner)) +- Add $quality usage for Image Library [\#2169](https://github.com/codeigniter4/CodeIgniter4/pull/2169) ([MGatner](https://github.com/MGatner)) +- Cookie error [\#2166](https://github.com/codeigniter4/CodeIgniter4/pull/2166) ([pjsde](https://github.com/pjsde)) +- RESTful help [\#2165](https://github.com/codeigniter4/CodeIgniter4/pull/2165) ([jim-parry](https://github.com/jim-parry)) +- Exit error code on CLI Command failure [\#2164](https://github.com/codeigniter4/CodeIgniter4/pull/2164) ([MGatner](https://github.com/MGatner)) +- User Guide updates for Common.php [\#2162](https://github.com/codeigniter4/CodeIgniter4/pull/2162) ([MGatner](https://github.com/MGatner)) +- Add BaseBuilder SelectCount [\#2160](https://github.com/codeigniter4/CodeIgniter4/pull/2160) ([MGatner](https://github.com/MGatner)) +- Update migrations config [\#2157](https://github.com/codeigniter4/CodeIgniter4/pull/2157) ([jim-parry](https://github.com/jim-parry)) +- Include .gitignore in starters [\#2155](https://github.com/codeigniter4/CodeIgniter4/pull/2155) ([MGatner](https://github.com/MGatner)) +- Fix email & migrations docs; update changelog [\#2154](https://github.com/codeigniter4/CodeIgniter4/pull/2154) ([jim-parry](https://github.com/jim-parry)) +- Bug fix countAllResults with LIMIT [\#2153](https://github.com/codeigniter4/CodeIgniter4/pull/2153) ([tangix](https://github.com/tangix)) +- ImageMagick-\>save\(\) return value [\#2151](https://github.com/codeigniter4/CodeIgniter4/pull/2151) ([MGatner](https://github.com/MGatner)) +- New logic for Image-\>fit\(\) [\#2150](https://github.com/codeigniter4/CodeIgniter4/pull/2150) ([MGatner](https://github.com/MGatner)) +- listNamespaceFiles: Ensure trailing slash [\#2149](https://github.com/codeigniter4/CodeIgniter4/pull/2149) ([MGatner](https://github.com/MGatner)) +- Remove UserModel reference from Home controller [\#2145](https://github.com/codeigniter4/CodeIgniter4/pull/2145) ([andreportaro](https://github.com/andreportaro)) +- Update Redis legacy function [\#2144](https://github.com/codeigniter4/CodeIgniter4/pull/2144) ([MGatner](https://github.com/MGatner)) +- Fixing BuilderBase resetting when getting the SQL [\#2142](https://github.com/codeigniter4/CodeIgniter4/pull/2142) ([tangix](https://github.com/tangix)) +- New Migration Logic [\#2137](https://github.com/codeigniter4/CodeIgniter4/pull/2137) ([MGatner](https://github.com/MGatner)) +- Migrations user guide fixes [\#2136](https://github.com/codeigniter4/CodeIgniter4/pull/2136) ([MGatner](https://github.com/MGatner)) +- Encryption [\#2135](https://github.com/codeigniter4/CodeIgniter4/pull/2135) ([jim-parry](https://github.com/jim-parry)) +- Fix localization writeup [\#2134](https://github.com/codeigniter4/CodeIgniter4/pull/2134) ([jim-parry](https://github.com/jim-parry)) +- Update migration User Guide [\#2132](https://github.com/codeigniter4/CodeIgniter4/pull/2132) ([MGatner](https://github.com/MGatner)) +- Added No Content response to API\ResponseTrait [\#2131](https://github.com/codeigniter4/CodeIgniter4/pull/2131) ([tangix](https://github.com/tangix)) +- Add setFileName\(\) to DownloadResponse [\#2129](https://github.com/codeigniter4/CodeIgniter4/pull/2129) ([MGatner](https://github.com/MGatner)) +- guessExtension fallback to clientExtension [\#2128](https://github.com/codeigniter4/CodeIgniter4/pull/2128) ([MGatner](https://github.com/MGatner)) +- Update limit function since $offset is nullable [\#2127](https://github.com/codeigniter4/CodeIgniter4/pull/2127) ([vibbow](https://github.com/vibbow)) +- Limit storePreviousURL to certain requests [\#2126](https://github.com/codeigniter4/CodeIgniter4/pull/2126) ([MGatner](https://github.com/MGatner)) +- Updated redis session handler to support redis 5.0.x [\#2125](https://github.com/codeigniter4/CodeIgniter4/pull/2125) ([tangix](https://github.com/tangix)) +- Disabled Toolbar on downloads [\#2118](https://github.com/codeigniter4/CodeIgniter4/pull/2118) ([MGatner](https://github.com/MGatner)) +- Add Image-\>convert\(\) [\#2113](https://github.com/codeigniter4/CodeIgniter4/pull/2113) ([MGatner](https://github.com/MGatner)) +- Update `Entity.php` `\_\_isset` method [\#2112](https://github.com/codeigniter4/CodeIgniter4/pull/2112) ([vibbow](https://github.com/vibbow)) +- Added app/Common.php [\#2110](https://github.com/codeigniter4/CodeIgniter4/pull/2110) ([jason-napolitano](https://github.com/jason-napolitano)) +- Fix typo in checking if exists db\_connect\(\) [\#2109](https://github.com/codeigniter4/CodeIgniter4/pull/2109) ([xbotkaj](https://github.com/xbotkaj)) +- Original email port [\#2092](https://github.com/codeigniter4/CodeIgniter4/pull/2092) ([jim-parry](https://github.com/jim-parry)) +- Fix prevent soft delete all without conditions set [\#2090](https://github.com/codeigniter4/CodeIgniter4/pull/2090) ([rino7](https://github.com/rino7)) +- Update BaseConfig.php [\#2082](https://github.com/codeigniter4/CodeIgniter4/pull/2082) ([zl59503020](https://github.com/zl59503020)) +- WIP: Migration updates for more wholistic functionality [\#2065](https://github.com/codeigniter4/CodeIgniter4/pull/2065) ([lonnieezell](https://github.com/lonnieezell)) +- clean base controller code [\#2046](https://github.com/codeigniter4/CodeIgniter4/pull/2046) ([behnampro](https://github.com/behnampro)) +- Fix CSRF hash regeneration [\#2027](https://github.com/codeigniter4/CodeIgniter4/pull/2027) ([Workoverflow](https://github.com/Workoverflow)) +- WIP Verbiage revisions [\#2010](https://github.com/codeigniter4/CodeIgniter4/pull/2010) ([kydojo](https://github.com/kydojo)) +- Subqueries in BaseBuilder [\#2001](https://github.com/codeigniter4/CodeIgniter4/pull/2001) ([iRedds](https://github.com/iRedds)) + +## [v4.0.0-beta.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-beta.4) (2019-07-25) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-beta.3...v4.0.0-beta.4) + +**Fixed bugs:** + +- Controller filters match too loosely. [\#2038](https://github.com/codeigniter4/CodeIgniter4/issues/2038) + +**Closed issues:** + +- File-\>getDestination fails without extension [\#2103](https://github.com/codeigniter4/CodeIgniter4/issues/2103) +- User Guide: UploadedFile Class [\#2102](https://github.com/codeigniter4/CodeIgniter4/issues/2102) +- Worries about postgresql errors [\#2097](https://github.com/codeigniter4/CodeIgniter4/issues/2097) +- README.md - Link to Announcement on Forums is a 404? [\#2094](https://github.com/codeigniter4/CodeIgniter4/issues/2094) +- Entity castAsJson returns an empty array [\#2080](https://github.com/codeigniter4/CodeIgniter4/issues/2080) +- Migrations Sequential field information is required [\#2076](https://github.com/codeigniter4/CodeIgniter4/issues/2076) +- function gussExtension return wrong result, return csv instead of right answer "txt" or "text" [\#2066](https://github.com/codeigniter4/CodeIgniter4/issues/2066) +- Unexpected empty "query" property when returning CodeIgniter\HTTP\URI [\#2062](https://github.com/codeigniter4/CodeIgniter4/issues/2062) +- Multiple rules for file upload always return false [\#2061](https://github.com/codeigniter4/CodeIgniter4/issues/2061) +- The assets of the public\_folder are not loaded [\#2047](https://github.com/codeigniter4/CodeIgniter4/issues/2047) +- Modify Model's deleted field to be a date [\#2041](https://github.com/codeigniter4/CodeIgniter4/issues/2041) +- Filter Config not quite working with Routes? [\#2037](https://github.com/codeigniter4/CodeIgniter4/issues/2037) +- force\_https\(\) doesn't redirect [\#2033](https://github.com/codeigniter4/CodeIgniter4/issues/2033) +- URI segments passed in as method parameters skips segments with value as 0 \(zero\) [\#2032](https://github.com/codeigniter4/CodeIgniter4/issues/2032) +- /System/Debug/Toolbar/Collectors/Routes.php on line 83 [\#2028](https://github.com/codeigniter4/CodeIgniter4/issues/2028) +- php spark not working [\#2025](https://github.com/codeigniter4/CodeIgniter4/issues/2025) +- PR\#2012 caused 404 exception in spark [\#2021](https://github.com/codeigniter4/CodeIgniter4/issues/2021) +- Cache config [\#2017](https://github.com/codeigniter4/CodeIgniter4/issues/2017) +- CodeIgniter\Entity Setter doesn't work [\#2013](https://github.com/codeigniter4/CodeIgniter4/issues/2013) +- validation match\[x\] don't work anymore... if custom setter is used. [\#2006](https://github.com/codeigniter4/CodeIgniter4/issues/2006) +- Paths issue when moving Views outside of app folder [\#1998](https://github.com/codeigniter4/CodeIgniter4/issues/1998) +- View Parser Register Plugins as closures not works! [\#1997](https://github.com/codeigniter4/CodeIgniter4/issues/1997) +- View Parser site\_url not works? [\#1995](https://github.com/codeigniter4/CodeIgniter4/issues/1995) +- CURLRequest not respecting debug flag [\#1994](https://github.com/codeigniter4/CodeIgniter4/issues/1994) +- Entity null values cause database error [\#1992](https://github.com/codeigniter4/CodeIgniter4/issues/1992) +- SQLite driver throws exception when using dropForeignKey [\#1982](https://github.com/codeigniter4/CodeIgniter4/issues/1982) +- Security: DotEnv loads DB password plaintext in $\_SERVER [\#1969](https://github.com/codeigniter4/CodeIgniter4/issues/1969) +- Feature: FK Constraint Enable/Disable [\#1964](https://github.com/codeigniter4/CodeIgniter4/issues/1964) +- redirect\($namedRoute\) missing helpful exception [\#1953](https://github.com/codeigniter4/CodeIgniter4/issues/1953) + +**Merged pull requests:** + +- Update the starters [\#2108](https://github.com/codeigniter4/CodeIgniter4/pull/2108) ([jim-parry](https://github.com/jim-parry)) +- Prep for beta.4 [\#2107](https://github.com/codeigniter4/CodeIgniter4/pull/2107) ([jim-parry](https://github.com/jim-parry)) +- File & UploadFile Fixes [\#2104](https://github.com/codeigniter4/CodeIgniter4/pull/2104) ([MGatner](https://github.com/MGatner)) +- Timezone select [\#2091](https://github.com/codeigniter4/CodeIgniter4/pull/2091) ([MGatner](https://github.com/MGatner)) +- JSON format checking improved [\#2081](https://github.com/codeigniter4/CodeIgniter4/pull/2081) ([nowackipawel](https://github.com/nowackipawel)) +- Update config\(\) to check all namespaces [\#2079](https://github.com/codeigniter4/CodeIgniter4/pull/2079) ([MGatner](https://github.com/MGatner)) +- Throttler can access bucket for bucket life time [\#2074](https://github.com/codeigniter4/CodeIgniter4/pull/2074) ([MohKari](https://github.com/MohKari)) +- Fix autoloader.rst formatting [\#2071](https://github.com/codeigniter4/CodeIgniter4/pull/2071) ([jim-parry](https://github.com/jim-parry)) +- validation rule: then -\> than \(spelling\) [\#2069](https://github.com/codeigniter4/CodeIgniter4/pull/2069) ([nowackipawel](https://github.com/nowackipawel)) +- Bugfix file locator slash error [\#2064](https://github.com/codeigniter4/CodeIgniter4/pull/2064) ([MGatner](https://github.com/MGatner)) +- Ensure query vars are part of request-\>uri. Fixes \#2062 [\#2063](https://github.com/codeigniter4/CodeIgniter4/pull/2063) ([lonnieezell](https://github.com/lonnieezell)) +- Cache Drive Backups [\#2060](https://github.com/codeigniter4/CodeIgniter4/pull/2060) ([MohKari](https://github.com/MohKari)) +- Add multi-path support to `locateFile\(\)` [\#2059](https://github.com/codeigniter4/CodeIgniter4/pull/2059) ([MGatner](https://github.com/MGatner)) +- Add model exceptions for missing/invalid dateFormat [\#2054](https://github.com/codeigniter4/CodeIgniter4/pull/2054) ([MGatner](https://github.com/MGatner)) +- Change Model's deleted flag to a deleted\_at datetime/timestamp. Fixes \#2041 [\#2053](https://github.com/codeigniter4/CodeIgniter4/pull/2053) ([lonnieezell](https://github.com/lonnieezell)) +- Add various tests for \(not\) null [\#2052](https://github.com/codeigniter4/CodeIgniter4/pull/2052) ([MGatner](https://github.com/MGatner)) +- Soft deletes use deleted\_at [\#2051](https://github.com/codeigniter4/CodeIgniter4/pull/2051) ([MGatner](https://github.com/MGatner)) +- Stash insert ID before event trigger [\#2050](https://github.com/codeigniter4/CodeIgniter4/pull/2050) ([MGatner](https://github.com/MGatner)) +- Zero params should be passed through when routing. Fixes \#2032 [\#2043](https://github.com/codeigniter4/CodeIgniter4/pull/2043) ([lonnieezell](https://github.com/lonnieezell)) +- SQLite3 now supports dropping foreign keys. Fixes \#1982 [\#2042](https://github.com/codeigniter4/CodeIgniter4/pull/2042) ([lonnieezell](https://github.com/lonnieezell)) +- Update CURLRequest.php [\#2040](https://github.com/codeigniter4/CodeIgniter4/pull/2040) ([nowackipawel](https://github.com/nowackipawel)) +- Restrict filter matching of uris so they require an exact match. Fixes \#2038 [\#2039](https://github.com/codeigniter4/CodeIgniter4/pull/2039) ([lonnieezell](https://github.com/lonnieezell)) +- Make `force\_https\(\)` send headers before exit [\#2036](https://github.com/codeigniter4/CodeIgniter4/pull/2036) ([MGatner](https://github.com/MGatner)) +- Various typos and Guide corrections [\#2035](https://github.com/codeigniter4/CodeIgniter4/pull/2035) ([MGatner](https://github.com/MGatner)) +- Fallback to server request for default method [\#2031](https://github.com/codeigniter4/CodeIgniter4/pull/2031) ([MGatner](https://github.com/MGatner)) +- Support the new `router` service in Debug Toolbar [\#2030](https://github.com/codeigniter4/CodeIgniter4/pull/2030) ([MGatner](https://github.com/MGatner)) +- Extension Pager::makeLinks \(optional grup name\) [\#2026](https://github.com/codeigniter4/CodeIgniter4/pull/2026) ([nowackipawel](https://github.com/nowackipawel)) +- Refactor the way the router and route collection determine the current HTTP verb. [\#2024](https://github.com/codeigniter4/CodeIgniter4/pull/2024) ([lonnieezell](https://github.com/lonnieezell)) +- SQLite and Mysql driver additional tests and migration runner test fixes [\#2019](https://github.com/codeigniter4/CodeIgniter4/pull/2019) ([lonnieezell](https://github.com/lonnieezell)) +- Direct user to follow the upgrade steps after installation [\#2015](https://github.com/codeigniter4/CodeIgniter4/pull/2015) ([agmckee](https://github.com/agmckee)) +- Added a new Session/ArrayHandler that can be used during testing. [\#2014](https://github.com/codeigniter4/CodeIgniter4/pull/2014) ([lonnieezell](https://github.com/lonnieezell)) +- Use request-\>method for HTTP verb [\#2012](https://github.com/codeigniter4/CodeIgniter4/pull/2012) ([MGatner](https://github.com/MGatner)) +- Set the raw data array without any mutations for the Entity [\#2011](https://github.com/codeigniter4/CodeIgniter4/pull/2011) ([iRedds](https://github.com/iRedds)) +- Add `patch` method to command "routes" [\#2008](https://github.com/codeigniter4/CodeIgniter4/pull/2008) ([MGatner](https://github.com/MGatner)) +- Plugin closures docs update and test [\#2005](https://github.com/codeigniter4/CodeIgniter4/pull/2005) ([lonnieezell](https://github.com/lonnieezell)) +- Allow hasChanged\(\) without parameter [\#2004](https://github.com/codeigniter4/CodeIgniter4/pull/2004) ([MGatner](https://github.com/MGatner)) +- Entity refactor [\#2002](https://github.com/codeigniter4/CodeIgniter4/pull/2002) ([lonnieezell](https://github.com/lonnieezell)) +- use CodeIgniter\Controller; not needed since Home Controller extends … [\#1999](https://github.com/codeigniter4/CodeIgniter4/pull/1999) ([titounnes](https://github.com/titounnes)) +- Attempting to fix CURLRequest debug issue. \#1994 [\#1996](https://github.com/codeigniter4/CodeIgniter4/pull/1996) ([lonnieezell](https://github.com/lonnieezell)) +- argument set\(\) must by type of string - cannot agree [\#1989](https://github.com/codeigniter4/CodeIgniter4/pull/1989) ([nowackipawel](https://github.com/nowackipawel)) +- Prevent reverseRoute from searching closures [\#1959](https://github.com/codeigniter4/CodeIgniter4/pull/1959) ([MGatner](https://github.com/MGatner)) + +## [v4.0.0-beta.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-beta.3) (2019-05-06) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-beta.1...v4.0.0-beta.3) + +**Fixed bugs:** + +- travis-ci build broken [\#1830](https://github.com/codeigniter4/CodeIgniter4/issues/1830) +- \[Re-opened\] 404 File not found when running CodeIgniter on local Apache web server and virtual hosts [\#1400](https://github.com/codeigniter4/CodeIgniter4/issues/1400) +- MySQLi SSL verify [\#1219](https://github.com/codeigniter4/CodeIgniter4/issues/1219) + +**Closed issues:** + +- SQLite driver doesn't drop indexes when dropping a table [\#1983](https://github.com/codeigniter4/CodeIgniter4/issues/1983) +- About mysqli options MYSQLI\_OPT\_INT\_AND\_FLOAT\_NATIVE [\#1979](https://github.com/codeigniter4/CodeIgniter4/issues/1979) +- Toolbar won't accept custom collectors [\#1971](https://github.com/codeigniter4/CodeIgniter4/issues/1971) +- 404 Error in pathinfo mode [\#1965](https://github.com/codeigniter4/CodeIgniter4/issues/1965) +- A controller parameter without default value creates ReflectionException error [\#1948](https://github.com/codeigniter4/CodeIgniter4/issues/1948) +- Wrong datetime on history debug toolbar [\#1944](https://github.com/codeigniter4/CodeIgniter4/issues/1944) +- Appstarter is not working after installation [\#1941](https://github.com/codeigniter4/CodeIgniter4/issues/1941) +- AppStarter Vanilla Installation - No hint that writable directory is not writable [\#1934](https://github.com/codeigniter4/CodeIgniter4/issues/1934) +- Updating appstarter with composer does not take composer.phar into account [\#1932](https://github.com/codeigniter4/CodeIgniter4/issues/1932) +- Problem with extending core class. [\#1922](https://github.com/codeigniter4/CodeIgniter4/issues/1922) +- Debug Toolbar causes error if an array is provided as session value since Beta 2 [\#1919](https://github.com/codeigniter4/CodeIgniter4/issues/1919) +- Wrong controller filter processing because of faulty regular expression generation [\#1907](https://github.com/codeigniter4/CodeIgniter4/issues/1907) +- Toolbar not supporting IE11 \(realXHR.responseURL\) [\#1905](https://github.com/codeigniter4/CodeIgniter4/issues/1905) +- Validation Always Print Error Message [\#1903](https://github.com/codeigniter4/CodeIgniter4/issues/1903) +- Using soft deletes can lead to mysql ambiguous exception [\#1881](https://github.com/codeigniter4/CodeIgniter4/issues/1881) +- Error when running `php spark serve` \(spaces in folder names\) [\#1880](https://github.com/codeigniter4/CodeIgniter4/issues/1880) +- Class 'CodeIgniter\Filters\DebugToolbar' not found [\#1871](https://github.com/codeigniter4/CodeIgniter4/issues/1871) +- Should unmatched cache\(\) return null? [\#1870](https://github.com/codeigniter4/CodeIgniter4/issues/1870) +- Class 'CodeIgniter\Test\CIUnitTestCase' not found when testing in Appstarter project [\#1864](https://github.com/codeigniter4/CodeIgniter4/issues/1864) +- Public methods in system/Controller [\#1849](https://github.com/codeigniter4/CodeIgniter4/issues/1849) +- Controller in folder - method not accessible unless I set a route on Linux - Windows OK [\#1841](https://github.com/codeigniter4/CodeIgniter4/issues/1841) +- Routing to hardcoded ids does not work [\#1838](https://github.com/codeigniter4/CodeIgniter4/issues/1838) +- Form Validation: required\_with and required\_without implementations [\#1837](https://github.com/codeigniter4/CodeIgniter4/issues/1837) +- Events.php on method $callable parameter accepts only callable [\#1835](https://github.com/codeigniter4/CodeIgniter4/issues/1835) +- Controller response property overriding by ControllerResponse inPHPUnit [\#1834](https://github.com/codeigniter4/CodeIgniter4/issues/1834) +- ValidationInterface run method $data attribute should be nullable [\#1833](https://github.com/codeigniter4/CodeIgniter4/issues/1833) +- Fail to render view in a view with layout [\#1826](https://github.com/codeigniter4/CodeIgniter4/issues/1826) +- UploadedFile::move and File::move have different implementation [\#1825](https://github.com/codeigniter4/CodeIgniter4/issues/1825) +- Missing documentation: parser is not able to handle nested loops [\#1821](https://github.com/codeigniter4/CodeIgniter4/issues/1821) +- Is hashId function missing? [\#1801](https://github.com/codeigniter4/CodeIgniter4/issues/1801) +- Parser is not able to handle nested loops [\#1799](https://github.com/codeigniter4/CodeIgniter4/issues/1799) +- Routing rules order \[suspended / probably csrf\] [\#1798](https://github.com/codeigniter4/CodeIgniter4/issues/1798) +- I need to call session\(\) if I want to be able to use old\(\) in the forms. [\#1795](https://github.com/codeigniter4/CodeIgniter4/issues/1795) +- Output getting buffered when running via command line [\#1792](https://github.com/codeigniter4/CodeIgniter4/issues/1792) +- Wrong CodeIgniter::handleRequest method definition [\#1786](https://github.com/codeigniter4/CodeIgniter4/issues/1786) +- File::move is not moving file [\#1785](https://github.com/codeigniter4/CodeIgniter4/issues/1785) +- Question about date helper [\#1783](https://github.com/codeigniter4/CodeIgniter4/issues/1783) +- Intention or bug? File::move does not update path [\#1782](https://github.com/codeigniter4/CodeIgniter4/issues/1782) +- Small typos in documentation section "Taking Advantage of Spl" [\#1781](https://github.com/codeigniter4/CodeIgniter4/issues/1781) +- Documentation mistake: Model::save does not return a boolean only [\#1780](https://github.com/codeigniter4/CodeIgniter4/issues/1780) +- Toolbar::run produces incompatible data for json\_encode [\#1779](https://github.com/codeigniter4/CodeIgniter4/issues/1779) +- History::setFiles may crash when reading empty file [\#1778](https://github.com/codeigniter4/CodeIgniter4/issues/1778) +- Can't set subquery as WHERE condition. [\#1775](https://github.com/codeigniter4/CodeIgniter4/issues/1775) +- Ignoring 'required' validation rule for inserts. [\#1773](https://github.com/codeigniter4/CodeIgniter4/issues/1773) +- save\(\) method trying to insert instead of update [\#1770](https://github.com/codeigniter4/CodeIgniter4/issues/1770) +- Controller Test / Feature Testing output issues [\#1767](https://github.com/codeigniter4/CodeIgniter4/issues/1767) +- MigrationRunner::version should return "current version string on success" [\#1766](https://github.com/codeigniter4/CodeIgniter4/issues/1766) +- DIRECTORY\_SEPARATOR / Different Behavior under Windows [\#1760](https://github.com/codeigniter4/CodeIgniter4/issues/1760) +- HTTP Feature Testing not working [\#1710](https://github.com/codeigniter4/CodeIgniter4/issues/1710) +- alpha4-\>5 requires to have primary key in every model/table [\#1706](https://github.com/codeigniter4/CodeIgniter4/issues/1706) +- route\_to\('name'\); does not work for other subdomains [\#1697](https://github.com/codeigniter4/CodeIgniter4/issues/1697) +- Router issue - overwriting. [\#1692](https://github.com/codeigniter4/CodeIgniter4/issues/1692) +- Using Memcache as Session Handler cause exception during regenerate. [\#1676](https://github.com/codeigniter4/CodeIgniter4/issues/1676) +- Model's without primary keys get pagination counts wrong [\#1597](https://github.com/codeigniter4/CodeIgniter4/issues/1597) +- Unable set ENVIRONMENT with Spark [\#1268](https://github.com/codeigniter4/CodeIgniter4/issues/1268) +- WIP Improve unit tests [\#512](https://github.com/codeigniter4/CodeIgniter4/issues/512) + +**Merged pull requests:** + +- Prep for beta.3 [\#1990](https://github.com/codeigniter4/CodeIgniter4/pull/1990) ([jim-parry](https://github.com/jim-parry)) +- Correct API docblock problems for phpdocs [\#1987](https://github.com/codeigniter4/CodeIgniter4/pull/1987) ([jim-parry](https://github.com/jim-parry)) +- Update docblock version to 4.0.0 [\#1986](https://github.com/codeigniter4/CodeIgniter4/pull/1986) ([jim-parry](https://github.com/jim-parry)) +- Fix filter processing. Fixes \#1907 [\#1985](https://github.com/codeigniter4/CodeIgniter4/pull/1985) ([jim-parry](https://github.com/jim-parry)) +- Add footing to HTML Table [\#1984](https://github.com/codeigniter4/CodeIgniter4/pull/1984) ([jim-parry](https://github.com/jim-parry)) +- Using soft deletes should not return an ambiguous field message when joining tables. Closes \#1881 [\#1981](https://github.com/codeigniter4/CodeIgniter4/pull/1981) ([lonnieezell](https://github.com/lonnieezell)) +- Corrected return value for Session/RedisHandler::read to string, per PHP specs [\#1980](https://github.com/codeigniter4/CodeIgniter4/pull/1980) ([lonnieezell](https://github.com/lonnieezell)) +- Implement HTML Table for CI4 [\#1978](https://github.com/codeigniter4/CodeIgniter4/pull/1978) ([jim-parry](https://github.com/jim-parry)) +- Test/featuretestcase [\#1977](https://github.com/codeigniter4/CodeIgniter4/pull/1977) ([jim-parry](https://github.com/jim-parry)) +- Fix validation rules table format [\#1975](https://github.com/codeigniter4/CodeIgniter4/pull/1975) ([jim-parry](https://github.com/jim-parry)) +- Remove framework classes from the autoloader classmap. [\#1974](https://github.com/codeigniter4/CodeIgniter4/pull/1974) ([lonnieezell](https://github.com/lonnieezell)) +- Defaultfixes [\#1973](https://github.com/codeigniter4/CodeIgniter4/pull/1973) ([lonnieezell](https://github.com/lonnieezell)) +- Toolbar fix for custom collectors [\#1972](https://github.com/codeigniter4/CodeIgniter4/pull/1972) ([MGatner](https://github.com/MGatner)) +- Add back filter arguments [\#1970](https://github.com/codeigniter4/CodeIgniter4/pull/1970) ([MGatner](https://github.com/MGatner)) +- Fixed pathinfo mode 404 error, rebuild array index of uri segments from array\_filter\(\) [\#1968](https://github.com/codeigniter4/CodeIgniter4/pull/1968) ([viosion](https://github.com/viosion)) +- String type primary key should also wrap into an array during db update [\#1963](https://github.com/codeigniter4/CodeIgniter4/pull/1963) ([vibbow](https://github.com/vibbow)) +- WIP - Fix side issue [\#1962](https://github.com/codeigniter4/CodeIgniter4/pull/1962) ([vibbow](https://github.com/vibbow)) +- Fix Debugbar url tail slash issue [\#1961](https://github.com/codeigniter4/CodeIgniter4/pull/1961) ([vibbow](https://github.com/vibbow)) +- New generic string validation rule. [\#1957](https://github.com/codeigniter4/CodeIgniter4/pull/1957) ([lonnieezell](https://github.com/lonnieezell)) +- Use Null Coalesce Operator [\#1956](https://github.com/codeigniter4/CodeIgniter4/pull/1956) ([carusogabriel](https://github.com/carusogabriel)) +- Travis-CI build failed fix [\#1955](https://github.com/codeigniter4/CodeIgniter4/pull/1955) ([atishamte](https://github.com/atishamte)) +- Fix validation table format [\#1954](https://github.com/codeigniter4/CodeIgniter4/pull/1954) ([jim-parry](https://github.com/jim-parry)) +- Add Validations for `equals\(\)` and `not\_equals\(\)` [\#1952](https://github.com/codeigniter4/CodeIgniter4/pull/1952) ([MGatner](https://github.com/MGatner)) +- System typos changes & code cleanup [\#1951](https://github.com/codeigniter4/CodeIgniter4/pull/1951) ([atishamte](https://github.com/atishamte)) +- Fix some side issue [\#1950](https://github.com/codeigniter4/CodeIgniter4/pull/1950) ([vibbow](https://github.com/vibbow)) +- Toobar/Routes correction [\#1949](https://github.com/codeigniter4/CodeIgniter4/pull/1949) ([atishamte](https://github.com/atishamte)) +- Fix BaseConfig didn't load Registrar files properly [\#1947](https://github.com/codeigniter4/CodeIgniter4/pull/1947) ([vibbow](https://github.com/vibbow)) +- Fix datetime extraction from debugbar file [\#1945](https://github.com/codeigniter4/CodeIgniter4/pull/1945) ([soft2u](https://github.com/soft2u)) +- Model, Entity, Exception & Migration test cases [\#1943](https://github.com/codeigniter4/CodeIgniter4/pull/1943) ([atishamte](https://github.com/atishamte)) +- Remove section that prevents hotlinking [\#1939](https://github.com/codeigniter4/CodeIgniter4/pull/1939) ([MGatner](https://github.com/MGatner)) +- Database typos changes [\#1938](https://github.com/codeigniter4/CodeIgniter4/pull/1938) ([atishamte](https://github.com/atishamte)) +- Docs: improve app testing writeup [\#1936](https://github.com/codeigniter4/CodeIgniter4/pull/1936) ([jim-parry](https://github.com/jim-parry)) +- Update phpunit.xml scripts. Fixes \#1932 [\#1935](https://github.com/codeigniter4/CodeIgniter4/pull/1935) ([jim-parry](https://github.com/jim-parry)) +- having \(Is NULL deletion\) [\#1933](https://github.com/codeigniter4/CodeIgniter4/pull/1933) ([nowackipawel](https://github.com/nowackipawel)) +- Toolbar IE11 fix [\#1931](https://github.com/codeigniter4/CodeIgniter4/pull/1931) ([REJack](https://github.com/REJack)) +- Model Changes w.r.t. \#1773 [\#1930](https://github.com/codeigniter4/CodeIgniter4/pull/1930) ([atishamte](https://github.com/atishamte)) +- Entity exception for non existed props. [\#1927](https://github.com/codeigniter4/CodeIgniter4/pull/1927) ([nowackipawel](https://github.com/nowackipawel)) +- Docs: update installation guide [\#1926](https://github.com/codeigniter4/CodeIgniter4/pull/1926) ([jim-parry](https://github.com/jim-parry)) +- removed $\_SERVER\['CI\_ENVIRONMENT'\] [\#1925](https://github.com/codeigniter4/CodeIgniter4/pull/1925) ([truelineinfotech](https://github.com/truelineinfotech)) +- missing return [\#1923](https://github.com/codeigniter4/CodeIgniter4/pull/1923) ([titounnes](https://github.com/titounnes)) +- JSONFormatter [\#1918](https://github.com/codeigniter4/CodeIgniter4/pull/1918) ([nowackipawel](https://github.com/nowackipawel)) +- Database Test Cases [\#1917](https://github.com/codeigniter4/CodeIgniter4/pull/1917) ([atishamte](https://github.com/atishamte)) +- Check if the value is string [\#1916](https://github.com/codeigniter4/CodeIgniter4/pull/1916) ([daif](https://github.com/daif)) +- Fix for POST + JSON \(Content-Length added\) [\#1915](https://github.com/codeigniter4/CodeIgniter4/pull/1915) ([nowackipawel](https://github.com/nowackipawel)) +- Housekeeping - prep for beta.2 [\#1914](https://github.com/codeigniter4/CodeIgniter4/pull/1914) ([jim-parry](https://github.com/jim-parry)) +- More RouteCollection tests for overwriting. Closes \#1692 [\#1913](https://github.com/codeigniter4/CodeIgniter4/pull/1913) ([jim-parry](https://github.com/jim-parry)) +- Additional RouteCollectionTests [\#1912](https://github.com/codeigniter4/CodeIgniter4/pull/1912) ([jim-parry](https://github.com/jim-parry)) +- JSON Cast exception test cases [\#1911](https://github.com/codeigniter4/CodeIgniter4/pull/1911) ([atishamte](https://github.com/atishamte)) +- Added print method to CLI library so you can print multiple times on same line [\#1910](https://github.com/codeigniter4/CodeIgniter4/pull/1910) ([lonnieezell](https://github.com/lonnieezell)) +- Add filter parameters to User Guide [\#1908](https://github.com/codeigniter4/CodeIgniter4/pull/1908) ([MGatner](https://github.com/MGatner)) +- SubQuery related test cases w.r.t \#1775 [\#1906](https://github.com/codeigniter4/CodeIgniter4/pull/1906) ([atishamte](https://github.com/atishamte)) +- BaseBuilder Corrections [\#1902](https://github.com/codeigniter4/CodeIgniter4/pull/1902) ([atishamte](https://github.com/atishamte)) +- Update .htaccess for better security and caching [\#1900](https://github.com/codeigniter4/CodeIgniter4/pull/1900) ([atishamte](https://github.com/atishamte)) +- Database Forge correction [\#1899](https://github.com/codeigniter4/CodeIgniter4/pull/1899) ([atishamte](https://github.com/atishamte)) +- Toolbar fix w.r.t \#1779 [\#1897](https://github.com/codeigniter4/CodeIgniter4/pull/1897) ([atishamte](https://github.com/atishamte)) +- Mysql connection issue with MYSQLI\_CLIENT\_SSL\_DONT\_VERIFY\_SERVER\_CERT \#1219 [\#1896](https://github.com/codeigniter4/CodeIgniter4/pull/1896) ([atishamte](https://github.com/atishamte)) +- Unmatched Cache Library `get\(\)` return null [\#1895](https://github.com/codeigniter4/CodeIgniter4/pull/1895) ([MGatner](https://github.com/MGatner)) +- New method Find Column w.r.t. \#1619 [\#1861](https://github.com/codeigniter4/CodeIgniter4/pull/1861) ([atishamte](https://github.com/atishamte)) + +## [v4.0.0-beta.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-beta.1) (2019-03-01) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0.0-alpha.5...v4.0.0-beta.1) + +**Fixed bugs:** + +- Sequential Migrations don't run to the correct version [\#1666](https://github.com/codeigniter4/CodeIgniter4/issues/1666) + +**Closed issues:** + +- Back references not working in route [\#1761](https://github.com/codeigniter4/CodeIgniter4/issues/1761) +- setDefaultController not working as expected [\#1758](https://github.com/codeigniter4/CodeIgniter4/issues/1758) +- Spark doesn't work when using devstarter [\#1748](https://github.com/codeigniter4/CodeIgniter4/issues/1748) +- required\_with and required\_without validation rules [\#1735](https://github.com/codeigniter4/CodeIgniter4/issues/1735) +- validation rule password never match [\#1728](https://github.com/codeigniter4/CodeIgniter4/issues/1728) +- Entities errors [\#1727](https://github.com/codeigniter4/CodeIgniter4/issues/1727) +- Loading namespace helpers doesn't work as expected CI4 alpha5 [\#1726](https://github.com/codeigniter4/CodeIgniter4/issues/1726) +- spark migrate:latest ErrorException alpha5 [\#1724](https://github.com/codeigniter4/CodeIgniter4/issues/1724) +- redirect\(\)-\>to lost $baseURL config [\#1721](https://github.com/codeigniter4/CodeIgniter4/issues/1721) +- Bug Report: Seeding [\#1720](https://github.com/codeigniter4/CodeIgniter4/issues/1720) +- Spark missing arguments [\#1718](https://github.com/codeigniter4/CodeIgniter4/issues/1718) +- Model required validation rule not working [\#1717](https://github.com/codeigniter4/CodeIgniter4/issues/1717) +- ZendEscaper - duplicate? [\#1716](https://github.com/codeigniter4/CodeIgniter4/issues/1716) +- Why we required form pointed to correct url? [\#1713](https://github.com/codeigniter4/CodeIgniter4/issues/1713) +- Why there is only 1 function in ArrayHelper? Can we introduce more? [\#1711](https://github.com/codeigniter4/CodeIgniter4/issues/1711) +- CodeIgniter\Model::cleanValidationRules\(\) must be of the type array, string given [\#1707](https://github.com/codeigniter4/CodeIgniter4/issues/1707) +- alpha 4-\>5 query param binding [\#1705](https://github.com/codeigniter4/CodeIgniter4/issues/1705) +- failValidationError\($description\) [\#1702](https://github.com/codeigniter4/CodeIgniter4/issues/1702) +- Bug : changing viewsDirectory misses errors folder when exception occures [\#1701](https://github.com/codeigniter4/CodeIgniter4/issues/1701) +- Cannot define complex routes , i.e. date [\#1700](https://github.com/codeigniter4/CodeIgniter4/issues/1700) +- lang bug or not? \(empty translations\) [\#1698](https://github.com/codeigniter4/CodeIgniter4/issues/1698) +- Issue Extend Core Class [\#1653](https://github.com/codeigniter4/CodeIgniter4/issues/1653) +- Turn OFF getMyProperty\(\) method during DB save. [\#1646](https://github.com/codeigniter4/CodeIgniter4/issues/1646) +- Model class crashes when handling complex validation rules [\#1574](https://github.com/codeigniter4/CodeIgniter4/issues/1574) +- Database ForgeTest hiccup [\#1478](https://github.com/codeigniter4/CodeIgniter4/issues/1478) +- SQLLite3 Forge needs better column handling [\#1255](https://github.com/codeigniter4/CodeIgniter4/issues/1255) +- TODO BaseConnection needs better error handling [\#1254](https://github.com/codeigniter4/CodeIgniter4/issues/1254) +- Model Alternative Keys [\#428](https://github.com/codeigniter4/CodeIgniter4/issues/428) + +**Merged pull requests:** + +- Housekeeping for beta.1 [\#1774](https://github.com/codeigniter4/CodeIgniter4/pull/1774) ([jim-parry](https://github.com/jim-parry)) +- Helper changes [\#1768](https://github.com/codeigniter4/CodeIgniter4/pull/1768) ([atishamte](https://github.com/atishamte)) +- Fix routing when no default route has been specified. Fixes \#1758 [\#1764](https://github.com/codeigniter4/CodeIgniter4/pull/1764) ([lonnieezell](https://github.com/lonnieezell)) +- Ensure validation works in Model with errors as part of rules. Fixes \#1574 [\#1763](https://github.com/codeigniter4/CodeIgniter4/pull/1763) ([lonnieezell](https://github.com/lonnieezell)) +- Correct the unneeded double-quote \(typo\) [\#1757](https://github.com/codeigniter4/CodeIgniter4/pull/1757) ([smhnaji](https://github.com/smhnaji)) +- lowercase 'vfsStream' in composer files [\#1755](https://github.com/codeigniter4/CodeIgniter4/pull/1755) ([MGatner](https://github.com/MGatner)) +- Fixed typo preventing link format [\#1752](https://github.com/codeigniter4/CodeIgniter4/pull/1752) ([MGatner](https://github.com/MGatner)) +- Guide: Moving misplaced text under correct heading [\#1751](https://github.com/codeigniter4/CodeIgniter4/pull/1751) ([MGatner](https://github.com/MGatner)) +- Remove reference to Encryption Key in User Guide [\#1750](https://github.com/codeigniter4/CodeIgniter4/pull/1750) ([MGatner](https://github.com/MGatner)) +- Adding environment to .env [\#1749](https://github.com/codeigniter4/CodeIgniter4/pull/1749) ([MGatner](https://github.com/MGatner)) +- Updated composite key tests for SQLite3 support. Fixes \#1478 [\#1745](https://github.com/codeigniter4/CodeIgniter4/pull/1745) ([lonnieezell](https://github.com/lonnieezell)) +- Update entity docs for current framework state. Fixes \#1727 [\#1744](https://github.com/codeigniter4/CodeIgniter4/pull/1744) ([lonnieezell](https://github.com/lonnieezell)) +- Manually sort migrations found instead of relying on the OS. Fixes \#1666 [\#1743](https://github.com/codeigniter4/CodeIgniter4/pull/1743) ([lonnieezell](https://github.com/lonnieezell)) +- Fix required\_without rule bug. [\#1742](https://github.com/codeigniter4/CodeIgniter4/pull/1742) ([bangbangda](https://github.com/bangbangda)) +- Helpers with a specific namespace can be loaded now. Fixes \#1726 [\#1741](https://github.com/codeigniter4/CodeIgniter4/pull/1741) ([lonnieezell](https://github.com/lonnieezell)) +- Refactor test support for app starter [\#1740](https://github.com/codeigniter4/CodeIgniter4/pull/1740) ([jim-parry](https://github.com/jim-parry)) +- Fix typo [\#1739](https://github.com/codeigniter4/CodeIgniter4/pull/1739) ([vibbow](https://github.com/vibbow)) +- Fix required\_with rule bug. Fixes \#1728 [\#1738](https://github.com/codeigniter4/CodeIgniter4/pull/1738) ([bangbangda](https://github.com/bangbangda)) +- Added support for dropTable and modifyTable with SQLite driver [\#1737](https://github.com/codeigniter4/CodeIgniter4/pull/1737) ([lonnieezell](https://github.com/lonnieezell)) +- Accommodate long travis execution times [\#1736](https://github.com/codeigniter4/CodeIgniter4/pull/1736) ([jim-parry](https://github.com/jim-parry)) +- Fix increment and decrement errors with Postgres [\#1733](https://github.com/codeigniter4/CodeIgniter4/pull/1733) ([lonnieezell](https://github.com/lonnieezell)) +- Don't check from CLI in Routes. Fixes \#1724 [\#1732](https://github.com/codeigniter4/CodeIgniter4/pull/1732) ([lonnieezell](https://github.com/lonnieezell)) +- Revert "Ensure isn't checked during RouteCollection calls when called from CLI" [\#1731](https://github.com/codeigniter4/CodeIgniter4/pull/1731) ([lonnieezell](https://github.com/lonnieezell)) +- Ensure isn't checked during RouteCollection calls when called from CLI [\#1730](https://github.com/codeigniter4/CodeIgniter4/pull/1730) ([lonnieezell](https://github.com/lonnieezell)) +- New View Layout functionality for simple template functionality. [\#1729](https://github.com/codeigniter4/CodeIgniter4/pull/1729) ([lonnieezell](https://github.com/lonnieezell)) +- Update Request.php [\#1725](https://github.com/codeigniter4/CodeIgniter4/pull/1725) ([HieuPT7](https://github.com/HieuPT7)) +- Log an error if redis authentication is failed. [\#1723](https://github.com/codeigniter4/CodeIgniter4/pull/1723) ([vibbow](https://github.com/vibbow)) +- Seeder adds default namespace to seeds [\#1722](https://github.com/codeigniter4/CodeIgniter4/pull/1722) ([lonnieezell](https://github.com/lonnieezell)) +- Update Cache RedisHandler to support select database. [\#1719](https://github.com/codeigniter4/CodeIgniter4/pull/1719) ([vibbow](https://github.com/vibbow)) +- minors \(Model.php\) [\#1712](https://github.com/codeigniter4/CodeIgniter4/pull/1712) ([nowackipawel](https://github.com/nowackipawel)) +- Fix/rc [\#1709](https://github.com/codeigniter4/CodeIgniter4/pull/1709) ([jim-parry](https://github.com/jim-parry)) +- UploadFile - language support [\#1708](https://github.com/codeigniter4/CodeIgniter4/pull/1708) ([nowackipawel](https://github.com/nowackipawel)) +- Fix viewsDirectory bug Fixes \#1701 [\#1704](https://github.com/codeigniter4/CodeIgniter4/pull/1704) ([bangbangda](https://github.com/bangbangda)) +- Fix install link in user guide [\#1699](https://github.com/codeigniter4/CodeIgniter4/pull/1699) ([jim-parry](https://github.com/jim-parry)) +- Fix page structure etc [\#1696](https://github.com/codeigniter4/CodeIgniter4/pull/1696) ([jim-parry](https://github.com/jim-parry)) +- Tidy up code blocks in the user guide [\#1695](https://github.com/codeigniter4/CodeIgniter4/pull/1695) ([jim-parry](https://github.com/jim-parry)) + +## [v4.0.0.0-alpha.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0.0-alpha.5) (2019-01-30) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-alpha.5...v4.0.0.0-alpha.5) + +**Implemented enhancements:** + +- Cache system should support site-wide prefixes [\#1659](https://github.com/codeigniter4/CodeIgniter4/issues/1659) + +**Fixed bugs:** + +- Problem with Database BaseBuilder binds [\#1226](https://github.com/codeigniter4/CodeIgniter4/issues/1226) + +**Closed issues:** + +- DB query not working with disabled escaping [\#1687](https://github.com/codeigniter4/CodeIgniter4/issues/1687) +- migrate:refresh ArgumentCountError [\#1682](https://github.com/codeigniter4/CodeIgniter4/issues/1682) +- Do I need to clear old session files manually? [\#1681](https://github.com/codeigniter4/CodeIgniter4/issues/1681) +- Pagination / pager-\>links\(\) double encodes links [\#1680](https://github.com/codeigniter4/CodeIgniter4/issues/1680) +- Document Method Spoofing for forms. [\#1668](https://github.com/codeigniter4/CodeIgniter4/issues/1668) +- insertBatch with escape=false still escapes values [\#1667](https://github.com/codeigniter4/CodeIgniter4/issues/1667) +- Filters should not be case sensitive [\#1664](https://github.com/codeigniter4/CodeIgniter4/issues/1664) +- RouteCollection::discoverRoutes incomplete [\#1662](https://github.com/codeigniter4/CodeIgniter4/issues/1662) +- Feature request make is\_unique - more than one field. [\#1655](https://github.com/codeigniter4/CodeIgniter4/issues/1655) +- Toolbar logs tab - not logging [\#1651](https://github.com/codeigniter4/CodeIgniter4/issues/1651) +- DebugToolbar - too much recursion [\#1650](https://github.com/codeigniter4/CodeIgniter4/issues/1650) +- \[documentation\] Typing mistake in transaction example [\#1639](https://github.com/codeigniter4/CodeIgniter4/issues/1639) +- Transaction documentation error and/or bug [\#1638](https://github.com/codeigniter4/CodeIgniter4/issues/1638) +- Bug : pagination broken when using 1 as perPage [\#1628](https://github.com/codeigniter4/CodeIgniter4/issues/1628) +- View data not being passed between each call? [\#1621](https://github.com/codeigniter4/CodeIgniter4/issues/1621) +- Composer Installation downloads app and application folder. [\#1620](https://github.com/codeigniter4/CodeIgniter4/issues/1620) +- countAllResults\(\) should respect soft deletes [\#1617](https://github.com/codeigniter4/CodeIgniter4/issues/1617) +- redirect function don't redirect to base\_url [\#1611](https://github.com/codeigniter4/CodeIgniter4/issues/1611) +- Memory issue - Toolbar collects every query [\#1607](https://github.com/codeigniter4/CodeIgniter4/issues/1607) +- Pls remove string type in parameter $group at Database::forge [\#1605](https://github.com/codeigniter4/CodeIgniter4/issues/1605) +- SQL JOIN : bad aliasing on join with prefixed db tables [\#1599](https://github.com/codeigniter4/CodeIgniter4/issues/1599) +- Model's update method fails when validation rules exist [\#1584](https://github.com/codeigniter4/CodeIgniter4/issues/1584) +- maybe need to modify session garbage collector section. \(FileHandler\) [\#1565](https://github.com/codeigniter4/CodeIgniter4/issues/1565) +- Maybe routes has problem. \[setTranslateURIDashes\] [\#1564](https://github.com/codeigniter4/CodeIgniter4/issues/1564) +- ErrorException Trying to get property 'affected\_rows' of non-object [\#1559](https://github.com/codeigniter4/CodeIgniter4/issues/1559) +- UG - typo in Managing Apps [\#1558](https://github.com/codeigniter4/CodeIgniter4/issues/1558) +- Database migration uses wrong database when initialising migration classes [\#1532](https://github.com/codeigniter4/CodeIgniter4/issues/1532) +- Database migration table not correctly created when a non-default database connection is used [\#1531](https://github.com/codeigniter4/CodeIgniter4/issues/1531) +- MYSQL : orderBy\(\) considers CASE statement as a column [\#1528](https://github.com/codeigniter4/CodeIgniter4/issues/1528) +- getCompiledSelect\(\) return query without binds [\#1526](https://github.com/codeigniter4/CodeIgniter4/issues/1526) +- Commit pre-hook misbehaving [\#1404](https://github.com/codeigniter4/CodeIgniter4/issues/1404) +- Lack of proper instruction in documentation for changing Application and System folder name [\#1366](https://github.com/codeigniter4/CodeIgniter4/issues/1366) +- SubQueries \(tables from outside of the current model\) [\#1175](https://github.com/codeigniter4/CodeIgniter4/issues/1175) +- FileHandler Garbage Collector fails to delete expired session files. [\#942](https://github.com/codeigniter4/CodeIgniter4/issues/942) + +**Merged pull requests:** + +- Update changelog for alpha.5 [\#1694](https://github.com/codeigniter4/CodeIgniter4/pull/1694) ([jim-parry](https://github.com/jim-parry)) +- Docs/tutorial [\#1693](https://github.com/codeigniter4/CodeIgniter4/pull/1693) ([jim-parry](https://github.com/jim-parry)) +- Update the running docs [\#1691](https://github.com/codeigniter4/CodeIgniter4/pull/1691) ([jim-parry](https://github.com/jim-parry)) +- Rework install docs [\#1690](https://github.com/codeigniter4/CodeIgniter4/pull/1690) ([jim-parry](https://github.com/jim-parry)) +- Model Validation Fix [\#1689](https://github.com/codeigniter4/CodeIgniter4/pull/1689) ([lonnieezell](https://github.com/lonnieezell)) +- Add copyright blocks to filters [\#1688](https://github.com/codeigniter4/CodeIgniter4/pull/1688) ([jim-parry](https://github.com/jim-parry)) +- Refactor/filters [\#1686](https://github.com/codeigniter4/CodeIgniter4/pull/1686) ([jim-parry](https://github.com/jim-parry)) +- Fix admin - app starter creation [\#1685](https://github.com/codeigniter4/CodeIgniter4/pull/1685) ([jim-parry](https://github.com/jim-parry)) +- Updating session id cleanup for filehandler. Fixes \#1681 Fixes \#1565 [\#1684](https://github.com/codeigniter4/CodeIgniter4/pull/1684) ([lonnieezell](https://github.com/lonnieezell)) +- Fix migrate:refresh bug Fixes \#1682 [\#1683](https://github.com/codeigniter4/CodeIgniter4/pull/1683) ([bangbangda](https://github.com/bangbangda)) +- save\_path - for memcached \(Session.php\) + sess\_prefix \(..Handler.php\) [\#1679](https://github.com/codeigniter4/CodeIgniter4/pull/1679) ([nowackipawel](https://github.com/nowackipawel)) +- fix route not replacing forward slashes [\#1678](https://github.com/codeigniter4/CodeIgniter4/pull/1678) ([puschie286](https://github.com/puschie286)) +- Implement Don't Escape feature for db engine [\#1677](https://github.com/codeigniter4/CodeIgniter4/pull/1677) ([lonnieezell](https://github.com/lonnieezell)) +- Add missing test group directives [\#1675](https://github.com/codeigniter4/CodeIgniter4/pull/1675) ([jim-parry](https://github.com/jim-parry)) +- Changelog alpha.5 so far [\#1674](https://github.com/codeigniter4/CodeIgniter4/pull/1674) ([jim-parry](https://github.com/jim-parry)) +- Updated download & installation docs [\#1673](https://github.com/codeigniter4/CodeIgniter4/pull/1673) ([jim-parry](https://github.com/jim-parry)) +- Update Autoloader.php [\#1672](https://github.com/codeigniter4/CodeIgniter4/pull/1672) ([zl59503020](https://github.com/zl59503020)) +- Update docs [\#1671](https://github.com/codeigniter4/CodeIgniter4/pull/1671) ([jim-parry](https://github.com/jim-parry)) +- Update PHP dependency to 7.2 [\#1670](https://github.com/codeigniter4/CodeIgniter4/pull/1670) ([jim-parry](https://github.com/jim-parry)) +- Enhance Parser & Plugin testing [\#1669](https://github.com/codeigniter4/CodeIgniter4/pull/1669) ([jim-parry](https://github.com/jim-parry)) +- Composer PSR4 namespaces are now part of the modules auto-discovery [\#1665](https://github.com/codeigniter4/CodeIgniter4/pull/1665) ([lonnieezell](https://github.com/lonnieezell)) +- Fix bind issue that occurred when using whereIn or orWhereIn with a c… [\#1663](https://github.com/codeigniter4/CodeIgniter4/pull/1663) ([lonnieezell](https://github.com/lonnieezell)) +- Migrations Tests and database tweaks [\#1660](https://github.com/codeigniter4/CodeIgniter4/pull/1660) ([lonnieezell](https://github.com/lonnieezell)) +- DBGroup in \_\_get\(\), allows to validate "database" data outside the model. [\#1656](https://github.com/codeigniter4/CodeIgniter4/pull/1656) ([nowackipawel](https://github.com/nowackipawel)) +- Toolbar - Return Logger::$logCache items when collecting [\#1654](https://github.com/codeigniter4/CodeIgniter4/pull/1654) ([natanfelles](https://github.com/natanfelles)) +- remove php 7.3 from "allow\_failures" in travis config [\#1649](https://github.com/codeigniter4/CodeIgniter4/pull/1649) ([samsonasik](https://github.com/samsonasik)) +- Update "managing apps" docs [\#1648](https://github.com/codeigniter4/CodeIgniter4/pull/1648) ([jim-parry](https://github.com/jim-parry)) +- Fix transaction enabling confusing \(docu\) [\#1645](https://github.com/codeigniter4/CodeIgniter4/pull/1645) ([puschie286](https://github.com/puschie286)) +- Remove Email module [\#1643](https://github.com/codeigniter4/CodeIgniter4/pull/1643) ([jim-parry](https://github.com/jim-parry)) +- CSP nonce attribute value in "" [\#1642](https://github.com/codeigniter4/CodeIgniter4/pull/1642) ([nowackipawel](https://github.com/nowackipawel)) +- More unit testing tweaks [\#1641](https://github.com/codeigniter4/CodeIgniter4/pull/1641) ([jim-parry](https://github.com/jim-parry)) +- Update getCompiledX methods in BaseBuilder to return fully compiled q… [\#1640](https://github.com/codeigniter4/CodeIgniter4/pull/1640) ([lonnieezell](https://github.com/lonnieezell)) +- Fix starter README [\#1637](https://github.com/codeigniter4/CodeIgniter4/pull/1637) ([kenjis](https://github.com/kenjis)) +- Refactor Files module [\#1636](https://github.com/codeigniter4/CodeIgniter4/pull/1636) ([jim-parry](https://github.com/jim-parry)) +- Unit testing enhancements [\#1635](https://github.com/codeigniter4/CodeIgniter4/pull/1635) ([jim-parry](https://github.com/jim-parry)) +- Uses csrf\_field and form\_hidden instead of inline-html in form\_open [\#1633](https://github.com/codeigniter4/CodeIgniter4/pull/1633) ([nowackipawel](https://github.com/nowackipawel)) +- DBGroup should be passed to -\>run instead of -\>setRules [\#1632](https://github.com/codeigniter4/CodeIgniter4/pull/1632) ([nowackipawel](https://github.com/nowackipawel)) +- move use statement after License doc at UploadedFile class [\#1631](https://github.com/codeigniter4/CodeIgniter4/pull/1631) ([samsonasik](https://github.com/samsonasik)) +- Update copyright to 2019 [\#1630](https://github.com/codeigniter4/CodeIgniter4/pull/1630) ([jim-parry](https://github.com/jim-parry)) +- "application" to "app" directory doc and comments and welcome\_message clean up [\#1629](https://github.com/codeigniter4/CodeIgniter4/pull/1629) ([samsonasik](https://github.com/samsonasik)) +- clean up Paths::$viewDirectory property [\#1626](https://github.com/codeigniter4/CodeIgniter4/pull/1626) ([samsonasik](https://github.com/samsonasik)) +- fix. After matches is not set empty [\#1625](https://github.com/codeigniter4/CodeIgniter4/pull/1625) ([Instrye](https://github.com/Instrye)) +- Property was not cast if was defined as nullable. [\#1623](https://github.com/codeigniter4/CodeIgniter4/pull/1623) ([nowackipawel](https://github.com/nowackipawel)) +- Nullable support for \_\_set. [\#1622](https://github.com/codeigniter4/CodeIgniter4/pull/1622) ([nowackipawel](https://github.com/nowackipawel)) +- Fix View config merge order [\#1616](https://github.com/codeigniter4/CodeIgniter4/pull/1616) ([jim-parry](https://github.com/jim-parry)) +- Typo in documentation [\#1613](https://github.com/codeigniter4/CodeIgniter4/pull/1613) ([tpw1314](https://github.com/tpw1314)) +- WIP img fix\(?\) - html\_helper [\#1538](https://github.com/codeigniter4/CodeIgniter4/pull/1538) ([nowackipawel](https://github.com/nowackipawel)) + +## [v4.0.0-alpha.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-alpha.5) (2018-12-15) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-alpha.4...v4.0.0-alpha.5) + +## [v4.0.0-alpha.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-alpha.4) (2018-12-15) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-alpha.3...v4.0.0-alpha.4) + +**Implemented enhancements:** + +- WIP Vagrant and/or Docker support [\#1452](https://github.com/codeigniter4/CodeIgniter4/issues/1452) + +**Closed issues:** + +- Custom routing rule not match the User Guide [\#1609](https://github.com/codeigniter4/CodeIgniter4/issues/1609) +- Memory leak - binds not being cleared after find\(\) [\#1604](https://github.com/codeigniter4/CodeIgniter4/issues/1604) +- Soft deletes need parentheses around proceeding query? [\#1592](https://github.com/codeigniter4/CodeIgniter4/issues/1592) +- Cannot use Model first\(\) and update\(\) in table without primary key [\#1583](https://github.com/codeigniter4/CodeIgniter4/issues/1583) +- tried to allocate [\#1578](https://github.com/codeigniter4/CodeIgniter4/issues/1578) +- Database Migrations inconsistent info about sequential type [\#1577](https://github.com/codeigniter4/CodeIgniter4/issues/1577) +- Wrong or not precise documentation of magic \_\_get and \_\_set methods in section Handling Business Logic [\#1568](https://github.com/codeigniter4/CodeIgniter4/issues/1568) +- Entity's fill method is not supporting key mapping [\#1567](https://github.com/codeigniter4/CodeIgniter4/issues/1567) +- Ability to add namespace to FileLocator class [\#1552](https://github.com/codeigniter4/CodeIgniter4/issues/1552) +- Should log file contain plain text database username passwords etc? [\#1542](https://github.com/codeigniter4/CodeIgniter4/issues/1542) +- Issues with redirects - had to use exit to make it work and blank page [\#1501](https://github.com/codeigniter4/CodeIgniter4/issues/1501) +- Use of undefined constant BASEPATH [\#1439](https://github.com/codeigniter4/CodeIgniter4/issues/1439) +- MYSQL : BETWEEN operator loses condition value on JOIN in \(:\) used [\#1403](https://github.com/codeigniter4/CodeIgniter4/issues/1403) +- The problem in catching exceptions [\#1274](https://github.com/codeigniter4/CodeIgniter4/issues/1274) +- TODO Language needs improved locating [\#1262](https://github.com/codeigniter4/CodeIgniter4/issues/1262) +- Email attachment [\#1008](https://github.com/codeigniter4/CodeIgniter4/issues/1008) + +**Merged pull requests:** + +- Alpha.4 release prep [\#1612](https://github.com/codeigniter4/CodeIgniter4/pull/1612) ([jim-parry](https://github.com/jim-parry)) +- Test, fix & enhance Language [\#1610](https://github.com/codeigniter4/CodeIgniter4/pull/1610) ([jim-parry](https://github.com/jim-parry)) +- Note about environment configuration in UG [\#1608](https://github.com/codeigniter4/CodeIgniter4/pull/1608) ([jim-parry](https://github.com/jim-parry)) +- release framework script clean up [\#1606](https://github.com/codeigniter4/CodeIgniter4/pull/1606) ([samsonasik](https://github.com/samsonasik)) +- Flesh out I18n testing [\#1603](https://github.com/codeigniter4/CodeIgniter4/pull/1603) ([jim-parry](https://github.com/jim-parry)) +- Model's first and update didn't work primary key-less tables [\#1602](https://github.com/codeigniter4/CodeIgniter4/pull/1602) ([lonnieezell](https://github.com/lonnieezell)) +- clean up \Config\Services in Common.php [\#1601](https://github.com/codeigniter4/CodeIgniter4/pull/1601) ([samsonasik](https://github.com/samsonasik)) +- admin/starter/composer.json clean up [\#1600](https://github.com/codeigniter4/CodeIgniter4/pull/1600) ([samsonasik](https://github.com/samsonasik)) +- use $defaultGroup as default value for database session DBGroup [\#1598](https://github.com/codeigniter4/CodeIgniter4/pull/1598) ([puschie286](https://github.com/puschie286)) +- Retry handle fatal error via pre\_system [\#1595](https://github.com/codeigniter4/CodeIgniter4/pull/1595) ([samsonasik](https://github.com/samsonasik)) +- Fix Toolbar invalid css [\#1594](https://github.com/codeigniter4/CodeIgniter4/pull/1594) ([puschie286](https://github.com/puschie286)) +- Flesh out the Test package testing [\#1593](https://github.com/codeigniter4/CodeIgniter4/pull/1593) ([jim-parry](https://github.com/jim-parry)) +- Fix Toolbar file loading throw exception [\#1589](https://github.com/codeigniter4/CodeIgniter4/pull/1589) ([puschie286](https://github.com/puschie286)) +- Fix site\_url generate invalid url [\#1588](https://github.com/codeigniter4/CodeIgniter4/pull/1588) ([puschie286](https://github.com/puschie286)) +- Add Language fallback [\#1587](https://github.com/codeigniter4/CodeIgniter4/pull/1587) ([natanfelles](https://github.com/natanfelles)) +- Fix model namespace in tutorial [\#1586](https://github.com/codeigniter4/CodeIgniter4/pull/1586) ([jim-parry](https://github.com/jim-parry)) +- Type hint MigrationRunner methods [\#1585](https://github.com/codeigniter4/CodeIgniter4/pull/1585) ([natanfelles](https://github.com/natanfelles)) +- Fix changelog index & common functions UG indent [\#1582](https://github.com/codeigniter4/CodeIgniter4/pull/1582) ([jim-parry](https://github.com/jim-parry)) +- ContentSecurityPolicy testing & enhancement [\#1581](https://github.com/codeigniter4/CodeIgniter4/pull/1581) ([jim-parry](https://github.com/jim-parry)) +- Use Absolute Paths [\#1579](https://github.com/codeigniter4/CodeIgniter4/pull/1579) ([natanfelles](https://github.com/natanfelles)) +- Testing13/http [\#1576](https://github.com/codeigniter4/CodeIgniter4/pull/1576) ([jim-parry](https://github.com/jim-parry)) +- Adds ?integer, ?double, ?string, etc. cast types :\) [\#1575](https://github.com/codeigniter4/CodeIgniter4/pull/1575) ([nowackipawel](https://github.com/nowackipawel)) +- Lessons learned [\#1573](https://github.com/codeigniter4/CodeIgniter4/pull/1573) ([jim-parry](https://github.com/jim-parry)) +- Toolbar updates [\#1571](https://github.com/codeigniter4/CodeIgniter4/pull/1571) ([natanfelles](https://github.com/natanfelles)) +- Test esc\(\) with different encodings and ignore app-only helpers [\#1569](https://github.com/codeigniter4/CodeIgniter4/pull/1569) ([natanfelles](https://github.com/natanfelles)) +- id attribute support added for csrf\_field [\#1563](https://github.com/codeigniter4/CodeIgniter4/pull/1563) ([nowackipawel](https://github.com/nowackipawel)) +- Integrates Autoloader and FileLocator [\#1562](https://github.com/codeigniter4/CodeIgniter4/pull/1562) ([natanfelles](https://github.com/natanfelles)) +- Update Connection.php [\#1561](https://github.com/codeigniter4/CodeIgniter4/pull/1561) ([nowackipawel](https://github.com/nowackipawel)) +- remove \ prefix on use statements [\#1557](https://github.com/codeigniter4/CodeIgniter4/pull/1557) ([samsonasik](https://github.com/samsonasik)) +- using protected intead of public modifier for setUp\(\) function in tests [\#1556](https://github.com/codeigniter4/CodeIgniter4/pull/1556) ([samsonasik](https://github.com/samsonasik)) +- autoload clean up: remove Psr\Log namespace from composer.json [\#1555](https://github.com/codeigniter4/CodeIgniter4/pull/1555) ([samsonasik](https://github.com/samsonasik)) +- remove manual define "system/" directory prefix at ComposerScripts [\#1551](https://github.com/codeigniter4/CodeIgniter4/pull/1551) ([samsonasik](https://github.com/samsonasik)) +- allows to set empty html attr [\#1548](https://github.com/codeigniter4/CodeIgniter4/pull/1548) ([nowackipawel](https://github.com/nowackipawel)) +- Add Vagrantfile [\#1459](https://github.com/codeigniter4/CodeIgniter4/pull/1459) ([natanfelles](https://github.com/natanfelles)) + +## [v4.0.0-alpha.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-alpha.3) (2018-11-30) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-alpha.2...v4.0.0-alpha.3) + +**Implemented enhancements:** + +- Events should pass it's arguments by reference [\#1298](https://github.com/codeigniter4/CodeIgniter4/issues/1298) +- Feature request. Small but useful. Entity class. [\#1176](https://github.com/codeigniter4/CodeIgniter4/issues/1176) + +**Fixed bugs:** + +- \Config\Database::connect returns unexpected result for custom config values [\#1533](https://github.com/codeigniter4/CodeIgniter4/issues/1533) +- Old\(\) - Seems to have an issue with retrieving array values [\#1492](https://github.com/codeigniter4/CodeIgniter4/issues/1492) +- Language is not merging with modules [\#1433](https://github.com/codeigniter4/CodeIgniter4/issues/1433) +- RedirectResponse does not set cookies [\#1393](https://github.com/codeigniter4/CodeIgniter4/issues/1393) +- ThrottleTest intermittent failure [\#1382](https://github.com/codeigniter4/CodeIgniter4/issues/1382) +- Support replacer functions on validations [\#1363](https://github.com/codeigniter4/CodeIgniter4/issues/1363) +- Filter on route group only attach / [\#1247](https://github.com/codeigniter4/CodeIgniter4/issues/1247) +- TypeError in MemcachedHandler::\_\_construct\(\) [\#1204](https://github.com/codeigniter4/CodeIgniter4/issues/1204) +- Required\_without validation rule [\#1007](https://github.com/codeigniter4/CodeIgniter4/issues/1007) +- Division by zero - fresh install [\#979](https://github.com/codeigniter4/CodeIgniter4/issues/979) + +**Closed issues:** + +- need change file\_exists to is\_file? [\#1543](https://github.com/codeigniter4/CodeIgniter4/issues/1543) +- Docs : some links are broken due to lack of prefix CodeIgniter4 [\#1537](https://github.com/codeigniter4/CodeIgniter4/issues/1537) +- Spelling mistake /wrong function reference in documentation "Handling Business Logic" [\#1535](https://github.com/codeigniter4/CodeIgniter4/issues/1535) +- Sample code in Entity documentation contains useless statement [\#1534](https://github.com/codeigniter4/CodeIgniter4/issues/1534) +- Model events why is there no beforeFind? [\#1527](https://github.com/codeigniter4/CodeIgniter4/issues/1527) +- \[Documentation\] Bug in code example for "Validating $\_POST data" [\#1520](https://github.com/codeigniter4/CodeIgniter4/issues/1520) +- Before filters with sessions break php spark serve [\#1519](https://github.com/codeigniter4/CodeIgniter4/issues/1519) +- Missing documentation for placeholders in validation errors [\#1503](https://github.com/codeigniter4/CodeIgniter4/issues/1503) +- no $baseURL set will be notice "The baseURL value must be set" [\#1476](https://github.com/codeigniter4/CodeIgniter4/issues/1476) +- $field parameter as string at Forge::addField [\#1474](https://github.com/codeigniter4/CodeIgniter4/issues/1474) +- FeatureTestCaseTest still broken [\#1446](https://github.com/codeigniter4/CodeIgniter4/issues/1446) +- Unit test output not captured [\#1435](https://github.com/codeigniter4/CodeIgniter4/issues/1435) +- Response setJSON body \$key set value [\#1522](https://github.com/codeigniter4/CodeIgniter4/pull/1522) ([samsonasik](https://github.com/samsonasik)) +- .gitignore clean up [\#1521](https://github.com/codeigniter4/CodeIgniter4/pull/1521) ([samsonasik](https://github.com/samsonasik)) +- Small typo: changed setCreatedOn to setCreatedAt [\#1518](https://github.com/codeigniter4/CodeIgniter4/pull/1518) ([obozdag](https://github.com/obozdag)) +- move .htaccess from per-directory in writable/{directory} to writable/ [\#1517](https://github.com/codeigniter4/CodeIgniter4/pull/1517) ([samsonasik](https://github.com/samsonasik)) +- More secure redirection [\#1513](https://github.com/codeigniter4/CodeIgniter4/pull/1513) ([jim-parry](https://github.com/jim-parry)) +- remove unused use statements [\#1509](https://github.com/codeigniter4/CodeIgniter4/pull/1509) ([samsonasik](https://github.com/samsonasik)) +- remove duplicate strtolower\(\) call in URI::setScheme\(\) call [\#1508](https://github.com/codeigniter4/CodeIgniter4/pull/1508) ([samsonasik](https://github.com/samsonasik)) +- Fix multi "empty" string separated by "," marked as valid emails [\#1507](https://github.com/codeigniter4/CodeIgniter4/pull/1507) ([samsonasik](https://github.com/samsonasik)) +- Flesh out HTTP/File unit testing [\#1506](https://github.com/codeigniter4/CodeIgniter4/pull/1506) ([jim-parry](https://github.com/jim-parry)) +- Do not exit until all Response is completed [\#1505](https://github.com/codeigniter4/CodeIgniter4/pull/1505) ([natanfelles](https://github.com/natanfelles)) +- Revert RedirectResponse changes [\#1504](https://github.com/codeigniter4/CodeIgniter4/pull/1504) ([jim-parry](https://github.com/jim-parry)) +- Revert to buggy oldInput [\#1502](https://github.com/codeigniter4/CodeIgniter4/pull/1502) ([jim-parry](https://github.com/jim-parry)) +- Ignoring errors suppressed by @ [\#1500](https://github.com/codeigniter4/CodeIgniter4/pull/1500) ([samsonasik](https://github.com/samsonasik)) +- Fix form\_helper's set\_value writeup [\#1499](https://github.com/codeigniter4/CodeIgniter4/pull/1499) ([jim-parry](https://github.com/jim-parry)) +- Add CURLRequest helper methods [\#1498](https://github.com/codeigniter4/CodeIgniter4/pull/1498) ([natanfelles](https://github.com/natanfelles)) +- Remove unused RedirectException and add some PHPDocs [\#1497](https://github.com/codeigniter4/CodeIgniter4/pull/1497) ([natanfelles](https://github.com/natanfelles)) +- Fix Common::old\(\) [\#1496](https://github.com/codeigniter4/CodeIgniter4/pull/1496) ([jim-parry](https://github.com/jim-parry)) +- Add URI segment test [\#1495](https://github.com/codeigniter4/CodeIgniter4/pull/1495) ([natanfelles](https://github.com/natanfelles)) +- Method naming [\#1494](https://github.com/codeigniter4/CodeIgniter4/pull/1494) ([ghost](https://github.com/ghost)) +- Error logging [\#1491](https://github.com/codeigniter4/CodeIgniter4/pull/1491) ([jim-parry](https://github.com/jim-parry)) +- Changelog\(s\) restructure [\#1490](https://github.com/codeigniter4/CodeIgniter4/pull/1490) ([jim-parry](https://github.com/jim-parry)) +- Add CLI::strlen\(\) [\#1489](https://github.com/codeigniter4/CodeIgniter4/pull/1489) ([natanfelles](https://github.com/natanfelles)) +- Load Language strings from other locations [\#1488](https://github.com/codeigniter4/CodeIgniter4/pull/1488) ([natanfelles](https://github.com/natanfelles)) +- Test RedirectResponse problem report [\#1486](https://github.com/codeigniter4/CodeIgniter4/pull/1486) ([jim-parry](https://github.com/jim-parry)) +- missing slash [\#1484](https://github.com/codeigniter4/CodeIgniter4/pull/1484) ([titounnes](https://github.com/titounnes)) +- Small typo in Session\Handlers\BaseHandler.php [\#1483](https://github.com/codeigniter4/CodeIgniter4/pull/1483) ([obozdag](https://github.com/obozdag)) +- doc fix: query binding fix in Seeds documentation [\#1482](https://github.com/codeigniter4/CodeIgniter4/pull/1482) ([samsonasik](https://github.com/samsonasik)) +- RedisHandler test clean up: remove unneeded 2nd parameter in \_\_construct [\#1481](https://github.com/codeigniter4/CodeIgniter4/pull/1481) ([samsonasik](https://github.com/samsonasik)) +- Fix Language Key-File confusion [\#1480](https://github.com/codeigniter4/CodeIgniter4/pull/1480) ([puschie286](https://github.com/puschie286)) +- Yet another time test to fix [\#1479](https://github.com/codeigniter4/CodeIgniter4/pull/1479) ([jim-parry](https://github.com/jim-parry)) +- Add Response send testing [\#1477](https://github.com/codeigniter4/CodeIgniter4/pull/1477) ([jim-parry](https://github.com/jim-parry)) +- Correct phpdocs for Forge::addField\(\) [\#1475](https://github.com/codeigniter4/CodeIgniter4/pull/1475) ([jim-parry](https://github.com/jim-parry)) +- Fuzzify another time test [\#1473](https://github.com/codeigniter4/CodeIgniter4/pull/1473) ([jim-parry](https://github.com/jim-parry)) +- HTTP\Response cookie testing & missing functionality [\#1472](https://github.com/codeigniter4/CodeIgniter4/pull/1472) ([jim-parry](https://github.com/jim-parry)) +- remove unused local variable $result in XMLFormatter::format\(\) [\#1471](https://github.com/codeigniter4/CodeIgniter4/pull/1471) ([samsonasik](https://github.com/samsonasik)) +- Allow create table with array field constraints [\#1470](https://github.com/codeigniter4/CodeIgniter4/pull/1470) ([natanfelles](https://github.com/natanfelles)) +- use static:: instead of self:: for call protected/public functions as well [\#1469](https://github.com/codeigniter4/CodeIgniter4/pull/1469) ([samsonasik](https://github.com/samsonasik)) +- Fix FeatureTestCaseTest output buffer [\#1468](https://github.com/codeigniter4/CodeIgniter4/pull/1468) ([puschie286](https://github.com/puschie286)) +- Provide time testing within tolerance [\#1467](https://github.com/codeigniter4/CodeIgniter4/pull/1467) ([jim-parry](https://github.com/jim-parry)) +- Fix phpdocs for BaseBuilder [\#1466](https://github.com/codeigniter4/CodeIgniter4/pull/1466) ([jim-parry](https://github.com/jim-parry)) +- use static:: instead of self:: for protected and public properties [\#1465](https://github.com/codeigniter4/CodeIgniter4/pull/1465) ([samsonasik](https://github.com/samsonasik)) +- remove unused use statements [\#1464](https://github.com/codeigniter4/CodeIgniter4/pull/1464) ([samsonasik](https://github.com/samsonasik)) +- Fix the remaining bcit-ci references [\#1463](https://github.com/codeigniter4/CodeIgniter4/pull/1463) ([jim-parry](https://github.com/jim-parry)) +- Typo fix: donload -\> download [\#1461](https://github.com/codeigniter4/CodeIgniter4/pull/1461) ([samsonasik](https://github.com/samsonasik)) +- remove unneeded ternary check at HoneyPot::hasContent\(\) [\#1460](https://github.com/codeigniter4/CodeIgniter4/pull/1460) ([samsonasik](https://github.com/samsonasik)) +- WIP use $paths-\>systemDirectory in public/index.php [\#1457](https://github.com/codeigniter4/CodeIgniter4/pull/1457) ([samsonasik](https://github.com/samsonasik)) +- Beef up HTTP URI & Response testing [\#1456](https://github.com/codeigniter4/CodeIgniter4/pull/1456) ([jim-parry](https://github.com/jim-parry)) +- WIP un-ignore application/Database/Migrations directory from .gitignore [\#1455](https://github.com/codeigniter4/CodeIgniter4/pull/1455) ([samsonasik](https://github.com/samsonasik)) +- add missing break; in loop at Email::getEncoding\(\) [\#1454](https://github.com/codeigniter4/CodeIgniter4/pull/1454) ([samsonasik](https://github.com/samsonasik)) +- BugFix if there extension has only one mime type \(string\) [\#1453](https://github.com/codeigniter4/CodeIgniter4/pull/1453) ([nowackipawel](https://github.com/nowackipawel)) +- remove unneeded $session-\>start\(\); check on RedirectResponse::ensureSession\(\) [\#1451](https://github.com/codeigniter4/CodeIgniter4/pull/1451) ([samsonasik](https://github.com/samsonasik)) +- phpcbf: fix all at once [\#1450](https://github.com/codeigniter4/CodeIgniter4/pull/1450) ([natanfelles](https://github.com/natanfelles)) +- Simplify how to get indexData from mysql/mariadb [\#1449](https://github.com/codeigniter4/CodeIgniter4/pull/1449) ([natanfelles](https://github.com/natanfelles)) +- documentation: add missing application structures: Database, Filters, ThirdParty directory [\#1448](https://github.com/codeigniter4/CodeIgniter4/pull/1448) ([samsonasik](https://github.com/samsonasik)) +- add missing break; on loop cards to get card info at CreditCardRules::valid\_cc\_number\(\) [\#1447](https://github.com/codeigniter4/CodeIgniter4/pull/1447) ([samsonasik](https://github.com/samsonasik)) +- using existing is\_cli\(\) function in HTTP\IncomingRequest::isCLI\(\) [\#1445](https://github.com/codeigniter4/CodeIgniter4/pull/1445) ([samsonasik](https://github.com/samsonasik)) +- Dox for reorganized repo admin \(4of4\) [\#1444](https://github.com/codeigniter4/CodeIgniter4/pull/1444) ([jim-parry](https://github.com/jim-parry)) +- Fixes \#1435 : unit test output not captured [\#1443](https://github.com/codeigniter4/CodeIgniter4/pull/1443) ([samsonasik](https://github.com/samsonasik)) +- remove form view in application/View/ and form helper usage in create new items tutorial [\#1442](https://github.com/codeigniter4/CodeIgniter4/pull/1442) ([samsonasik](https://github.com/samsonasik)) +- Access to model's last inserted ID [\#1440](https://github.com/codeigniter4/CodeIgniter4/pull/1440) ([nowackipawel](https://github.com/nowackipawel)) +- Tailor the last few repo org names \(3of4\) [\#1438](https://github.com/codeigniter4/CodeIgniter4/pull/1438) ([jim-parry](https://github.com/jim-parry)) +- Replace repo org name in MOST php docs \(2 of 4\) [\#1437](https://github.com/codeigniter4/CodeIgniter4/pull/1437) ([jim-parry](https://github.com/jim-parry)) +- Change github organization name in docs \(1of4\) [\#1436](https://github.com/codeigniter4/CodeIgniter4/pull/1436) ([jim-parry](https://github.com/jim-parry)) +- Use mb\_strlen to get length of columns [\#1432](https://github.com/codeigniter4/CodeIgniter4/pull/1432) ([natanfelles](https://github.com/natanfelles)) +- can't call run\(\) method with params from commands migrations. [\#1431](https://github.com/codeigniter4/CodeIgniter4/pull/1431) ([bangbangda](https://github.com/bangbangda)) +- performance improvement in Database\BaseResult to use truthy check instead of count\($var\) when possible [\#1426](https://github.com/codeigniter4/CodeIgniter4/pull/1426) ([samsonasik](https://github.com/samsonasik)) +- Ensure FileHandlerTest uses MockFileHandler [\#1425](https://github.com/codeigniter4/CodeIgniter4/pull/1425) ([jim-parry](https://github.com/jim-parry)) +- Fix FileMovingTest leaving cruft [\#1424](https://github.com/codeigniter4/CodeIgniter4/pull/1424) ([jim-parry](https://github.com/jim-parry)) +- Fix Controller use validate bug Fixes \#1419 [\#1423](https://github.com/codeigniter4/CodeIgniter4/pull/1423) ([bangbangda](https://github.com/bangbangda)) +- normalize composer.json [\#1418](https://github.com/codeigniter4/CodeIgniter4/pull/1418) ([samsonasik](https://github.com/samsonasik)) +- add php 7.3 to travis config [\#1394](https://github.com/codeigniter4/CodeIgniter4/pull/1394) ([samsonasik](https://github.com/samsonasik)) +- Add Header Link Pagination [\#622](https://github.com/codeigniter4/CodeIgniter4/pull/622) ([natanfelles](https://github.com/natanfelles)) + +## [v4.0.0-alpha.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-alpha.2) (2018-10-26) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.0-alpha.1...v4.0.0-alpha.2) + +**Implemented enhancements:** + +- Filter in the router [\#1315](https://github.com/codeigniter4/CodeIgniter4/issues/1315) +- Making Views path changeable [\#1296](https://github.com/codeigniter4/CodeIgniter4/issues/1296) + +**Fixed bugs:** + +- Error in user guide for session config [\#1330](https://github.com/codeigniter4/CodeIgniter4/issues/1330) +- Route in the News Tutorial Routes are ERROR all over. [\#1240](https://github.com/codeigniter4/CodeIgniter4/issues/1240) +- Time testing in travis-ci wonky [\#1229](https://github.com/codeigniter4/CodeIgniter4/issues/1229) + +**Closed issues:** + +- CLI CommandRunner is trying to instantiate abstract classes [\#1349](https://github.com/codeigniter4/CodeIgniter4/issues/1349) +- redirect\(\)-\> problem [\#1346](https://github.com/codeigniter4/CodeIgniter4/issues/1346) +- Question new form validation rule [\#1332](https://github.com/codeigniter4/CodeIgniter4/issues/1332) +- Download Response Not working [\#1331](https://github.com/codeigniter4/CodeIgniter4/issues/1331) +- Incorrect Error Message ? [\#1328](https://github.com/codeigniter4/CodeIgniter4/issues/1328) +- Wrong variable reference [\#1324](https://github.com/codeigniter4/CodeIgniter4/issues/1324) +- Model Pagination: Problem with total rows [\#1318](https://github.com/codeigniter4/CodeIgniter4/issues/1318) +- Should display an exception when minimum PHP version not met. [\#1307](https://github.com/codeigniter4/CodeIgniter4/issues/1307) +- News Tutorial Error on form submit [\#1301](https://github.com/codeigniter4/CodeIgniter4/issues/1301) +- Small Typo Correction [\#1299](https://github.com/codeigniter4/CodeIgniter4/issues/1299) +- Making config variable global as CI3 [\#1297](https://github.com/codeigniter4/CodeIgniter4/issues/1297) +- Config files aren't discovered automatically when using the config\(\) function. [\#1293](https://github.com/codeigniter4/CodeIgniter4/issues/1293) +- News Tutorial post riute still not working [\#1292](https://github.com/codeigniter4/CodeIgniter4/issues/1292) +- Form Validation [\#1290](https://github.com/codeigniter4/CodeIgniter4/issues/1290) +- News Tutorial Routes [\#1288](https://github.com/codeigniter4/CodeIgniter4/issues/1288) +- I want to separate responsibility of Cast from Entity. [\#1287](https://github.com/codeigniter4/CodeIgniter4/issues/1287) +- error 404 in routing with controller in subdirectories [\#1276](https://github.com/codeigniter4/CodeIgniter4/issues/1276) +- TODO Extending helpers [\#1264](https://github.com/codeigniter4/CodeIgniter4/issues/1264) +- TODO MockResponse needs cookies solution [\#1263](https://github.com/codeigniter4/CodeIgniter4/issues/1263) +- TODO url\_helper needs fixing [\#1260](https://github.com/codeigniter4/CodeIgniter4/issues/1260) +- TODO FileLocator better path checking [\#1252](https://github.com/codeigniter4/CodeIgniter4/issues/1252) +- TODO FileLocator filename sanitizing [\#1251](https://github.com/codeigniter4/CodeIgniter4/issues/1251) +- BUG in form\_hidden with associative array [\#1244](https://github.com/codeigniter4/CodeIgniter4/issues/1244) +- Save entity after selected find results in null data [\#1234](https://github.com/codeigniter4/CodeIgniter4/issues/1234) +- System/Database/Database.php::loadForge returns Connection when using custom DBDriver [\#1225](https://github.com/codeigniter4/CodeIgniter4/issues/1225) + +**Merged pull requests:** + +- Add timing assertion to CIUnitTestCase [\#1361](https://github.com/codeigniter4/CodeIgniter4/pull/1361) ([jim-parry](https://github.com/jim-parry)) +- Testing/commands [\#1356](https://github.com/codeigniter4/CodeIgniter4/pull/1356) ([jim-parry](https://github.com/jim-parry)) +- Handle duplicate HTTP verb and generic rules properly [\#1355](https://github.com/codeigniter4/CodeIgniter4/pull/1355) ([jim-parry](https://github.com/jim-parry)) +- Refresh changelog [\#1352](https://github.com/codeigniter4/CodeIgniter4/pull/1352) ([jim-parry](https://github.com/jim-parry)) +- Checks if class is instantiable and is a command [\#1350](https://github.com/codeigniter4/CodeIgniter4/pull/1350) ([natanfelles](https://github.com/natanfelles)) +- Fix sphinx formatting in sessions [\#1348](https://github.com/codeigniter4/CodeIgniter4/pull/1348) ([jim-parry](https://github.com/jim-parry)) +- Fix sphinx formatting in sessions [\#1347](https://github.com/codeigniter4/CodeIgniter4/pull/1347) ([jim-parry](https://github.com/jim-parry)) +- Toolbar Styles [\#1342](https://github.com/codeigniter4/CodeIgniter4/pull/1342) ([lonnieezell](https://github.com/lonnieezell)) +- Make viewpath configurable in Paths.php. Fixes \#1296 [\#1341](https://github.com/codeigniter4/CodeIgniter4/pull/1341) ([lonnieezell](https://github.com/lonnieezell)) +- Update docs for downloads to reflect the need to return it. Fixes \#1331 [\#1340](https://github.com/codeigniter4/CodeIgniter4/pull/1340) ([lonnieezell](https://github.com/lonnieezell)) +- Fix error where Forge class might not be returned. Fixes \#1225 [\#1339](https://github.com/codeigniter4/CodeIgniter4/pull/1339) ([lonnieezell](https://github.com/lonnieezell)) +- Filter in the router Fixes \#1315 [\#1337](https://github.com/codeigniter4/CodeIgniter4/pull/1337) ([bangbangda](https://github.com/bangbangda)) +- Revert alpha.2 [\#1336](https://github.com/codeigniter4/CodeIgniter4/pull/1336) ([jim-parry](https://github.com/jim-parry)) +- Proposed changelog for alpha.2 [\#1334](https://github.com/codeigniter4/CodeIgniter4/pull/1334) ([jim-parry](https://github.com/jim-parry)) +- Error in user guide for session config. Fixes \#1330 [\#1333](https://github.com/codeigniter4/CodeIgniter4/pull/1333) ([bangbangda](https://github.com/bangbangda)) +- Tweaks [\#1329](https://github.com/codeigniter4/CodeIgniter4/pull/1329) ([lonnieezell](https://github.com/lonnieezell)) +- FIX form\_hidden and form\_open - value escaping as is in form\_input. [\#1327](https://github.com/codeigniter4/CodeIgniter4/pull/1327) ([nowackipawel](https://github.com/nowackipawel)) +- Fix doc error : show\_404\(\) doesn't exist any more [\#1323](https://github.com/codeigniter4/CodeIgniter4/pull/1323) ([bvrignaud](https://github.com/bvrignaud)) +- Added missing xml\_helper UG page [\#1321](https://github.com/codeigniter4/CodeIgniter4/pull/1321) ([jim-parry](https://github.com/jim-parry)) +- Testing/entity [\#1319](https://github.com/codeigniter4/CodeIgniter4/pull/1319) ([jim-parry](https://github.com/jim-parry)) +- Refactor TimeTest [\#1316](https://github.com/codeigniter4/CodeIgniter4/pull/1316) ([jim-parry](https://github.com/jim-parry)) +- Fix & expand Honeypot & its tests [\#1314](https://github.com/codeigniter4/CodeIgniter4/pull/1314) ([jim-parry](https://github.com/jim-parry)) +- Clean exception [\#1313](https://github.com/codeigniter4/CodeIgniter4/pull/1313) ([lonnieezell](https://github.com/lonnieezell)) +- Add headerEmited \(or not\) assertions to CIUnitTestCase [\#1312](https://github.com/codeigniter4/CodeIgniter4/pull/1312) ([jim-parry](https://github.com/jim-parry)) +- Entities store an original stack of values to compare against so we d… [\#1311](https://github.com/codeigniter4/CodeIgniter4/pull/1311) ([lonnieezell](https://github.com/lonnieezell)) +- Testing3/http [\#1306](https://github.com/codeigniter4/CodeIgniter4/pull/1306) ([jim-parry](https://github.com/jim-parry)) +- Change chdir\('public'\) to chdir\($public\) [\#1305](https://github.com/codeigniter4/CodeIgniter4/pull/1305) ([titounnes](https://github.com/titounnes)) +- Refactor script name stripping in parseRequestURI\(\) [\#1304](https://github.com/codeigniter4/CodeIgniter4/pull/1304) ([jim-parry](https://github.com/jim-parry)) +- Testing/http [\#1303](https://github.com/codeigniter4/CodeIgniter4/pull/1303) ([jim-parry](https://github.com/jim-parry)) +- Exception:No Formatter defined for mime type '' [\#1302](https://github.com/codeigniter4/CodeIgniter4/pull/1302) ([bangbangda](https://github.com/bangbangda)) +- Allow redirect with Query Vars from the current request. [\#1300](https://github.com/codeigniter4/CodeIgniter4/pull/1300) ([lonnieezell](https://github.com/lonnieezell)) +- Fix grammar in front controller comment. [\#1295](https://github.com/codeigniter4/CodeIgniter4/pull/1295) ([mdwheele](https://github.com/mdwheele)) +- Updated final tutorial page. Fixes \#1292 [\#1294](https://github.com/codeigniter4/CodeIgniter4/pull/1294) ([lonnieezell](https://github.com/lonnieezell)) +- Allows extending of helpers. Fixes \#1264 [\#1291](https://github.com/codeigniter4/CodeIgniter4/pull/1291) ([lonnieezell](https://github.com/lonnieezell)) +- Cookies [\#1286](https://github.com/codeigniter4/CodeIgniter4/pull/1286) ([lonnieezell](https://github.com/lonnieezell)) +- Ensure current HTTP verb routes are matched prior to any \* matched ro… [\#1285](https://github.com/codeigniter4/CodeIgniter4/pull/1285) ([lonnieezell](https://github.com/lonnieezell)) +- Entities [\#1283](https://github.com/codeigniter4/CodeIgniter4/pull/1283) ([lonnieezell](https://github.com/lonnieezell)) +- system/Test/FeatureTestCase::setupRequest\(\), minor fixes phpdoc block… [\#1282](https://github.com/codeigniter4/CodeIgniter4/pull/1282) ([fmertins](https://github.com/fmertins)) +- Tut [\#1281](https://github.com/codeigniter4/CodeIgniter4/pull/1281) ([lonnieezell](https://github.com/lonnieezell)) +- Add contributing reference to user guide [\#1280](https://github.com/codeigniter4/CodeIgniter4/pull/1280) ([jim-parry](https://github.com/jim-parry)) +- Fix/timing [\#1273](https://github.com/codeigniter4/CodeIgniter4/pull/1273) ([jim-parry](https://github.com/jim-parry)) +- Fix undefined variable "heading" in cli 404 [\#1272](https://github.com/codeigniter4/CodeIgniter4/pull/1272) ([samsonasik](https://github.com/samsonasik)) +- remove inexistent "CodeIgniter\Loader" from AutoloadConfig::classmap [\#1271](https://github.com/codeigniter4/CodeIgniter4/pull/1271) ([samsonasik](https://github.com/samsonasik)) +- Release notes & process [\#1269](https://github.com/codeigniter4/CodeIgniter4/pull/1269) ([jim-parry](https://github.com/jim-parry)) +- Fix \#1244 \(form\_hidden declaration\) [\#1245](https://github.com/codeigniter4/CodeIgniter4/pull/1245) ([bvrignaud](https://github.com/bvrignaud)) +- 【Unsolicited PR】I changed the download method to testable. [\#1239](https://github.com/codeigniter4/CodeIgniter4/pull/1239) ([ytetsuro](https://github.com/ytetsuro)) +- Optional parameter for resetSelect\(\) call in Builder's countAll\(\); [\#1217](https://github.com/codeigniter4/CodeIgniter4/pull/1217) ([nowackipawel](https://github.com/nowackipawel)) +- Fix undefined function xml\_convert at Database\BaseUtils [\#1209](https://github.com/codeigniter4/CodeIgniter4/pull/1209) ([samsonasik](https://github.com/samsonasik)) + +## [v4.0.0-alpha.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.0.0-alpha.1) (2018-09-29) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/0d92381e74730331626b49e6f259d9073727c4f1...v4.0.0-alpha.1) + +**Implemented enhancements:** + +- Parser plugins should be allowed to have non-paired directives [\#547](https://github.com/codeigniter4/CodeIgniter4/issues/547) +- Modify View Parser to support quoted phrases [\#537](https://github.com/codeigniter4/CodeIgniter4/issues/537) +- Move Formatting functionality from the API namespace to it's own namespace [\#451](https://github.com/codeigniter4/CodeIgniter4/issues/451) +- Add method spoofing for forms. [\#432](https://github.com/codeigniter4/CodeIgniter4/issues/432) +- Add overwrite option to UploadedFile-\>move\(\) [\#275](https://github.com/codeigniter4/CodeIgniter4/issues/275) +- Add Download method to Response object. [\#208](https://github.com/codeigniter4/CodeIgniter4/issues/208) +- Cache/Session fallback to predis/predis composer package if phpredis not available [\#192](https://github.com/codeigniter4/CodeIgniter4/issues/192) +- View: saveData [\#181](https://github.com/codeigniter4/CodeIgniter4/issues/181) +- url\_helper implementation [\#157](https://github.com/codeigniter4/CodeIgniter4/issues/157) +- Add true 'prepare' functionality to Queries [\#131](https://github.com/codeigniter4/CodeIgniter4/issues/131) +- Add :hash placeholder for URI parameters [\#130](https://github.com/codeigniter4/CodeIgniter4/issues/130) +- \[URI\] Provide ability for query vars manipulation [\#119](https://github.com/codeigniter4/CodeIgniter4/issues/119) +- \[FilesCollection\] Allow getFiles with dot syntax [\#112](https://github.com/codeigniter4/CodeIgniter4/issues/112) +- Database to fire events [\#105](https://github.com/codeigniter4/CodeIgniter4/issues/105) +- New Toolbar Collector for Events [\#84](https://github.com/codeigniter4/CodeIgniter4/issues/84) +- Debug Toolbar Enhancements [\#83](https://github.com/codeigniter4/CodeIgniter4/issues/83) +- Add indexing functions to Forge [\#65](https://github.com/codeigniter4/CodeIgniter4/issues/65) +- Refactor DB Backup to stream to file instead of holding in memory [\#64](https://github.com/codeigniter4/CodeIgniter4/issues/64) +- Add support for foreign keys to the Forge [\#63](https://github.com/codeigniter4/CodeIgniter4/issues/63) +- Headers must support multiple headers with same name [\#16](https://github.com/codeigniter4/CodeIgniter4/issues/16) +- Add 'secure' option for Routes [\#10](https://github.com/codeigniter4/CodeIgniter4/issues/10) +- \[Test Helper\] assertLogged [\#9](https://github.com/codeigniter4/CodeIgniter4/issues/9) +- Additional Logger Enhancements [\#8](https://github.com/codeigniter4/CodeIgniter4/issues/8) +- Implement Content Secure Policy [\#6](https://github.com/codeigniter4/CodeIgniter4/issues/6) +- Replace our escaper with Zend Escaper [\#5](https://github.com/codeigniter4/CodeIgniter4/issues/5) +- Redirect Security [\#4](https://github.com/codeigniter4/CodeIgniter4/issues/4) +- Reverse Routing [\#3](https://github.com/codeigniter4/CodeIgniter4/issues/3) + +**Fixed bugs:** + +- Testing output buffer not closed [\#1230](https://github.com/codeigniter4/CodeIgniter4/issues/1230) +- XML formater xmltoarray indexed array incorrect [\#577](https://github.com/codeigniter4/CodeIgniter4/issues/577) +- stringify\_attributes method MUST escape the values [\#282](https://github.com/codeigniter4/CodeIgniter4/issues/282) +- base\_url\(\) function doesn't work properly when it used on page with uri segments [\#240](https://github.com/codeigniter4/CodeIgniter4/issues/240) +- mysqli update bug [\#229](https://github.com/codeigniter4/CodeIgniter4/issues/229) +- database update [\#201](https://github.com/codeigniter4/CodeIgniter4/issues/201) +- Paginating Multiple Results - user Guides [\#196](https://github.com/codeigniter4/CodeIgniter4/issues/196) +- 'Filters' Bug [\#188](https://github.com/codeigniter4/CodeIgniter4/issues/188) +- POST and debugbar [\#172](https://github.com/codeigniter4/CodeIgniter4/issues/172) +- url\_helper functions don't correctly apply $baseURL [\#155](https://github.com/codeigniter4/CodeIgniter4/issues/155) +- Routes ending in '/' redirect oddly [\#147](https://github.com/codeigniter4/CodeIgniter4/issues/147) +- Error when trying access URI with Global Function's name [\#136](https://github.com/codeigniter4/CodeIgniter4/issues/136) +- CLI problem with progress complete's message [\#135](https://github.com/codeigniter4/CodeIgniter4/issues/135) + +**Closed issues:** + +- validation error [\#1214](https://github.com/codeigniter4/CodeIgniter4/issues/1214) +- How to use Controller own constructer? [\#1208](https://github.com/codeigniter4/CodeIgniter4/issues/1208) +- autoload psr4 is not right. [\#1205](https://github.com/codeigniter4/CodeIgniter4/issues/1205) +- multiple rules in validate\(\) for File Upload not working [\#1201](https://github.com/codeigniter4/CodeIgniter4/issues/1201) +- can't use the same model to update, delete and insert record [\#1193](https://github.com/codeigniter4/CodeIgniter4/issues/1193) +- $myModel-\>find\(string "value-of-my-primary-key"\) [\#1188](https://github.com/codeigniter4/CodeIgniter4/issues/1188) +- Undefined variable: \_SESSION in command php spark [\#1183](https://github.com/codeigniter4/CodeIgniter4/issues/1183) +- Typographical error [\#1179](https://github.com/codeigniter4/CodeIgniter4/issues/1179) +- CSP + .kint d\(foo\) [\#1174](https://github.com/codeigniter4/CodeIgniter4/issues/1174) +- DebugBar -\> Server Error 500 [\#1170](https://github.com/codeigniter4/CodeIgniter4/issues/1170) +- NULL in select is escaped \(mysqli\_sql\_exception\) [\#1169](https://github.com/codeigniter4/CodeIgniter4/issues/1169) +- Routing for "cli" actions. [\#1166](https://github.com/codeigniter4/CodeIgniter4/issues/1166) +- Create Auto-discovery system [\#1161](https://github.com/codeigniter4/CodeIgniter4/issues/1161) +- Routing wrong default value [\#1139](https://github.com/codeigniter4/CodeIgniter4/issues/1139) +- $session-\>push gets wrong [\#1136](https://github.com/codeigniter4/CodeIgniter4/issues/1136) +- Toolbar Oldest files delete bug [\#1135](https://github.com/codeigniter4/CodeIgniter4/issues/1135) +- redirect helper : redirectResponse is ignored [\#1127](https://github.com/codeigniter4/CodeIgniter4/issues/1127) +- redirect to route ignore baseurl [\#1126](https://github.com/codeigniter4/CodeIgniter4/issues/1126) +- redirect-\>route wrong docu or default values [\#1125](https://github.com/codeigniter4/CodeIgniter4/issues/1125) +- \#1109 breaks route setup/uri parsing [\#1114](https://github.com/codeigniter4/CodeIgniter4/issues/1114) +- \[TESTS\] Session tests for php 7.2 [\#1106](https://github.com/codeigniter4/CodeIgniter4/issues/1106) +- set\_cookie - not working [\#1103](https://github.com/codeigniter4/CodeIgniter4/issues/1103) +- safe\_mailto - not working [\#1102](https://github.com/codeigniter4/CodeIgniter4/issues/1102) +- CLI tool sorting wrong [\#1099](https://github.com/codeigniter4/CodeIgniter4/issues/1099) +- Issues with redirect [\#1098](https://github.com/codeigniter4/CodeIgniter4/issues/1098) +- Can't use validate with regex\_match\[\] [\#1084](https://github.com/codeigniter4/CodeIgniter4/issues/1084) +- problem with redirect\(\) withInput\(\) when validation [\#1081](https://github.com/codeigniter4/CodeIgniter4/issues/1081) +- Redis Handler Fails [\#1079](https://github.com/codeigniter4/CodeIgniter4/issues/1079) +- about cache path [\#1078](https://github.com/codeigniter4/CodeIgniter4/issues/1078) +- validation error [\#1077](https://github.com/codeigniter4/CodeIgniter4/issues/1077) +- \#Request. Features for REST server. [\#1076](https://github.com/codeigniter4/CodeIgniter4/issues/1076) +- Database Migrations [\#1075](https://github.com/codeigniter4/CodeIgniter4/issues/1075) +- Codeigniter/Model - Select Database Table Fields [\#1072](https://github.com/codeigniter4/CodeIgniter4/issues/1072) +- New Config helper [\#1071](https://github.com/codeigniter4/CodeIgniter4/issues/1071) +- HTTP\ResponseTest Language Problem [\#1069](https://github.com/codeigniter4/CodeIgniter4/issues/1069) +- CLI Error [\#1068](https://github.com/codeigniter4/CodeIgniter4/issues/1068) +- Entity \_options dates [\#1061](https://github.com/codeigniter4/CodeIgniter4/issues/1061) +- class Locale not found when using I18n/Time on xampp localhost [\#1059](https://github.com/codeigniter4/CodeIgniter4/issues/1059) +- Cookie not working [\#1057](https://github.com/codeigniter4/CodeIgniter4/issues/1057) +- Where is class 'MessageFormatter' [\#1054](https://github.com/codeigniter4/CodeIgniter4/issues/1054) +- Is CI 4 ready for production, please? [\#1051](https://github.com/codeigniter4/CodeIgniter4/issues/1051) +- Router 404 [\#1050](https://github.com/codeigniter4/CodeIgniter4/issues/1050) +- Toolbar - memory usage [\#1049](https://github.com/codeigniter4/CodeIgniter4/issues/1049) +- Session saving [\#1045](https://github.com/codeigniter4/CodeIgniter4/issues/1045) +- $Email-\>initialize\($config\) not work! [\#1042](https://github.com/codeigniter4/CodeIgniter4/issues/1042) +- Class '\CodeIgniter\Database\postgre\Connection' not found [\#1038](https://github.com/codeigniter4/CodeIgniter4/issues/1038) +- Tutorial controller Pages and file\_exists\(\) case sensitivity [\#1030](https://github.com/codeigniter4/CodeIgniter4/issues/1030) +- options base\_uri not being based from curlrequest client instantiation [\#1029](https://github.com/codeigniter4/CodeIgniter4/issues/1029) +- Missing method in db result [\#1022](https://github.com/codeigniter4/CodeIgniter4/issues/1022) +- Email Config $fromEmail not work [\#1021](https://github.com/codeigniter4/CodeIgniter4/issues/1021) +- Class 'CodeIgniter\PageNotFoundException' not found [\#1016](https://github.com/codeigniter4/CodeIgniter4/issues/1016) +- Redirect glitch [\#1013](https://github.com/codeigniter4/CodeIgniter4/issues/1013) +- CSRF Error [\#1012](https://github.com/codeigniter4/CodeIgniter4/issues/1012) +- Php serv CLI stopped working [\#1006](https://github.com/codeigniter4/CodeIgniter4/issues/1006) +- Unit testing broken in travis-ci [\#1003](https://github.com/codeigniter4/CodeIgniter4/issues/1003) +- empty php\_errors.log file [\#1001](https://github.com/codeigniter4/CodeIgniter4/issues/1001) +- \[Help\] setVar\('body', $view, 'raw'\) [\#1000](https://github.com/codeigniter4/CodeIgniter4/issues/1000) +- It's blank page and set $baseURL [\#999](https://github.com/codeigniter4/CodeIgniter4/issues/999) +- Unable to use another controller'method in one controller? [\#997](https://github.com/codeigniter4/CodeIgniter4/issues/997) +- CodeIgniter\Session\Handlers\FileHandler Class and writable\session Directory not found while using .env [\#994](https://github.com/codeigniter4/CodeIgniter4/issues/994) +- route\_to\(\) function not work if greater than 3 parameters. [\#992](https://github.com/codeigniter4/CodeIgniter4/issues/992) +- Redirect Back [\#991](https://github.com/codeigniter4/CodeIgniter4/issues/991) +- helper method should accept more than one filename.... [\#987](https://github.com/codeigniter4/CodeIgniter4/issues/987) +- New Feature Request - Sub queries using query builder class [\#985](https://github.com/codeigniter4/CodeIgniter4/issues/985) +- MySQL join / missing value for field which was used in join. [\#983](https://github.com/codeigniter4/CodeIgniter4/issues/983) +- Documentation - Session Library - session\(\)-\>start\(\) [\#982](https://github.com/codeigniter4/CodeIgniter4/issues/982) +- Cache with handler file woking incorrect on windown os [\#978](https://github.com/codeigniter4/CodeIgniter4/issues/978) +- Formatter for: "content-type: application/json" [\#977](https://github.com/codeigniter4/CodeIgniter4/issues/977) +- route\_to function working incorrect [\#975](https://github.com/codeigniter4/CodeIgniter4/issues/975) +- Getters and Setters in the Model [\#974](https://github.com/codeigniter4/CodeIgniter4/issues/974) +- About Replacing Core Classes! [\#973](https://github.com/codeigniter4/CodeIgniter4/issues/973) +- CodeIgniter 4 should use PSR-2 [\#972](https://github.com/codeigniter4/CodeIgniter4/issues/972) +- Model Validations and Insert/Update Batchs [\#967](https://github.com/codeigniter4/CodeIgniter4/issues/967) +- function old\(\) not allow input name type array [\#966](https://github.com/codeigniter4/CodeIgniter4/issues/966) +- test [\#963](https://github.com/codeigniter4/CodeIgniter4/issues/963) +- namespace error in centos7 [\#959](https://github.com/codeigniter4/CodeIgniter4/issues/959) +- Session cannot be saved [\#958](https://github.com/codeigniter4/CodeIgniter4/issues/958) +- Model calling Query Builder replace\(\) [\#957](https://github.com/codeigniter4/CodeIgniter4/issues/957) +- gzuncompress\(\): data error [\#956](https://github.com/codeigniter4/CodeIgniter4/issues/956) +- Additional space in a parameter which is a string \(mysql\) [\#955](https://github.com/codeigniter4/CodeIgniter4/issues/955) +- Pager with search queries [\#950](https://github.com/codeigniter4/CodeIgniter4/issues/950) +- IncomingRequest getUserAgent problem only object [\#948](https://github.com/codeigniter4/CodeIgniter4/issues/948) +- url\_helper current\_url https fix. [\#947](https://github.com/codeigniter4/CodeIgniter4/issues/947) +- Validation "matches" rule is not getting the label from the corresponding field [\#946](https://github.com/codeigniter4/CodeIgniter4/issues/946) +- no luck setting up user\_guide [\#944](https://github.com/codeigniter4/CodeIgniter4/issues/944) +- mysqli\_sql\_exception: No such file or directory [\#943](https://github.com/codeigniter4/CodeIgniter4/issues/943) +- Major Security Flaw - Database Credentials get leaked. [\#935](https://github.com/codeigniter4/CodeIgniter4/issues/935) +- Debug Toolbar is not loaded when CSP is enabled [\#934](https://github.com/codeigniter4/CodeIgniter4/issues/934) +- This is a problem with frequent operations [\#933](https://github.com/codeigniter4/CodeIgniter4/issues/933) +- Redirects not working [\#931](https://github.com/codeigniter4/CodeIgniter4/issues/931) +- Model fillPlaceholders\(\) rule reference [\#930](https://github.com/codeigniter4/CodeIgniter4/issues/930) +- session in not working on database [\#925](https://github.com/codeigniter4/CodeIgniter4/issues/925) +- Twig [\#919](https://github.com/codeigniter4/CodeIgniter4/issues/919) +- setDefaultNamespace Sub directories [\#917](https://github.com/codeigniter4/CodeIgniter4/issues/917) +- Load language issue [\#913](https://github.com/codeigniter4/CodeIgniter4/issues/913) +- Find a bug ,CSRFVerify [\#912](https://github.com/codeigniter4/CodeIgniter4/issues/912) +- validation form\_error\(\) [\#911](https://github.com/codeigniter4/CodeIgniter4/issues/911) +- About Filters matching rules [\#908](https://github.com/codeigniter4/CodeIgniter4/issues/908) +- Prepared Query Update problem [\#904](https://github.com/codeigniter4/CodeIgniter4/issues/904) +- Model::first\(\) ambiguous id error [\#903](https://github.com/codeigniter4/CodeIgniter4/issues/903) +- Add Model beforeDelete property [\#902](https://github.com/codeigniter4/CodeIgniter4/issues/902) +- passing form\_validation\(\) errors with redirect\(\) to any view | and include methods for post only by using \[ HTTP verbs in routes \] and Resource route [\#900](https://github.com/codeigniter4/CodeIgniter4/issues/900) +- Database failover error on postgresql [\#899](https://github.com/codeigniter4/CodeIgniter4/issues/899) +- valid\_email validation error [\#898](https://github.com/codeigniter4/CodeIgniter4/issues/898) +- Multi language \( on same page \) support [\#891](https://github.com/codeigniter4/CodeIgniter4/issues/891) +- About environnement error\_reporting [\#889](https://github.com/codeigniter4/CodeIgniter4/issues/889) +- Subfolders for lang\(\) [\#887](https://github.com/codeigniter4/CodeIgniter4/issues/887) +- Urgent issues [\#875](https://github.com/codeigniter4/CodeIgniter4/issues/875) +- remark [\#871](https://github.com/codeigniter4/CodeIgniter4/issues/871) +- Bug fix mysqli transaction function call [\#870](https://github.com/codeigniter4/CodeIgniter4/issues/870) +- Debugbar SSL request javascript problem [\#867](https://github.com/codeigniter4/CodeIgniter4/issues/867) +- Backslashes being escaped in where conditions. [\#866](https://github.com/codeigniter4/CodeIgniter4/issues/866) +- Model::delete method [\#865](https://github.com/codeigniter4/CodeIgniter4/issues/865) +- about SessionHandlerInterface error [\#864](https://github.com/codeigniter4/CodeIgniter4/issues/864) +- Respond Error Pages by Content-Type [\#863](https://github.com/codeigniter4/CodeIgniter4/issues/863) +- When codeigniter 4 will be released ? [\#860](https://github.com/codeigniter4/CodeIgniter4/issues/860) +- Catch in Controllers/Checks.php [\#859](https://github.com/codeigniter4/CodeIgniter4/issues/859) +- View render function LFI\(local arbitray file include\) issue [\#857](https://github.com/codeigniter4/CodeIgniter4/issues/857) +- Suggestion regarding codeigniter 4 [\#856](https://github.com/codeigniter4/CodeIgniter4/issues/856) +- Problem with using Entity class \(Could not execute App\Entities\::\_\_construct\(\)\) [\#855](https://github.com/codeigniter4/CodeIgniter4/issues/855) +- Config Unexpectedly Being Overwritten by $\_ENV variable [\#853](https://github.com/codeigniter4/CodeIgniter4/issues/853) +- Use PHP 7.0 or 7.1 typehints? [\#847](https://github.com/codeigniter4/CodeIgniter4/issues/847) +- There is something wrong with ViewTest::testRenderScrapsDataByDefault [\#846](https://github.com/codeigniter4/CodeIgniter4/issues/846) +- Session in filter [\#840](https://github.com/codeigniter4/CodeIgniter4/issues/840) +- Moved uploaded files doesn't retain new filename. [\#839](https://github.com/codeigniter4/CodeIgniter4/issues/839) +- Unable to use like in where? [\#838](https://github.com/codeigniter4/CodeIgniter4/issues/838) +- Status of the version [\#837](https://github.com/codeigniter4/CodeIgniter4/issues/837) +- when set cache to file, when get a error [\#836](https://github.com/codeigniter4/CodeIgniter4/issues/836) +- Bug toolbar [\#834](https://github.com/codeigniter4/CodeIgniter4/issues/834) +- Table name can not use the alias [\#831](https://github.com/codeigniter4/CodeIgniter4/issues/831) +- Validation - permit\_empty [\#830](https://github.com/codeigniter4/CodeIgniter4/issues/830) +- Unable to install Codeigniter using composer [\#829](https://github.com/codeigniter4/CodeIgniter4/issues/829) +- Throttler usage [\#827](https://github.com/codeigniter4/CodeIgniter4/issues/827) +- Display Label on Form Validation [\#826](https://github.com/codeigniter4/CodeIgniter4/issues/826) +- Question about Filters [\#824](https://github.com/codeigniter4/CodeIgniter4/issues/824) +- uri-\>getHost\(\) [\#815](https://github.com/codeigniter4/CodeIgniter4/issues/815) +- Error changing databases CodeIgniter\Database\MySQLi\Connection::dbSelect\(\) [\#813](https://github.com/codeigniter4/CodeIgniter4/issues/813) +- Create a skeleton system for codeigniter [\#806](https://github.com/codeigniter4/CodeIgniter4/issues/806) +- Feature - CLI Prompt with Validation [\#800](https://github.com/codeigniter4/CodeIgniter4/issues/800) +- Routing problem /sth-abc-\>App:sth/foo \(ok\) and /sth/abc-\>App:sth/foo \(nok\) [\#799](https://github.com/codeigniter4/CodeIgniter4/issues/799) +- Update with delete value [\#796](https://github.com/codeigniter4/CodeIgniter4/issues/796) +- redirect\(\)-\>to\(\) is changing value of the base64 parameter [\#790](https://github.com/codeigniter4/CodeIgniter4/issues/790) +- View Parser escaping data even if passed 'raw' as context in setData\(\) [\#788](https://github.com/codeigniter4/CodeIgniter4/issues/788) +- Cant instantiate Parser Directly. [\#787](https://github.com/codeigniter4/CodeIgniter4/issues/787) +- about redis error [\#783](https://github.com/codeigniter4/CodeIgniter4/issues/783) +- Validation issue and routing issue [\#782](https://github.com/codeigniter4/CodeIgniter4/issues/782) +- Query binding stopped working [\#781](https://github.com/codeigniter4/CodeIgniter4/issues/781) +- Entity exception in line 270 and 143 \(current version\). [\#780](https://github.com/codeigniter4/CodeIgniter4/issues/780) +- about Error Handling [\#778](https://github.com/codeigniter4/CodeIgniter4/issues/778) +- $this-\>CI-\>request-\>getIPAddress\(\) protected [\#776](https://github.com/codeigniter4/CodeIgniter4/issues/776) +- How do you access the $this bound data of the controller in the view? Ci3 is OK, ci4 doesn't seem to work [\#775](https://github.com/codeigniter4/CodeIgniter4/issues/775) +- I wish I could call the $this of the controller in the business model. What's the solution? [\#774](https://github.com/codeigniter4/CodeIgniter4/issues/774) +- Ignore\_value should be the value of ID, how do I get it? [\#772](https://github.com/codeigniter4/CodeIgniter4/issues/772) +- Can you increase the function of unique values in the test database? [\#771](https://github.com/codeigniter4/CodeIgniter4/issues/771) +- about where\(null\) [\#770](https://github.com/codeigniter4/CodeIgniter4/issues/770) +- redirect\(\)-\>to\(\) problem with output buffering [\#769](https://github.com/codeigniter4/CodeIgniter4/issues/769) +- Class 'App\Models\NewsModel' not found [\#766](https://github.com/codeigniter4/CodeIgniter4/issues/766) +- Undefined property: Config\App::$errorViewPath [\#765](https://github.com/codeigniter4/CodeIgniter4/issues/765) +- Zend/Escaper too big ,its not necessary [\#764](https://github.com/codeigniter4/CodeIgniter4/issues/764) +- .htaccess on php7.0.12 nts not work. [\#763](https://github.com/codeigniter4/CodeIgniter4/issues/763) +- DebugBar Routes Collector throws ErrorException when optional parameter is not passed to controller [\#762](https://github.com/codeigniter4/CodeIgniter4/issues/762) +- Debug View change page structure [\#761](https://github.com/codeigniter4/CodeIgniter4/issues/761) +- Debug Tool: Show view filepath [\#758](https://github.com/codeigniter4/CodeIgniter4/issues/758) +- PHPCBF - CodeIgniter4-Standard [\#757](https://github.com/codeigniter4/CodeIgniter4/issues/757) +- about errorViewPath [\#745](https://github.com/codeigniter4/CodeIgniter4/issues/745) +- Combine Composer with Install [\#744](https://github.com/codeigniter4/CodeIgniter4/issues/744) +- I have multiple applications. How can I share helpers and Class Map in multiple applications? [\#742](https://github.com/codeigniter4/CodeIgniter4/issues/742) +- I have multiple applications. How can I share helpers and Class Map in multiple applications? [\#741](https://github.com/codeigniter4/CodeIgniter4/issues/741) +- When can the fourth version be released, too much [\#738](https://github.com/codeigniter4/CodeIgniter4/issues/738) +- about Sub-directories [\#737](https://github.com/codeigniter4/CodeIgniter4/issues/737) +- About 【Use Config\Services as CodeIgniter\Services】 [\#735](https://github.com/codeigniter4/CodeIgniter4/issues/735) +- Mysqli Driver doesn't throw sql level exceptions / error [\#734](https://github.com/codeigniter4/CodeIgniter4/issues/734) +- LogicException \#403 - CSRF [\#733](https://github.com/codeigniter4/CodeIgniter4/issues/733) +- User guide for ci4 is not responsive on mobile.\(screen width less then 769px\) [\#730](https://github.com/codeigniter4/CodeIgniter4/issues/730) +- Composer intall, as described in documentation, points to different github repository\(!\) [\#726](https://github.com/codeigniter4/CodeIgniter4/issues/726) +- Fatal error when trying to run using PHP built in server [\#725](https://github.com/codeigniter4/CodeIgniter4/issues/725) +- Passing array as argument in Parser Plugin not working [\#724](https://github.com/codeigniter4/CodeIgniter4/issues/724) +- $request-\>uri-\>getSegment\(\); return error [\#722](https://github.com/codeigniter4/CodeIgniter4/issues/722) +- Double quotation in getFieldData\(\); [\#721](https://github.com/codeigniter4/CodeIgniter4/issues/721) +- Should Model class implement the magic setter/getter methods for protected properties? [\#720](https://github.com/codeigniter4/CodeIgniter4/issues/720) +- Forge class trying to create UNSIGNED integer fields in Postgresql [\#719](https://github.com/codeigniter4/CodeIgniter4/issues/719) +- Tests Failing [\#717](https://github.com/codeigniter4/CodeIgniter4/issues/717) +- Form validation not working when Passing Empty array \[ \] [\#715](https://github.com/codeigniter4/CodeIgniter4/issues/715) +- Redirects to wrong incomplete URL [\#710](https://github.com/codeigniter4/CodeIgniter4/issues/710) +- pg\_escape\_literal\(\) expects parameter 1 to be resource, boolean given [\#709](https://github.com/codeigniter4/CodeIgniter4/issues/709) +- Separate DebugToolbar from CI\_DEBUG [\#707](https://github.com/codeigniter4/CodeIgniter4/issues/707) +- common.php [\#706](https://github.com/codeigniter4/CodeIgniter4/issues/706) +- Parser::parsePair fails if template contains a $ sign [\#705](https://github.com/codeigniter4/CodeIgniter4/issues/705) +- view parser throws InvalidArgumentException [\#704](https://github.com/codeigniter4/CodeIgniter4/issues/704) +- Auto redirect to root folder [\#703](https://github.com/codeigniter4/CodeIgniter4/issues/703) +- ErrorException \#1 htmlspecialchars\(\) expects parameter 1 to be string [\#702](https://github.com/codeigniter4/CodeIgniter4/issues/702) +- Support for Labels in Validation [\#696](https://github.com/codeigniter4/CodeIgniter4/issues/696) +- Why is system/bootstrap.php lowercase? [\#695](https://github.com/codeigniter4/CodeIgniter4/issues/695) +- To much escaping in where [\#691](https://github.com/codeigniter4/CodeIgniter4/issues/691) +- session in DB not working [\#690](https://github.com/codeigniter4/CodeIgniter4/issues/690) +- imagecopyresampled\(\): supplied resource is not a valid Image resource [\#689](https://github.com/codeigniter4/CodeIgniter4/issues/689) +- Postgres json column search error [\#680](https://github.com/codeigniter4/CodeIgniter4/issues/680) +- multiple File upload issue [\#679](https://github.com/codeigniter4/CodeIgniter4/issues/679) +- Postgres Json type error on save [\#677](https://github.com/codeigniter4/CodeIgniter4/issues/677) +- show strange chracters in kint.php inside system/ThirdParty folder. [\#676](https://github.com/codeigniter4/CodeIgniter4/issues/676) +- Need Parser plugin for validation errors. [\#675](https://github.com/codeigniter4/CodeIgniter4/issues/675) +- Fatal error: Cannot use Config\Services as Services [\#674](https://github.com/codeigniter4/CodeIgniter4/issues/674) +- Routing issue - bad args in function call. [\#672](https://github.com/codeigniter4/CodeIgniter4/issues/672) +- Entity Concerns [\#662](https://github.com/codeigniter4/CodeIgniter4/issues/662) +- Undefined method CLIRequest::isSecure\(\) [\#656](https://github.com/codeigniter4/CodeIgniter4/issues/656) +- PREG\_QUOTE and SQL String Binding [\#655](https://github.com/codeigniter4/CodeIgniter4/issues/655) +- Discussions on Pagination hasNext\(\) and hasPrevious\(\) [\#651](https://github.com/codeigniter4/CodeIgniter4/issues/651) +- Feature requested- Auth and Template Engine [\#647](https://github.com/codeigniter4/CodeIgniter4/issues/647) +- route\_to doesn't work for post routes [\#642](https://github.com/codeigniter4/CodeIgniter4/issues/642) +- CURL response header parsing \(100-continue\) [\#638](https://github.com/codeigniter4/CodeIgniter4/issues/638) +- Q: Support of array notations is not longer supported? [\#627](https://github.com/codeigniter4/CodeIgniter4/issues/627) +- Formvalidation not skip validation rules if field not required and field data empty [\#614](https://github.com/codeigniter4/CodeIgniter4/issues/614) +- Route naming and Groups [\#612](https://github.com/codeigniter4/CodeIgniter4/issues/612) +- Image resizing issue [\#610](https://github.com/codeigniter4/CodeIgniter4/issues/610) +- Too much escaping negative int \(int \< 0\) [\#606](https://github.com/codeigniter4/CodeIgniter4/issues/606) +- Problem with Sth3Model loading from Sth2Model when Sth3Model is in subdiretory \(Subdir1\) [\#605](https://github.com/codeigniter4/CodeIgniter4/issues/605) +- I'm not sure but captcha seems currently unavailable. Would it be included in ci4? [\#604](https://github.com/codeigniter4/CodeIgniter4/issues/604) +- Issue with UploadFile \[tempName == name/originalName\] ? [\#602](https://github.com/codeigniter4/CodeIgniter4/issues/602) +- Command Line Seeding Error [\#601](https://github.com/codeigniter4/CodeIgniter4/issues/601) +- form\_open\_multipart\('', \[\], $hidden\) [\#598](https://github.com/codeigniter4/CodeIgniter4/issues/598) +- Minor issue with docs - form helper \(there is no html\_escape function\) - shoud be esc [\#595](https://github.com/codeigniter4/CodeIgniter4/issues/595) +- $session-\>destroy\(\) and $session-\>stop\(\) do not work? [\#592](https://github.com/codeigniter4/CodeIgniter4/issues/592) +- file upload issue [\#591](https://github.com/codeigniter4/CodeIgniter4/issues/591) +- \IncomingRequest method getFiles\(\) - Should really return only arrays? [\#590](https://github.com/codeigniter4/CodeIgniter4/issues/590) +- Third argument '$param' missing in validation getErrorMessage [\#589](https://github.com/codeigniter4/CodeIgniter4/issues/589) +- Error on Postgre insertID [\#587](https://github.com/codeigniter4/CodeIgniter4/issues/587) +- \InputRequest $request-\>getFiles\(\) doesn't return FileCollection instance [\#586](https://github.com/codeigniter4/CodeIgniter4/issues/586) +- Documentation out of date? discoverLocal\(\) [\#581](https://github.com/codeigniter4/CodeIgniter4/issues/581) +- Storing data [\#580](https://github.com/codeigniter4/CodeIgniter4/issues/580) +- Propose Moving Model::classToArray\(\) method to Entity class as public method [\#579](https://github.com/codeigniter4/CodeIgniter4/issues/579) +- camelize helper function incorrect [\#576](https://github.com/codeigniter4/CodeIgniter4/issues/576) +- helper function uses include instead of include\_once [\#575](https://github.com/codeigniter4/CodeIgniter4/issues/575) +- system/Validation/Views/list.php fix [\#574](https://github.com/codeigniter4/CodeIgniter4/issues/574) +- \[FEATURE\] Model Class: updateWhere\(\) [\#572](https://github.com/codeigniter4/CodeIgniter4/issues/572) +- Model Order [\#571](https://github.com/codeigniter4/CodeIgniter4/issues/571) +- Minor thing with View file not found exception... [\#570](https://github.com/codeigniter4/CodeIgniter4/issues/570) +- Error: redirect\(\) - ltrim\(\) expects parameter 1 to be string, object given [\#568](https://github.com/codeigniter4/CodeIgniter4/issues/568) +- Tutorial is Wrong [\#562](https://github.com/codeigniter4/CodeIgniter4/issues/562) +- Model Hooks/Events Suggestion [\#557](https://github.com/codeigniter4/CodeIgniter4/issues/557) +- Initialize on Model \_\_construct [\#556](https://github.com/codeigniter4/CodeIgniter4/issues/556) +- Event trigger "pre\_system" not found [\#555](https://github.com/codeigniter4/CodeIgniter4/issues/555) +- Validation Issues [\#550](https://github.com/codeigniter4/CodeIgniter4/issues/550) +- Model Entity suggestion [\#549](https://github.com/codeigniter4/CodeIgniter4/issues/549) +- CodeIgniter\Files\File\(\) and FileNotFoundException issue with creating new files [\#548](https://github.com/codeigniter4/CodeIgniter4/issues/548) +- override core files [\#546](https://github.com/codeigniter4/CodeIgniter4/issues/546) +- CodeIgniter\Format\JSONFormatter needs a look. [\#544](https://github.com/codeigniter4/CodeIgniter4/issues/544) +- getenv non covered system method [\#543](https://github.com/codeigniter4/CodeIgniter4/issues/543) +- Couldn't manage without index.php [\#541](https://github.com/codeigniter4/CodeIgniter4/issues/541) +- Debug bar: Uncaught ErrorException: str\_repeat\(\): Second argument has to be greater than or equal to 0 [\#538](https://github.com/codeigniter4/CodeIgniter4/issues/538) +- Create localization filters for View Parser to aid in formatting numbers [\#536](https://github.com/codeigniter4/CodeIgniter4/issues/536) +- Create lang plugin for the View Parser [\#535](https://github.com/codeigniter4/CodeIgniter4/issues/535) +- Minor: Debug bar showing htmlentities \(\\) [\#531](https://github.com/codeigniter4/CodeIgniter4/issues/531) +- Autoloading not functional with custom namespaces outside application dir [\#529](https://github.com/codeigniter4/CodeIgniter4/issues/529) +- where i extends Controller, and rewrite \_\_construct, get error [\#527](https://github.com/codeigniter4/CodeIgniter4/issues/527) +- Debug/ImageException.php failed to open stream: No such file or directory [\#525](https://github.com/codeigniter4/CodeIgniter4/issues/525) +- Superglobals reset [\#524](https://github.com/codeigniter4/CodeIgniter4/issues/524) +- Bring ENV definition back out to the index file. [\#519](https://github.com/codeigniter4/CodeIgniter4/issues/519) +- Exception issue [\#517](https://github.com/codeigniter4/CodeIgniter4/issues/517) +- Port Zip library from CI3 [\#505](https://github.com/codeigniter4/CodeIgniter4/issues/505) +- Port Encryption library from CI3 [\#504](https://github.com/codeigniter4/CodeIgniter4/issues/504) +- Port SQLite database driver from CI3 [\#502](https://github.com/codeigniter4/CodeIgniter4/issues/502) +- A template engine suggession in this phase [\#500](https://github.com/codeigniter4/CodeIgniter4/issues/500) +- \_\_set in \App\Entities\Sample doesnt works [\#499](https://github.com/codeigniter4/CodeIgniter4/issues/499) +- Router rule: \(:alphanum\) doesn't work [\#498](https://github.com/codeigniter4/CodeIgniter4/issues/498) +- Undefined index: host [\#497](https://github.com/codeigniter4/CodeIgniter4/issues/497) +- Unreasonable default parameter [\#482](https://github.com/codeigniter4/CodeIgniter4/issues/482) +- Model issues [\#479](https://github.com/codeigniter4/CodeIgniter4/issues/479) +- Route Blocking [\#474](https://github.com/codeigniter4/CodeIgniter4/issues/474) +- Class 'CodeIgniter\Hooks\Hooks' not found [\#473](https://github.com/codeigniter4/CodeIgniter4/issues/473) +- Work with entities and validation bug [\#472](https://github.com/codeigniter4/CodeIgniter4/issues/472) +- route bug or not? [\#470](https://github.com/codeigniter4/CodeIgniter4/issues/470) +- serve.php option --host no effect on line 37 [\#469](https://github.com/codeigniter4/CodeIgniter4/issues/469) +- File class should stand on its own [\#468](https://github.com/codeigniter4/CodeIgniter4/issues/468) +- Database can't save connect instances because var "$group" default is NULL [\#466](https://github.com/codeigniter4/CodeIgniter4/issues/466) +- redirect\(\) issue [\#465](https://github.com/codeigniter4/CodeIgniter4/issues/465) +- Join not working in Query Builder [\#464](https://github.com/codeigniter4/CodeIgniter4/issues/464) +- If the controller's method's parameter has a default value , the program will show 500 error [\#461](https://github.com/codeigniter4/CodeIgniter4/issues/461) +- Wrong ROOTPATH on console [\#460](https://github.com/codeigniter4/CodeIgniter4/issues/460) +- Rename "public" Folder Not Working [\#453](https://github.com/codeigniter4/CodeIgniter4/issues/453) +- Environment File [\#452](https://github.com/codeigniter4/CodeIgniter4/issues/452) +- Currency Localisation Not Working [\#448](https://github.com/codeigniter4/CodeIgniter4/issues/448) +- session getFlashdata\(\) is not removing the flash data [\#446](https://github.com/codeigniter4/CodeIgniter4/issues/446) +- CURLRequest: HTTPHEADER and POSTFIELDS [\#445](https://github.com/codeigniter4/CodeIgniter4/issues/445) +- Language Line Prepend Recommendation [\#443](https://github.com/codeigniter4/CodeIgniter4/issues/443) +- Debug Toolbar Array Post Data [\#442](https://github.com/codeigniter4/CodeIgniter4/issues/442) +- Migration File & Class Name Issues [\#437](https://github.com/codeigniter4/CodeIgniter4/issues/437) +- Validation Rule Constants [\#436](https://github.com/codeigniter4/CodeIgniter4/issues/436) +- CURLRequest: Only variables should be passed by reference [\#434](https://github.com/codeigniter4/CodeIgniter4/issues/434) +- CLI new commands [\#433](https://github.com/codeigniter4/CodeIgniter4/issues/433) +- issue in phpunit [\#429](https://github.com/codeigniter4/CodeIgniter4/issues/429) +- Model Class To Array Does Not Use Get Magic Method Or Allowed Fields Property [\#427](https://github.com/codeigniter4/CodeIgniter4/issues/427) +- Response 404 not same satus get it [\#425](https://github.com/codeigniter4/CodeIgniter4/issues/425) +- Response Trait Fail Server Error Method [\#424](https://github.com/codeigniter4/CodeIgniter4/issues/424) +- Validation Required Rule Only Accepts Arrays or Strings [\#423](https://github.com/codeigniter4/CodeIgniter4/issues/423) +- CLI with parameters is not working [\#422](https://github.com/codeigniter4/CodeIgniter4/issues/422) +- API Response Trait :: failValidationError\(\) Status Code 422 [\#420](https://github.com/codeigniter4/CodeIgniter4/issues/420) +- Validation Get & Set Rule Group [\#419](https://github.com/codeigniter4/CodeIgniter4/issues/419) +- One-Off Validation for a Single Value [\#418](https://github.com/codeigniter4/CodeIgniter4/issues/418) +- Language File Array Support [\#414](https://github.com/codeigniter4/CodeIgniter4/issues/414) +- Pagination: How to trans string param when using $pager-\>links\(\)? [\#413](https://github.com/codeigniter4/CodeIgniter4/issues/413) +- Validation Exceptions [\#412](https://github.com/codeigniter4/CodeIgniter4/issues/412) +- Incorrect .htaccess [\#410](https://github.com/codeigniter4/CodeIgniter4/issues/410) +- Call to a member function getResult\(\) on boolean [\#409](https://github.com/codeigniter4/CodeIgniter4/issues/409) +- Writable directory question [\#407](https://github.com/codeigniter4/CodeIgniter4/issues/407) +- Paginate Class [\#406](https://github.com/codeigniter4/CodeIgniter4/issues/406) +- Base URL Not Working With HTTPS [\#396](https://github.com/codeigniter4/CodeIgniter4/issues/396) +- How to current page detailed information on router or etc? [\#393](https://github.com/codeigniter4/CodeIgniter4/issues/393) +- timer long time getElapsedTime problem [\#390](https://github.com/codeigniter4/CodeIgniter4/issues/390) +- Problem with system bootstrap file needs to be fixed! [\#389](https://github.com/codeigniter4/CodeIgniter4/issues/389) +- redirect reverseRoute problem [\#387](https://github.com/codeigniter4/CodeIgniter4/issues/387) +- Hooks::on not working \(it was working some time ago\) [\#383](https://github.com/codeigniter4/CodeIgniter4/issues/383) +- disable coveralls auto comment on pull request [\#382](https://github.com/codeigniter4/CodeIgniter4/issues/382) +- phpunit don't work \(fatal error\) on PHP 7.0.0 [\#373](https://github.com/codeigniter4/CodeIgniter4/issues/373) +- session id validation when using php7.1 [\#371](https://github.com/codeigniter4/CodeIgniter4/issues/371) +- travis test coverage report service [\#370](https://github.com/codeigniter4/CodeIgniter4/issues/370) +- The efficiency of 'insertBatch' function [\#368](https://github.com/codeigniter4/CodeIgniter4/issues/368) +- Run phpunit with --coverage-text/html got Error: Undefined variable: matchIP [\#363](https://github.com/codeigniter4/CodeIgniter4/issues/363) +- Extending Core Classes [\#358](https://github.com/codeigniter4/CodeIgniter4/issues/358) +- Validation field name issue? [\#356](https://github.com/codeigniter4/CodeIgniter4/issues/356) +- Old Cache-Control header tag remove? [\#355](https://github.com/codeigniter4/CodeIgniter4/issues/355) +- $this-\>request-\>getPost\(\) // Subkey not included data [\#353](https://github.com/codeigniter4/CodeIgniter4/issues/353) +- Routes static parameter problem not found page [\#352](https://github.com/codeigniter4/CodeIgniter4/issues/352) +- bug-form\_open\_multipart [\#345](https://github.com/codeigniter4/CodeIgniter4/issues/345) +- Recently ci4 everything goes well? I look ci the space named [\#336](https://github.com/codeigniter4/CodeIgniter4/issues/336) +- Request setBody not working [\#332](https://github.com/codeigniter4/CodeIgniter4/issues/332) +- HTTP/URI, CreateURIString yield unexpected result \(http:/// instead of http://\) [\#331](https://github.com/codeigniter4/CodeIgniter4/issues/331) +- session class: inconsistency on get\(\) between document and code [\#330](https://github.com/codeigniter4/CodeIgniter4/issues/330) +- Model.php Return Type Object causes Error when Saving. \(Fixed with this modification\) [\#329](https://github.com/codeigniter4/CodeIgniter4/issues/329) +- a bug about set\(\) [\#325](https://github.com/codeigniter4/CodeIgniter4/issues/325) +- `google map` in Mysqli Could be a Mistake [\#324](https://github.com/codeigniter4/CodeIgniter4/issues/324) +- Modules Controllers sub-directory problem [\#322](https://github.com/codeigniter4/CodeIgniter4/issues/322) +- Post action after Toolbar -\> Vars -\> Headers Content-Type 2 lines [\#321](https://github.com/codeigniter4/CodeIgniter4/issues/321) +- Database builder-\>table\(\) connection reference problem [\#320](https://github.com/codeigniter4/CodeIgniter4/issues/320) +- RedisHandler, Session regenerate id problem [\#318](https://github.com/codeigniter4/CodeIgniter4/issues/318) +- view\_cell optional variables array\_key\_exists null fix. [\#317](https://github.com/codeigniter4/CodeIgniter4/issues/317) +- sessionDriver Database not available [\#315](https://github.com/codeigniter4/CodeIgniter4/issues/315) +- Add support for Content-MD5 headers [\#314](https://github.com/codeigniter4/CodeIgniter4/issues/314) +- Scalar type declaration 'string' must be unqualified [\#312](https://github.com/codeigniter4/CodeIgniter4/issues/312) +- Now ci4 product development can be used? Or how long before they can be released? Very much looking forward [\#311](https://github.com/codeigniter4/CodeIgniter4/issues/311) +- Sessions in Debug Bar Could be a Mistake [\#310](https://github.com/codeigniter4/CodeIgniter4/issues/310) +- Should namespaces be used or fully written at method heads [\#309](https://github.com/codeigniter4/CodeIgniter4/issues/309) +- More filtering issues with Query [\#306](https://github.com/codeigniter4/CodeIgniter4/issues/306) +- Too much escaping in DB? [\#302](https://github.com/codeigniter4/CodeIgniter4/issues/302) +- Error Cell caching [\#297](https://github.com/codeigniter4/CodeIgniter4/issues/297) +- A suggestion for debug exceptions layout [\#295](https://github.com/codeigniter4/CodeIgniter4/issues/295) +- getSharedInstance doesn't work on costum services [\#294](https://github.com/codeigniter4/CodeIgniter4/issues/294) +- Separated services default with trait [\#292](https://github.com/codeigniter4/CodeIgniter4/issues/292) +- HTML-Purifier [\#291](https://github.com/codeigniter4/CodeIgniter4/issues/291) +- Implement native ORM [\#289](https://github.com/codeigniter4/CodeIgniter4/issues/289) +- Igniter 4 services [\#287](https://github.com/codeigniter4/CodeIgniter4/issues/287) +- Error Documentation - Tutorial [\#286](https://github.com/codeigniter4/CodeIgniter4/issues/286) +- Request is doing the filter\_var even if value is not in the $\_REQUEST array [\#285](https://github.com/codeigniter4/CodeIgniter4/issues/285) +- controller routing [\#284](https://github.com/codeigniter4/CodeIgniter4/issues/284) +- URI Routing issues [\#278](https://github.com/codeigniter4/CodeIgniter4/issues/278) +- Email Library [\#276](https://github.com/codeigniter4/CodeIgniter4/issues/276) +- sql error [\#273](https://github.com/codeigniter4/CodeIgniter4/issues/273) +- DB Needs Transactions implemented. [\#268](https://github.com/codeigniter4/CodeIgniter4/issues/268) +- Error Call to undefined method CodeIgniter\Database\MySQLi\Connection::close\(\) [\#267](https://github.com/codeigniter4/CodeIgniter4/issues/267) +- Application/ThirdParty folder necessity [\#265](https://github.com/codeigniter4/CodeIgniter4/issues/265) +- ErrorException when Connecting to Multiple Databases [\#255](https://github.com/codeigniter4/CodeIgniter4/issues/255) +- Toolbar.php :: ErrorException: Array to string conversion [\#254](https://github.com/codeigniter4/CodeIgniter4/issues/254) +- The Hooks feature does not work [\#248](https://github.com/codeigniter4/CodeIgniter4/issues/248) +- $baseUrl problem with Router [\#238](https://github.com/codeigniter4/CodeIgniter4/issues/238) +- File upload bugs [\#236](https://github.com/codeigniter4/CodeIgniter4/issues/236) +- standardize comments [\#234](https://github.com/codeigniter4/CodeIgniter4/issues/234) +- 3.1.0 Email Library Corrupting PDF Attachments [\#220](https://github.com/codeigniter4/CodeIgniter4/issues/220) +- Error DotEnv.php on line 121 [\#216](https://github.com/codeigniter4/CodeIgniter4/issues/216) +- Typography Helper [\#214](https://github.com/codeigniter4/CodeIgniter4/issues/214) +- Security Helper [\#213](https://github.com/codeigniter4/CodeIgniter4/issues/213) +- Number Helper [\#212](https://github.com/codeigniter4/CodeIgniter4/issues/212) +- Text Helper [\#211](https://github.com/codeigniter4/CodeIgniter4/issues/211) +- Inflector Helper [\#210](https://github.com/codeigniter4/CodeIgniter4/issues/210) +- HTML Helper [\#209](https://github.com/codeigniter4/CodeIgniter4/issues/209) +- FileSystem "Helper" [\#207](https://github.com/codeigniter4/CodeIgniter4/issues/207) +- Date Helper [\#206](https://github.com/codeigniter4/CodeIgniter4/issues/206) +- Cookie Helper [\#205](https://github.com/codeigniter4/CodeIgniter4/issues/205) +- Missing {memory\_usage} ? [\#197](https://github.com/codeigniter4/CodeIgniter4/issues/197) +- Exception or disable toolbar for specific URI [\#195](https://github.com/codeigniter4/CodeIgniter4/issues/195) +- Config Settings Usability [\#186](https://github.com/codeigniter4/CodeIgniter4/issues/186) +- Binders [\#185](https://github.com/codeigniter4/CodeIgniter4/issues/185) +- Mess Detector rules [\#184](https://github.com/codeigniter4/CodeIgniter4/issues/184) +- Coding Standards Fixer rules [\#183](https://github.com/codeigniter4/CodeIgniter4/issues/183) +- Code Sniffer Rules [\#182](https://github.com/codeigniter4/CodeIgniter4/issues/182) +- Placing view template outside of the 'Views' dir when using view\(\) [\#180](https://github.com/codeigniter4/CodeIgniter4/issues/180) +- Controller return output instead of echo view [\#179](https://github.com/codeigniter4/CodeIgniter4/issues/179) +- Honeypot Filter [\#176](https://github.com/codeigniter4/CodeIgniter4/issues/176) +- Form Helper [\#174](https://github.com/codeigniter4/CodeIgniter4/issues/174) +- ILIKE-based portion of the query for PostgreSQL [\#173](https://github.com/codeigniter4/CodeIgniter4/issues/173) +- “&get\_instance\(\)” in the Ci3 how to use it? [\#166](https://github.com/codeigniter4/CodeIgniter4/issues/166) +- system/Database/BaseConnection.php Change in getFieldNames\(\) method [\#164](https://github.com/codeigniter4/CodeIgniter4/issues/164) +- Error in session FileHandler and BaseHandler [\#152](https://github.com/codeigniter4/CodeIgniter4/issues/152) +- No listFields\(\) method in Postgre connection [\#151](https://github.com/codeigniter4/CodeIgniter4/issues/151) +- Controller Filters [\#150](https://github.com/codeigniter4/CodeIgniter4/issues/150) +- insert bug [\#149](https://github.com/codeigniter4/CodeIgniter4/issues/149) +- Router striping real dirpath from the urls. [\#148](https://github.com/codeigniter4/CodeIgniter4/issues/148) +- Problem throw error for default controller [\#146](https://github.com/codeigniter4/CodeIgniter4/issues/146) +- Routing issues. [\#145](https://github.com/codeigniter4/CodeIgniter4/issues/145) +- Pagination Library [\#142](https://github.com/codeigniter4/CodeIgniter4/issues/142) +- \[i18n\] Localization In Core [\#141](https://github.com/codeigniter4/CodeIgniter4/issues/141) +- Language [\#140](https://github.com/codeigniter4/CodeIgniter4/issues/140) +- Parser [\#139](https://github.com/codeigniter4/CodeIgniter4/issues/139) +- Application directory is missing the Helpers folder [\#133](https://github.com/codeigniter4/CodeIgniter4/issues/133) +- HTTP/Request.php Error [\#132](https://github.com/codeigniter4/CodeIgniter4/issues/132) +- Public properties issue? [\#124](https://github.com/codeigniter4/CodeIgniter4/issues/124) +- ci4 support websocket it? [\#121](https://github.com/codeigniter4/CodeIgniter4/issues/121) +- View Cells [\#116](https://github.com/codeigniter4/CodeIgniter4/issues/116) +- Cache Engine [\#115](https://github.com/codeigniter4/CodeIgniter4/issues/115) +- Image Class [\#114](https://github.com/codeigniter4/CodeIgniter4/issues/114) +- Uploader Class [\#113](https://github.com/codeigniter4/CodeIgniter4/issues/113) +- API Response Trait [\#86](https://github.com/codeigniter4/CodeIgniter4/issues/86) +- phpDocumentor bug [\#85](https://github.com/codeigniter4/CodeIgniter4/issues/85) +- Reserved method name [\#76](https://github.com/codeigniter4/CodeIgniter4/issues/76) +- Provide Throttler Filter [\#75](https://github.com/codeigniter4/CodeIgniter4/issues/75) +- Ensure docs are updated for current code. [\#72](https://github.com/codeigniter4/CodeIgniter4/issues/72) +- Writing Testing Docs [\#71](https://github.com/codeigniter4/CodeIgniter4/issues/71) +- Update Tutorial [\#70](https://github.com/codeigniter4/CodeIgniter4/issues/70) +- Review Contribution Guidelines [\#69](https://github.com/codeigniter4/CodeIgniter4/issues/69) +- Database Connection setDatabase\(\) and getVersion\(\) methods [\#68](https://github.com/codeigniter4/CodeIgniter4/issues/68) +- BaseConfig getEnvValue type juggling [\#67](https://github.com/codeigniter4/CodeIgniter4/issues/67) +- Migrations should track history per db group. [\#66](https://github.com/codeigniter4/CodeIgniter4/issues/66) +- Allow passing custom config values to Config\Database::connect\(\) [\#62](https://github.com/codeigniter4/CodeIgniter4/issues/62) +- SessionInterface: unset\(\) vs remove\(\) [\#60](https://github.com/codeigniter4/CodeIgniter4/issues/60) +- Remove Query Builder caching [\#59](https://github.com/codeigniter4/CodeIgniter4/issues/59) +- Why is `$getShared = false` the default? [\#55](https://github.com/codeigniter4/CodeIgniter4/issues/55) +- Why IncomingRequest has setCookie\(\)? [\#52](https://github.com/codeigniter4/CodeIgniter4/issues/52) +- AutoRoute and method arguments [\#45](https://github.com/codeigniter4/CodeIgniter4/issues/45) +- Rename the loader class [\#39](https://github.com/codeigniter4/CodeIgniter4/issues/39) +- exit\(\) prevents from testing [\#31](https://github.com/codeigniter4/CodeIgniter4/issues/31) +- getHeaders [\#27](https://github.com/codeigniter4/CodeIgniter4/issues/27) +- SYSDIR [\#25](https://github.com/codeigniter4/CodeIgniter4/issues/25) +- Coding style checker [\#21](https://github.com/codeigniter4/CodeIgniter4/issues/21) +- Test folder structure [\#20](https://github.com/codeigniter4/CodeIgniter4/issues/20) +- Namespace for test case classes [\#17](https://github.com/codeigniter4/CodeIgniter4/issues/17) +- Missing protocolVersion in response header [\#15](https://github.com/codeigniter4/CodeIgniter4/issues/15) +- Problem with Code Coverage Reporting [\#13](https://github.com/codeigniter4/CodeIgniter4/issues/13) +- Class 'Config\App' not found [\#12](https://github.com/codeigniter4/CodeIgniter4/issues/12) +- Can't get Code Coverage [\#7](https://github.com/codeigniter4/CodeIgniter4/issues/7) +- APPPATH, SYSPATH and similar constants. Rename? [\#2](https://github.com/codeigniter4/CodeIgniter4/issues/2) + +**Merged pull requests:** + +- Adjusting the release build scripts [\#1266](https://github.com/codeigniter4/CodeIgniter4/pull/1266) ([jim-parry](https://github.com/jim-parry)) +- WIP Fix docs re PHP server [\#1265](https://github.com/codeigniter4/CodeIgniter4/pull/1265) ([jim-parry](https://github.com/jim-parry)) +- Release prep part 1 [\#1248](https://github.com/codeigniter4/CodeIgniter4/pull/1248) ([jim-parry](https://github.com/jim-parry)) +- Tweaking the release builder [\#1246](https://github.com/codeigniter4/CodeIgniter4/pull/1246) ([jim-parry](https://github.com/jim-parry)) +- Move Response & APIResponseTrait to outgoing section of UG [\#1243](https://github.com/codeigniter4/CodeIgniter4/pull/1243) ([jim-parry](https://github.com/jim-parry)) +- workaround for buffer problem [\#1242](https://github.com/codeigniter4/CodeIgniter4/pull/1242) ([puschie286](https://github.com/puschie286)) +- Docs/restructure [\#1241](https://github.com/codeigniter4/CodeIgniter4/pull/1241) ([jim-parry](https://github.com/jim-parry)) +- doc fix: replace validation\_errors\(\) function with \Config\Services::validation\(\)-\>listErrors\(\) [\#1238](https://github.com/codeigniter4/CodeIgniter4/pull/1238) ([samsonasik](https://github.com/samsonasik)) +- doc fix: remove unneeded call helper\(url\) as already bootstrapped by default [\#1237](https://github.com/codeigniter4/CodeIgniter4/pull/1237) ([samsonasik](https://github.com/samsonasik)) +- Fix gh-pages deployment [\#1236](https://github.com/codeigniter4/CodeIgniter4/pull/1236) ([jim-parry](https://github.com/jim-parry)) +- Doc/fixes [\#1235](https://github.com/codeigniter4/CodeIgniter4/pull/1235) ([jim-parry](https://github.com/jim-parry)) +- typo in unset [\#1233](https://github.com/codeigniter4/CodeIgniter4/pull/1233) ([titounnes](https://github.com/titounnes)) +- Release build script [\#1231](https://github.com/codeigniter4/CodeIgniter4/pull/1231) ([jim-parry](https://github.com/jim-parry)) +- Fix user guide errors [\#1228](https://github.com/codeigniter4/CodeIgniter4/pull/1228) ([jim-parry](https://github.com/jim-parry)) +- Admin script for user guide build & deploy [\#1227](https://github.com/codeigniter4/CodeIgniter4/pull/1227) ([jim-parry](https://github.com/jim-parry)) +- use short array syntax [\#1223](https://github.com/codeigniter4/CodeIgniter4/pull/1223) ([samsonasik](https://github.com/samsonasik)) +- doc fix: FormatterInterface namespace [\#1222](https://github.com/codeigniter4/CodeIgniter4/pull/1222) ([samsonasik](https://github.com/samsonasik)) +- Improved division logic of validation rules. [\#1220](https://github.com/codeigniter4/CodeIgniter4/pull/1220) ([ytetsuro](https://github.com/ytetsuro)) +- Docs/contributing [\#1218](https://github.com/codeigniter4/CodeIgniter4/pull/1218) ([jim-parry](https://github.com/jim-parry)) +- Niggly fixes [\#1216](https://github.com/codeigniter4/CodeIgniter4/pull/1216) ([jim-parry](https://github.com/jim-parry)) +- Autodiscovery [\#1215](https://github.com/codeigniter4/CodeIgniter4/pull/1215) ([lonnieezell](https://github.com/lonnieezell)) +- Fix warnings in welcome\_message.php [\#1211](https://github.com/codeigniter4/CodeIgniter4/pull/1211) ([puschie286](https://github.com/puschie286)) +- Correct helper tests namespace [\#1207](https://github.com/codeigniter4/CodeIgniter4/pull/1207) ([jim-parry](https://github.com/jim-parry)) +- Validation Class - corresponding about the escaped separator. [\#1203](https://github.com/codeigniter4/CodeIgniter4/pull/1203) ([ytetsuro](https://github.com/ytetsuro)) +- Fixes FileRules::max\_size\(\) to use file-\>getSize\(\) instead of number\_formatted size [\#1199](https://github.com/codeigniter4/CodeIgniter4/pull/1199) ([samsonasik](https://github.com/samsonasik)) +- use Validation-\>getErrors\(\) call instead of Valdation::errors to handle errors that came from session [\#1197](https://github.com/codeigniter4/CodeIgniter4/pull/1197) ([samsonasik](https://github.com/samsonasik)) +- allows to get table and primary key name out of the model [\#1196](https://github.com/codeigniter4/CodeIgniter4/pull/1196) ([nowackipawel](https://github.com/nowackipawel)) +- pagination - optional page number [\#1195](https://github.com/codeigniter4/CodeIgniter4/pull/1195) ([nowackipawel](https://github.com/nowackipawel)) +- add writable/session directory and set default App::sessionSavePath to it [\#1194](https://github.com/codeigniter4/CodeIgniter4/pull/1194) ([samsonasik](https://github.com/samsonasik)) +- Fix travis error build on Router and RouteCollection [\#1192](https://github.com/codeigniter4/CodeIgniter4/pull/1192) ([samsonasik](https://github.com/samsonasik)) +- add bool type hint for getShared parameter [\#1191](https://github.com/codeigniter4/CodeIgniter4/pull/1191) ([samsonasik](https://github.com/samsonasik)) +- Catch Email Exceptions [\#1190](https://github.com/codeigniter4/CodeIgniter4/pull/1190) ([puschie286](https://github.com/puschie286)) +- $myModel-\>find\(string "value-of-my-primary-key"\) \#1188 [\#1189](https://github.com/codeigniter4/CodeIgniter4/pull/1189) ([nowackipawel](https://github.com/nowackipawel)) +- Adds valid\_json which is using json\_last\_error\(\) === JSON\_ERROR\_NONE [\#1187](https://github.com/codeigniter4/CodeIgniter4/pull/1187) ([nowackipawel](https://github.com/nowackipawel)) +- remove start\(\) call on session\(\) function call at Validation [\#1185](https://github.com/codeigniter4/CodeIgniter4/pull/1185) ([samsonasik](https://github.com/samsonasik)) +- remove unused salt in Model [\#1184](https://github.com/codeigniter4/CodeIgniter4/pull/1184) ([samsonasik](https://github.com/samsonasik)) +- log file:failed to delete buffer. No buffer to delete. [\#1182](https://github.com/codeigniter4/CodeIgniter4/pull/1182) ([bangbangda](https://github.com/bangbangda)) +- use string type hint in $file parameter in DotEnv::\_\_construct\(\) [\#1181](https://github.com/codeigniter4/CodeIgniter4/pull/1181) ([samsonasik](https://github.com/samsonasik)) +- Adjust log level to match RFC 5424 [\#1178](https://github.com/codeigniter4/CodeIgniter4/pull/1178) ([sugenganthos](https://github.com/sugenganthos)) +- Update Response.php [\#1173](https://github.com/codeigniter4/CodeIgniter4/pull/1173) ([sugenganthos](https://github.com/sugenganthos)) +- comparison fix at CIDatabaseTestCase [\#1172](https://github.com/codeigniter4/CodeIgniter4/pull/1172) ([samsonasik](https://github.com/samsonasik)) +- remove if \($template==forums/categories\) check in View/Parser [\#1171](https://github.com/codeigniter4/CodeIgniter4/pull/1171) ([samsonasik](https://github.com/samsonasik)) +- show PageNotFoundException message [\#1168](https://github.com/codeigniter4/CodeIgniter4/pull/1168) ([puschie286](https://github.com/puschie286)) +- make name parameter in HTTP\Header mandatory [\#1164](https://github.com/codeigniter4/CodeIgniter4/pull/1164) ([samsonasik](https://github.com/samsonasik)) +- Fixed insufficient validation of parameters related to pager. [\#1162](https://github.com/codeigniter4/CodeIgniter4/pull/1162) ([ytetsuro](https://github.com/ytetsuro)) +- remove unneeded helper\('url'\) call in plugins and form helper as already called in bootstrap file [\#1160](https://github.com/codeigniter4/CodeIgniter4/pull/1160) ([samsonasik](https://github.com/samsonasik)) +- reduce str\_replace in View/Parser::render\(\) [\#1159](https://github.com/codeigniter4/CodeIgniter4/pull/1159) ([samsonasik](https://github.com/samsonasik)) +- add missing string parameter type hint in Autoload methods [\#1158](https://github.com/codeigniter4/CodeIgniter4/pull/1158) ([samsonasik](https://github.com/samsonasik)) +- remove unneeded isset\($options\[hostname\]\) when next check is !empty\($options\[hostname\]\) [\#1157](https://github.com/codeigniter4/CodeIgniter4/pull/1157) ([samsonasik](https://github.com/samsonasik)) +- remove commented and never used methods in View\Parser class [\#1156](https://github.com/codeigniter4/CodeIgniter4/pull/1156) ([samsonasik](https://github.com/samsonasik)) +- Fixes \#1135 : Toolbar oldest file delete and show maximum in the list as App::toolbarMaxHistory [\#1155](https://github.com/codeigniter4/CodeIgniter4/pull/1155) ([samsonasik](https://github.com/samsonasik)) +- add ext-intl to require at composer.json [\#1153](https://github.com/codeigniter4/CodeIgniter4/pull/1153) ([samsonasik](https://github.com/samsonasik)) +- remove unneeded \(string\) cast as type hinted [\#1152](https://github.com/codeigniter4/CodeIgniter4/pull/1152) ([samsonasik](https://github.com/samsonasik)) +- implements session-\>push\(\) [\#1151](https://github.com/codeigniter4/CodeIgniter4/pull/1151) ([samsonasik](https://github.com/samsonasik)) +- SplFileInfo type case [\#1150](https://github.com/codeigniter4/CodeIgniter4/pull/1150) ([samsonasik](https://github.com/samsonasik)) +- Update views.rst [\#1149](https://github.com/codeigniter4/CodeIgniter4/pull/1149) ([sugenganthos](https://github.com/sugenganthos)) +- remove unneeded @todo for Time::setMonth\(\) to check max [\#1148](https://github.com/codeigniter4/CodeIgniter4/pull/1148) ([samsonasik](https://github.com/samsonasik)) +- fix RedirectResponse::route and added test [\#1147](https://github.com/codeigniter4/CodeIgniter4/pull/1147) ([puschie286](https://github.com/puschie286)) +- Fix uri detection with no index in uri [\#1146](https://github.com/codeigniter4/CodeIgniter4/pull/1146) ([puschie286](https://github.com/puschie286)) +- remove unneeded isset\($\_SESSION\) when next check is !empty\($\_SESSION\) [\#1145](https://github.com/codeigniter4/CodeIgniter4/pull/1145) ([samsonasik](https://github.com/samsonasik)) +- Add a namespace to the Pages.php [\#1143](https://github.com/codeigniter4/CodeIgniter4/pull/1143) ([fdjkgh580](https://github.com/fdjkgh580)) +- Add a namespace to the controller. [\#1142](https://github.com/codeigniter4/CodeIgniter4/pull/1142) ([fdjkgh580](https://github.com/fdjkgh580)) +- remove unused use statements [\#1141](https://github.com/codeigniter4/CodeIgniter4/pull/1141) ([samsonasik](https://github.com/samsonasik)) +- implements @todo max day in current month at Time::setDay\(\) [\#1140](https://github.com/codeigniter4/CodeIgniter4/pull/1140) ([samsonasik](https://github.com/samsonasik)) +- remove unneeded \(int\) casting as use int type hint or certainly an int [\#1138](https://github.com/codeigniter4/CodeIgniter4/pull/1138) ([samsonasik](https://github.com/samsonasik)) +- Update html\_helper.php [\#1133](https://github.com/codeigniter4/CodeIgniter4/pull/1133) ([WaldemarStanislawski](https://github.com/WaldemarStanislawski)) +- update to latest php-coveralls [\#1131](https://github.com/codeigniter4/CodeIgniter4/pull/1131) ([samsonasik](https://github.com/samsonasik)) +- Update View.php [\#1130](https://github.com/codeigniter4/CodeIgniter4/pull/1130) ([sugenganthos](https://github.com/sugenganthos)) +- Fix debugbar loading while csp is enabled [\#1129](https://github.com/codeigniter4/CodeIgniter4/pull/1129) ([puschie286](https://github.com/puschie286)) +- Run session tests in separate processes - fix for \#1106 [\#1128](https://github.com/codeigniter4/CodeIgniter4/pull/1128) ([andreif23](https://github.com/andreif23)) +- Feature/sqlite [\#793](https://github.com/codeigniter4/CodeIgniter4/pull/793) ([lonnieezell](https://github.com/lonnieezell)) diff --git a/changelogs/CHANGELOG_4.1.md b/changelogs/CHANGELOG_4.1.md new file mode 100644 index 000000000000..12b13d20cb63 --- /dev/null +++ b/changelogs/CHANGELOG_4.1.md @@ -0,0 +1,565 @@ +# Changelog 4.1 + +## [v4.1.9](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.9) (2022-02-25) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.8...v4.1.9) + +**SECURITY** + +* *Remote CLI Command Execution Vulnerability* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-xjp4-6w75-qrj7) for more information. +* *Cross-Site Request Forgery (CSRF) Protection Bypass Vulnerability* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-4v37-24gm-h554) for more information. + +## [v4.1.8](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.8) (2022-01-24) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.7...v4.1.8) + +**SECURITY** + +* *XSS Vulnerability* in the `API\ResponseTrait` was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-7528-7jg5-6g62) for more information. + +## [v4.1.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.7) (2022-01-09) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.6...v4.1.7) + +**Breaking Changes** + +* fix: replace deprecated FILTER_SANITIZE_STRING by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5555 + +**Fixed Bugs** + +* fix: BaseConnection::getConnectDuration() number_format(): Passing null to parameter by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5536 +* Fix: Debug toolbar selectors by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5544 +* Fix: Toolbar. ciDebugBar.showTab() context. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5554 +* Refactor Database Collector display by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5553 + +## [v4.1.6](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.6) (2022-01-03) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.5...v4.1.6) + +**SECURITY** + +* *Deserialization of Untrusted Data* found in the ``old()`` function was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-w6jr-wj64-mc9x) for more information. + +**Breaking Changes** + +* fix: Incorrect type `BaseBuilder::$tableName` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5378 +* fix: Validation cannot handle array item by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5405 + +**Fixed Bugs** + +* fix: FileLocator cannot find files in sub-namespaces of the same vendor by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5292 +* fix: add a workaround for upgraded users who do not update Config\Exceptions by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5314 +* Fix db escape negative integers by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5277 +* Fix: remove incorrect processing of CLI params by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5274 +* fix: table alias is prefixed when LIKE clause by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5361 +* fix: `dot_array_search()` unexpected array structure causes Type Error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5372 +* fix: UploadedFile::move() may return incorrect value by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5302 +* fix: BaseModel::insert() may not pass all the values from Entity by @katie1348 in https://github.com/codeigniter4/CodeIgniter4/pull/4980 +* fix: `IncomingRequest::getJsonVar()` may cause TypeError by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5392 +* chore: fix example test code for appstarter and module by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5421 +* fix: Model::save() may call unneeded countAllResults() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5439 +* fix: errors when MariaDB/MySQL has `ANSI_QUOTES` enabled by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5425 +* fix: Security class sends cookies immediately by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5429 +* fix: `is_cli()` returns `true` when `$_SERVER['HTTP_USER_AGENT']` is missing by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5393 +* fix: `MySQLi\Connection::_foreignKeyData()` may return duplicated rows by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5416 +* fix: `number_to_currency()` error on PHP 8.1 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5454 +* fix: VENDORPATH definition by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5453 +* fix: Throttler does not show correct token time by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5470 +* fix: directory_mirror() throws an error if destination directory exists by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5493 +* fix: KINT visual error when activating CSP by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5501 + +**New Features** + +* feat: add filter to check invalid chars in user input by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5227 + +**Enhancements** + +* Add support for PHP 8.1 by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/4883 +* Toolbar - Make it possible to turn off var data collection by @najdanovicivan in https://github.com/codeigniter4/CodeIgniter4/pull/5295 +* feat: add CSRF token randomization by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5283 +* Display file:line and trace information to database queries in debug toolbar by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/5334 +* feat: add SecureHeaders filter by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5419 +* Feature: BaseBuilder instance as subquery. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5488 + +**Refactoring** + +* Do not inappropriately register bind when the value is a closure by @vlakoff in https://github.com/codeigniter4/CodeIgniter4/pull/5247 +* refactor: replace $request->uri with $request->getUri() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5346 +* Determine if binds are simple or named by looking at the $binds array by @vlakoff in https://github.com/codeigniter4/CodeIgniter4/pull/5138 +* Remove unneeded cast to array by @vlakoff in https://github.com/codeigniter4/CodeIgniter4/pull/5379 +* Additional fix for deprecated `null` usage by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5388 +* refactor: dot_array_search() regex by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5443 +* refactor: Time::getDst() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5415 +* The View class. Optimizing duplicate code. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5455 +* refactor: fix `ThrottleTest::testFlooding` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5463 +* refactor: update deprecated method in DatetimeCast by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5474 +* Remove semicolons from SQL statements. by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5513 + +**New Contributors** + +* @katie1348 made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/4980 + +## [v4.1.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.5) (2021-11-08) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.4...v4.1.5) + +**Fixed bugs:** + +* Fix entity name generation when bundled in model by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5055 +* Fix `Model::__call` throwing `BadMethodCallException` on empty results by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5139 +* Fixed an issue where the dropForeginKey method would execute an empty query when the dropConstraintStr property was empty. by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5173 +* Update 'updated_at' when enabled in replace() by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/4684 +* Fix query binding with two colons in query by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5117 +* Fixed the problem that _createTable does not take into account that it returns true. by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5133 +* Fixed a problem with not run escape for identities in like when `insensitiveSearch` is true. by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5170 +* Fixed an issue where an unnecessary prefix was given when the random number was a column. by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5179 +* Always escape identifiers in the set(), setUpdateBatch(), and insertBatch() by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5132 +* Error when value is an object - validating api data by @daycry in https://github.com/codeigniter4/CodeIgniter4/pull/5142 +* Fix color not updated in several places of the precompiled CSS by @vlakoff in https://github.com/codeigniter4/CodeIgniter4/pull/5155 +* Fix debugbar styles printing by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5100 +* Fix highlighting in database debug toolbar by @vlakoff in https://github.com/codeigniter4/CodeIgniter4/pull/5129 +* Fix debug toolbar db connection count by @danielTiringer in https://github.com/codeigniter4/CodeIgniter4/pull/5172 +* Fix CSRF filter does not work when set it to only post by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5194 +* Add CSRF Protection for PUT/PATCH/DELETE by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5228 +* Fix GC issue when session lifetime is set to 0 by @lf-uraku-yuki in https://github.com/codeigniter4/CodeIgniter4/pull/4744 +* Fix wrong helper path resolution by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5246 +* Fix: remove CURLRequest headers sharing from $_SERVER by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5249 +* Fix Localization not working/being ignored for 404 page by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5267 +* fix: module filters are not discovered when using route filters by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5280 +* IncomingRequest - Trim trailing slash by @najdanovicivan in https://github.com/codeigniter4/CodeIgniter4/pull/4974 +* Previous Responses by @MGatner in https://github.com/codeigniter4/CodeIgniter4/pull/5034 +* (Paging) Ensure page validity by @puschie286 in https://github.com/codeigniter4/CodeIgniter4/pull/5125 +* Fix variable variable `$$id` in RedisHandler by @Terrorboy in https://github.com/codeigniter4/CodeIgniter4/pull/5062 +* Fixes and enhancements to Exceptions by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5052 + +**Implemented enhancements:** + +* feat: `_` can be used as separators in environment variable names by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5156 +* Multiple filters for a route and classname filter by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5128 +* Feature - Mark duplicate queries by @danielTiringer in https://github.com/codeigniter4/CodeIgniter4/pull/5185 +* [Debug] Add formatted query string to timeline. by @sfadschm in https://github.com/codeigniter4/CodeIgniter4/pull/5196 +* [Debug] Improve keyword highlighting and escaping of query strings. by @sfadschm in https://github.com/codeigniter4/CodeIgniter4/pull/5200 +* Add `dropKey` method to `Forge` by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5171 +* Reduce memory usage of insertBatch(), updateBatch() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5202 +* Add Session based CSRF Protection by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5201 +* feat: add valid_url_strict rule by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5268 + +**Merged pull requests:** + +* Merge branch '4.2' by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5060 +* Update to latest laminas-escaper 2.9.0 by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/5065 +* Remove unintended dead code in pre-commit by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5116 +* Adjust orange color in debug toolbar by @vlakoff in https://github.com/codeigniter4/CodeIgniter4/pull/5136 +* Extract method to get prefix for DB access function by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5178 +* Improve `model()` auto-completion by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5186 +* Rename toolbar loader to be a regular JS file by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5224 +* [HTTP] Update Http Status Description based on latest iana.org by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/5235 +* Remove CSRF properties by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5231 +* Remove static variables for PHP 8.1 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5262 +* Replace usage of `FILTER_SANITIZE_STRING` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5263 +* Simplify logic of `number_to_roman` function by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5270 +* Fix compatibility of `PgSql\Result` on closing the result instance by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5279 +* Fix compatibility of Postgres result for PHP 8.1 by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5278 + +**New Contributors** + +* @Terrorboy made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/5062 +* @vlakoff made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/5136 +* @Felipebros made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/5152 +* @daycry made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/5142 +* @danielTiringer made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/5172 + +## [v4.1.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.4) (2021-09-06) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.3...v4.1.4) + +This release focuses on code style. All changes (except those noted below) are cosmetic to bring the code in line with the new +[CodeIgniter Coding Standard](https://github.com/CodeIgniter/coding-standard) (based on PSR-12). + +**What's Changed** + +* Use php-cs-fixer as coding style tool by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/4770 +* Remove unused local variables by @jeromegamez in https://github.com/codeigniter4/CodeIgniter4/pull/4783 +* Use static lambda if a binding to `$this` is not required. by @jeromegamez in https://github.com/codeigniter4/CodeIgniter4/pull/4784 +* Use/Fix `preg_quote()` delimiters by @jeromegamez in https://github.com/codeigniter4/CodeIgniter4/pull/4789 +* Don't override `$path` parameter by @jeromegamez in https://github.com/codeigniter4/CodeIgniter4/pull/4787 +* Don't override `$value` parameter by @jeromegamez in https://github.com/codeigniter4/CodeIgniter4/pull/4788 +* Add brackets to clarify intent and avoid unwanted side-effects by @jeromegamez in https://github.com/codeigniter4/CodeIgniter4/pull/4791 +* Remove removed `safe_mode` ini Option by @jeromegamez in https://github.com/codeigniter4/CodeIgniter4/pull/4795 +* It will fix undefined index cid error when sending emails with embedded images by @mmfarhan in https://github.com/codeigniter4/CodeIgniter4/pull/4798 +* Revert Model coalesce by @MGatner in https://github.com/codeigniter4/CodeIgniter4/pull/4819 +* Master language constructs shall be used instead of aliases. by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/4847 +* [Commands] Remove unused $minPHPVersion property at Serve command by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/4852 +* Update to latest laminas-escaper ^2.8 by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/4878 +* Remove 'memory_usage' from 'displayPerformanceMetrics()' comment by @Mauricevb in https://github.com/codeigniter4/CodeIgniter4/pull/4939 +* Remove useless code separator comments by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/4946 +* Optimize Filters by @mostafakhudair in https://github.com/codeigniter4/CodeIgniter4/pull/4965 +* Fix properly the phpstan error in 0.12.93 by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/4970 +* Manual cleanup of docblocks and comments by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/4964 +* Make Cookie compatible with ArrayAccess by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5004 +* Replace deprecated FILTER_SANITIZE_STRING by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5005 +* Make CookieStore compatible with IteratorAggregate::getIterator by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5010 +* Make the session handlers all compatible with SessionHandlerInterface by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5012 +* Make CITestStreamFilter compatible with php_user_filter by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5014 +* Make Time compatible with DateTime by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5022 +* Add `ReturnTypeWillChange` attribute to Entity by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5028 +* Replace unused Entity private method by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5029 +* Make File compatible with SplFileInfo by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5040 +* Update documentation code samples by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5039 +* PHP Copy-Paste Detector by @MGatner in https://github.com/codeigniter4/CodeIgniter4/pull/5031 +* Fix key casting in form_dropdown helper. by @sfadschm in https://github.com/codeigniter4/CodeIgniter4/pull/5035 +* Switch to official coding standard by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5038 + +**New Contributors** + +* @mmfarhan made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/4798 +* @Mauricevb made their first contribution in https://github.com/codeigniter4/CodeIgniter4/pull/4939 + +## [v4.1.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.3) (2021-06-06) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.2...v4.1.3) + +**Fixed bugs:** + +- Bug: Error using SQLITE3 strftime in CodeIgniter 4.1.2 [\#4760](https://github.com/codeigniter4/CodeIgniter4/issues/4760) +- Bug: Caching something through cron, is not accessible in the web application [\#4751](https://github.com/codeigniter4/CodeIgniter4/issues/4751) +- Bug: SQLite Drop Column [\#4746](https://github.com/codeigniter4/CodeIgniter4/issues/4746) +- Bug: CURL Class - BaseURI options notworking [\#4713](https://github.com/codeigniter4/CodeIgniter4/issues/4713) +- Bug: autorouting [\#4711](https://github.com/codeigniter4/CodeIgniter4/issues/4711) +- Bug: curlrequest not using baseURI on localhost [\#4707](https://github.com/codeigniter4/CodeIgniter4/issues/4707) +- Bug: cli not working with cron [\#4699](https://github.com/codeigniter4/CodeIgniter4/issues/4699) + +**Closed issues:** + +- Bug: Class 'Locale' not found [\#4775](https://github.com/codeigniter4/CodeIgniter4/issues/4775) +- Bug: deprecated notice on CodeIgniter\HTTP\RequestInterface::getMethod\(\) [\#4717](https://github.com/codeigniter4/CodeIgniter4/issues/4717) +- Allow to join models between primary keys and foreign keys [\#4714](https://github.com/codeigniter4/CodeIgniter4/issues/4714) +- DateTime::\_\_construct\(\): Failed to parse time string \(\) at position 0 \(�\): Unexpected character [\#4708](https://github.com/codeigniter4/CodeIgniter4/issues/4708) +- Bug: Query Builder breaks with SQL function LENGTH\(\) and column name "row" [\#4687](https://github.com/codeigniter4/CodeIgniter4/issues/4687) + +**Merged pull requests:** + +- Expand Query named binds recognition [\#4769](https://github.com/codeigniter4/CodeIgniter4/pull/4769) ([paulbalandan](https://github.com/paulbalandan)) +- \[Rector\] Remove @var from class constant [\#4766](https://github.com/codeigniter4/CodeIgniter4/pull/4766) ([samsonasik](https://github.com/samsonasik)) +- Set WarningsReturnAsErrors = 0 before connection [\#4762](https://github.com/codeigniter4/CodeIgniter4/pull/4762) ([obelisk-services](https://github.com/obelisk-services)) +- \[Rector\] Apply Rector: VarConstantCommentRector [\#4759](https://github.com/codeigniter4/CodeIgniter4/pull/4759) ([samsonasik](https://github.com/samsonasik)) +- \[Autoloader\] include\_once is not needed on Autoloader::loadClass\(\) with no namespace [\#4756](https://github.com/codeigniter4/CodeIgniter4/pull/4756) ([samsonasik](https://github.com/samsonasik)) +- Fix imagemagick build [\#4755](https://github.com/codeigniter4/CodeIgniter4/pull/4755) ([michalsn](https://github.com/michalsn)) +- \[Rector\] Apply Rector: MoveVariableDeclarationNearReferenceRector [\#4752](https://github.com/codeigniter4/CodeIgniter4/pull/4752) ([samsonasik](https://github.com/samsonasik)) +- SQLite3 "nullable" [\#4749](https://github.com/codeigniter4/CodeIgniter4/pull/4749) ([MGatner](https://github.com/MGatner)) +- Remove $response variable at ControllerResponse::\_\_construct\(\) as never defined [\#4747](https://github.com/codeigniter4/CodeIgniter4/pull/4747) ([samsonasik](https://github.com/samsonasik)) +- Use variable for Config/Paths config to reduce repetitive definition [\#4745](https://github.com/codeigniter4/CodeIgniter4/pull/4745) ([samsonasik](https://github.com/samsonasik)) +- \[Rector\] Apply Rector : ListToArrayDestructRector [\#4743](https://github.com/codeigniter4/CodeIgniter4/pull/4743) ([samsonasik](https://github.com/samsonasik)) +- Add default TTL [\#4742](https://github.com/codeigniter4/CodeIgniter4/pull/4742) ([MGatner](https://github.com/MGatner)) +- update return sample of `dot array\_search\(\)` [\#4740](https://github.com/codeigniter4/CodeIgniter4/pull/4740) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Additional check for `$argv` variable when detecting CLI [\#4739](https://github.com/codeigniter4/CodeIgniter4/pull/4739) ([paulbalandan](https://github.com/paulbalandan)) +- Ensure variable declarations [\#4737](https://github.com/codeigniter4/CodeIgniter4/pull/4737) ([jeromegamez](https://github.com/jeromegamez)) +- Fix setting of value in Cookie's flag attributes [\#4736](https://github.com/codeigniter4/CodeIgniter4/pull/4736) ([paulbalandan](https://github.com/paulbalandan)) +- Add missing imports [\#4735](https://github.com/codeigniter4/CodeIgniter4/pull/4735) ([jeromegamez](https://github.com/jeromegamez)) +- Add environment spark command [\#4734](https://github.com/codeigniter4/CodeIgniter4/pull/4734) ([paulbalandan](https://github.com/paulbalandan)) +- Remove explicit condition that is always true [\#4731](https://github.com/codeigniter4/CodeIgniter4/pull/4731) ([jeromegamez](https://github.com/jeromegamez)) +- Deduplicate code [\#4730](https://github.com/codeigniter4/CodeIgniter4/pull/4730) ([jeromegamez](https://github.com/jeromegamez)) +- Replace `isset\(\)` with the `??` null coalesce operator [\#4729](https://github.com/codeigniter4/CodeIgniter4/pull/4729) ([jeromegamez](https://github.com/jeromegamez)) +- Remove unused imports [\#4728](https://github.com/codeigniter4/CodeIgniter4/pull/4728) ([jeromegamez](https://github.com/jeromegamez)) +- Fix truncated SCRIPT\_NAME [\#4726](https://github.com/codeigniter4/CodeIgniter4/pull/4726) ([MGatner](https://github.com/MGatner)) +- Expand CLI detection [\#4725](https://github.com/codeigniter4/CodeIgniter4/pull/4725) ([paulbalandan](https://github.com/paulbalandan)) +- \[Rector\] Add custom Rector Rule: RemoveErrorSuppressInTryCatchStmtsRector rector rule [\#4724](https://github.com/codeigniter4/CodeIgniter4/pull/4724) ([samsonasik](https://github.com/samsonasik)) +- Test with MySQL 8 [\#4721](https://github.com/codeigniter4/CodeIgniter4/pull/4721) ([jeromegamez](https://github.com/jeromegamez)) +- Replace URI string casts [\#4716](https://github.com/codeigniter4/CodeIgniter4/pull/4716) ([MGatner](https://github.com/MGatner)) +- Format URI directly [\#4715](https://github.com/codeigniter4/CodeIgniter4/pull/4715) ([MGatner](https://github.com/MGatner)) +- Additional File functions [\#4712](https://github.com/codeigniter4/CodeIgniter4/pull/4712) ([MGatner](https://github.com/MGatner)) +- Remove unused private rowOffset property in Database/SQLSRV/Result.php [\#4709](https://github.com/codeigniter4/CodeIgniter4/pull/4709) ([samsonasik](https://github.com/samsonasik)) +- Check for configured instead of hard-coded database in DbUtilsTest [\#4705](https://github.com/codeigniter4/CodeIgniter4/pull/4705) ([jeromegamez](https://github.com/jeromegamez)) +- Revert UG margins [\#4704](https://github.com/codeigniter4/CodeIgniter4/pull/4704) ([MGatner](https://github.com/MGatner)) +- Create .git/hooks directory if not already present [\#4703](https://github.com/codeigniter4/CodeIgniter4/pull/4703) ([jeromegamez](https://github.com/jeromegamez)) +- Annotate specifically designed slow tests with custom limits [\#4698](https://github.com/codeigniter4/CodeIgniter4/pull/4698) ([paulbalandan](https://github.com/paulbalandan)) +- Cache robustness [\#4697](https://github.com/codeigniter4/CodeIgniter4/pull/4697) ([MGatner](https://github.com/MGatner)) + +## [v4.1.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.2) (2021-05-18) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.1...v4.1.2) + +**Implemented enhancements:** + +- New HTTP classes, ``Cookie`` and ``CookieStore``, for abstracting web cookies. +- New ``assertRedirectTo()`` assertion available for HTTP tests. +- New logger handler, ``ErrorlogHandler``, that writes to ``error_log()``. +- Entity. Added custom type casting functionality. +- New option in routing. The ``priority`` option lower the priority of specific route processing. +- The ``Autoloader`` class can now load files which do not contain PHP classes. The list of `non-class` files will be listed in the ``$files`` property of ``Config\Autoload`` class. + +**Deprecations:** + +- Deprecated ``Codeigniter\View\View::$currentSection`` property. +- Language strings and exceptions on invalid cookie samesite are deprecated for the ``CookieException``'s own exception message. +- Deprecated `CodeIgniter\Entity` in favor of `CodeIgniter\Entity\Entity` +- Deprecated cookie-related properties of ``Response`` in order to use the ``Cookie`` class. +- Deprecated cookie-related properties of ``Security`` in order to use the ``Cookie`` class. +- Deprecated cookie-related properties of ``Session`` in order to use the ``Cookie`` class. +- Deprecated ``Security::isExpired()`` to use the ``Cookie``'s internal expires status. +- Deprecated ``CIDatabaseTestCase`` to use the ``DatabaseTestTrait`` instead. +- Deprecated ``FeatureTestCase`` to use the ``FeatureTestTrait`` instead. +- Deprecated ``ControllerTester`` to use the ``ControllerTestTrait`` instead. +- Consolidated and deprecated ``ControllerResponse`` and ``FeatureResponse`` in favor of ``TestResponse``. +- Deprecated ``Time::instance()``, use ``Time::createFromInstance()`` instead (now accepts ``DateTimeInterface``). +- Deprecated ``IncomingRequest::removeRelativeDirectory()``, use ``URI::removeDotSegments()`` instead +- Deprecated ``\API\ResponseTrait::failValidationError`` to use ``\API\ResponseTrait::failValidationErrors`` instead + +**Fixed bugs:** + +- Bug: NULL Fields definition not working for MSQLi Forge in Migration due property $\_null and $null names difference [\#4693](https://github.com/codeigniter4/CodeIgniter4/issues/4693) +- Bug: Missing Cookie Config [\#4619](https://github.com/codeigniter4/CodeIgniter4/issues/4619) +- Bug: [\#4610](https://github.com/codeigniter4/CodeIgniter4/issues/4610) +- Bug: Customized Validation language does not take effect [\#4597](https://github.com/codeigniter4/CodeIgniter4/issues/4597) +- Bug: colon issue in query binding [\#4595](https://github.com/codeigniter4/CodeIgniter4/issues/4595) +- Bug: set\_checkbox\(\) default value not working [\#4582](https://github.com/codeigniter4/CodeIgniter4/issues/4582) +- Bug: Request & Response objects stored multiple times [\#4580](https://github.com/codeigniter4/CodeIgniter4/issues/4580) +- Bug: Class information on output is missing during migrate:rollback command [\#4579](https://github.com/codeigniter4/CodeIgniter4/issues/4579) +- Bug: Cookie path replaced with system's PATH env variable [\#4559](https://github.com/codeigniter4/CodeIgniter4/issues/4559) +- Bug: Validation::withRequest\(\) method does not receive data. [\#4552](https://github.com/codeigniter4/CodeIgniter4/issues/4552) +- `esc` and `nl2br` combo gives nasty HTML error output [\#4533](https://github.com/codeigniter4/CodeIgniter4/issues/4533) +- Bug: typo error when creating a model using php spark make:model [\#4525](https://github.com/codeigniter4/CodeIgniter4/issues/4525) +- Bug: if\_exist not working with ".\*" notation [\#4521](https://github.com/codeigniter4/CodeIgniter4/issues/4521) +- Bug: Query::matchSimpleBinds index problem only toolbar. [\#4518](https://github.com/codeigniter4/CodeIgniter4/issues/4518) +- Bug: Unable to use debugger toolbar on a live server [\#4516](https://github.com/codeigniter4/CodeIgniter4/issues/4516) +- Missing config options and config options repetition [\#4504](https://github.com/codeigniter4/CodeIgniter4/issues/4504) +- Bug: db:create command should create database even database not exists yet, and defined in .env [\#4498](https://github.com/codeigniter4/CodeIgniter4/issues/4498) +- Bug: Differences in file names created with CLI command [\#4495](https://github.com/codeigniter4/CodeIgniter4/issues/4495) +- Bug: Session removeTempdata\(\) method not accepting arrays. [\#4490](https://github.com/codeigniter4/CodeIgniter4/issues/4490) +- Bug: Session remove\(\) method not removing tempdata sessions. [\#4489](https://github.com/codeigniter4/CodeIgniter4/issues/4489) +- Bug: Session getFlashdata\(\) not support for dot notation. [\#4488](https://github.com/codeigniter4/CodeIgniter4/issues/4488) +- Bug: New Service replacement fails at service provider precedence on core factory implementations [\#4483](https://github.com/codeigniter4/CodeIgniter4/issues/4483) +- Bug: Filter is not work ! [\#4482](https://github.com/codeigniter4/CodeIgniter4/issues/4482) +- Bug: PHPStorm anlysis fault [\#4474](https://github.com/codeigniter4/CodeIgniter4/issues/4474) +- Bug: apache mod\_userdir causes weird URL segment duplication [\#4471](https://github.com/codeigniter4/CodeIgniter4/issues/4471) +- Postgre Forge doesn't use schema in creating tables [\#4469](https://github.com/codeigniter4/CodeIgniter4/issues/4469) +- Bug: UG QueryBuilder::from\(\) wrong SQL example. [\#4464](https://github.com/codeigniter4/CodeIgniter4/issues/4464) +- Bug: results on getX\(\) not equal [\#4452](https://github.com/codeigniter4/CodeIgniter4/issues/4452) +- Bug: Queries with LOWER\( throwing errors [\#4443](https://github.com/codeigniter4/CodeIgniter4/issues/4443) +- Bug: RouteCollection::getHTTPVerb\(\) can return null [\#4435](https://github.com/codeigniter4/CodeIgniter4/issues/4435) +- Bug: can't run `spark migrate` on CI server [\#4428](https://github.com/codeigniter4/CodeIgniter4/issues/4428) +- Bug: URI Routing Placeholders [\#4421](https://github.com/codeigniter4/CodeIgniter4/issues/4421) +- Bug: Third Flags needs default [\#4411](https://github.com/codeigniter4/CodeIgniter4/issues/4411) +- Bug: another Flags needs default [\#4410](https://github.com/codeigniter4/CodeIgniter4/issues/4410) +- Bug: Flags needs default value [\#4409](https://github.com/codeigniter4/CodeIgniter4/issues/4409) +- Bug: log\_message passed object [\#4407](https://github.com/codeigniter4/CodeIgniter4/issues/4407) +- Bug: Model creation error [\#4393](https://github.com/codeigniter4/CodeIgniter4/issues/4393) +- Bug: If the file name contains "app", "php spark make: migration" will not create it successfully. [\#4383](https://github.com/codeigniter4/CodeIgniter4/issues/4383) +- Bug: IncomingRequest.php getVar\(\) [\#4381](https://github.com/codeigniter4/CodeIgniter4/issues/4381) +- Bug: Minimum PHP Version Discrepancy [\#4361](https://github.com/codeigniter4/CodeIgniter4/issues/4361) +- Bug: insertBatch generates an incorrect SQL query if the fields differ only in number at the end [\#4345](https://github.com/codeigniter4/CodeIgniter4/issues/4345) +- Bug: Database/Live tests fail [\#4336](https://github.com/codeigniter4/CodeIgniter4/issues/4336) +- Bug: red line on model by setPrefix & prefixTable [\#4329](https://github.com/codeigniter4/CodeIgniter4/issues/4329) +- Bug: $model-\>errors\(\) produce output when no error [\#4323](https://github.com/codeigniter4/CodeIgniter4/issues/4323) +- Bug: Can't Rewrite System Validation Messages [\#4318](https://github.com/codeigniter4/CodeIgniter4/issues/4318) +- Bug: "useSoftDelete" for model files generated by `phpspark` [\#4316](https://github.com/codeigniter4/CodeIgniter4/issues/4316) +- Bug: require the unused namespace [\#4309](https://github.com/codeigniter4/CodeIgniter4/issues/4309) +- Bug: FeatureTest cannot assert Status\(404\) [\#4306](https://github.com/codeigniter4/CodeIgniter4/issues/4306) +- Bug: BaseBuilder-\>\_insert [\#4302](https://github.com/codeigniter4/CodeIgniter4/issues/4302) +- Bug: previous\_url\(\) contains current URL after reloading a page. [\#4299](https://github.com/codeigniter4/CodeIgniter4/issues/4299) +- Bug: Cannot add route to controller in filename with dash/hyphen [\#4294](https://github.com/codeigniter4/CodeIgniter4/issues/4294) +- Bug: FeatureTest dies when throws RedirectException/cached page [\#4288](https://github.com/codeigniter4/CodeIgniter4/issues/4288) +- Bug: /test.php show home page [\#4263](https://github.com/codeigniter4/CodeIgniter4/issues/4263) +- Bug: Fabricator::fake\(\) function is breaking when it returns an array [\#4261](https://github.com/codeigniter4/CodeIgniter4/issues/4261) +- Bug: Session issue with CI Environment set to Testing \(CI4\) [\#4248](https://github.com/codeigniter4/CodeIgniter4/issues/4248) +- Bug: Wrong HTML code in output of "form\_input" helper function [\#4235](https://github.com/codeigniter4/CodeIgniter4/issues/4235) +- make:scaffold input information is missing [\#4230](https://github.com/codeigniter4/CodeIgniter4/issues/4230) +- Bug: CodeIgniter 4.1.1 - csrf token is always regenerated [\#4224](https://github.com/codeigniter4/CodeIgniter4/issues/4224) +- Bug: getFileMultiple expects an "0" index but string is given [\#4221](https://github.com/codeigniter4/CodeIgniter4/issues/4221) +- Bug: cannot resolve Services::xxx\(\) [\#4220](https://github.com/codeigniter4/CodeIgniter4/issues/4220) +- Bug: tfoot\_open / tfoot\_close have no default when using custom table template [\#4219](https://github.com/codeigniter4/CodeIgniter4/issues/4219) +- Bug: Spark PHP version [\#4213](https://github.com/codeigniter4/CodeIgniter4/issues/4213) +- Bug: Soft deletes and model validation when unique [\#4162](https://github.com/codeigniter4/CodeIgniter4/issues/4162) +- Bug: Debug Toolbar - Memory Leak - Allocation Exception [\#4137](https://github.com/codeigniter4/CodeIgniter4/issues/4137) +- current\_url\(\) global method returning URLs without the index.php part. [\#4116](https://github.com/codeigniter4/CodeIgniter4/issues/4116) +- Bug: appstarter HealthTest::testBaseUrlHasBeenSet fails [\#3977](https://github.com/codeigniter4/CodeIgniter4/issues/3977) +- Bug: Time::createFromTimestamp\(\) uses default timezone, not UTC for timestamp [\#3951](https://github.com/codeigniter4/CodeIgniter4/issues/3951) +- Bug: Unexpected filter behavior [\#3874](https://github.com/codeigniter4/CodeIgniter4/issues/3874) +- Bug: Double initializing of class [\#3855](https://github.com/codeigniter4/CodeIgniter4/issues/3855) +- Bug: Registrars take priority over .env [\#3845](https://github.com/codeigniter4/CodeIgniter4/issues/3845) +- Bug: SQLite3 NOT NULL prevents inserts [\#3599](https://github.com/codeigniter4/CodeIgniter4/issues/3599) +- Bug: Model doesn't reset errors in FeatureTestCase [\#3578](https://github.com/codeigniter4/CodeIgniter4/issues/3578) +- Bug: Problem in "/system/Database/Query.php" function "compileBinds\(\)" [\#3566](https://github.com/codeigniter4/CodeIgniter4/issues/3566) +- Bug: Exceptions cause risky Feature Tests [\#3114](https://github.com/codeigniter4/CodeIgniter4/issues/3114) +- Bug: current\_url\(\) loses subdomain [\#3004](https://github.com/codeigniter4/CodeIgniter4/issues/3004) + +**Closed issues:** + +- mysqli\_sql\_exception \#2002 [\#4640](https://github.com/codeigniter4/CodeIgniter4/issues/4640) +- intl - Name missing exception [\#4636](https://github.com/codeigniter4/CodeIgniter4/issues/4636) +- HUGE BUG: update\(\) function updates all records if id is empty [\#4617](https://github.com/codeigniter4/CodeIgniter4/issues/4617) +- Bug: Validation rule "matches" doesn't work [\#4615](https://github.com/codeigniter4/CodeIgniter4/issues/4615) +- Bug: chmod 777 writable/cache fixed codeignitor install for me [\#4598](https://github.com/codeigniter4/CodeIgniter4/issues/4598) +- Model-\>where method does not exist [\#4583](https://github.com/codeigniter4/CodeIgniter4/issues/4583) +- Transactions between two databases [\#4578](https://github.com/codeigniter4/CodeIgniter4/issues/4578) +- Bug: Mysql connection issue with MYSQLI\_CLIENT\_SSL\_DONT\_VERIFY\_SERVER\_CERT [\#4558](https://github.com/codeigniter4/CodeIgniter4/issues/4558) +- Release cycle [\#4526](https://github.com/codeigniter4/CodeIgniter4/issues/4526) +- Call to a member function setContentType\(\) on null - Responsetrait [\#4524](https://github.com/codeigniter4/CodeIgniter4/issues/4524) +- Bug: mock single\_service [\#4515](https://github.com/codeigniter4/CodeIgniter4/issues/4515) +- Bug: failed to open stream [\#4514](https://github.com/codeigniter4/CodeIgniter4/issues/4514) +- Array Validation Fails [\#4510](https://github.com/codeigniter4/CodeIgniter4/issues/4510) +- Bug: return $this-\>failValidationError\($validation-\>getErrors\(\)\) Has Invalid Signature [\#4506](https://github.com/codeigniter4/CodeIgniter4/issues/4506) +- Bug: Ok The Model ERRORS came right back with the newest build again! [\#4491](https://github.com/codeigniter4/CodeIgniter4/issues/4491) +- Bug: Composer install loads require-dev when I require another package [\#4477](https://github.com/codeigniter4/CodeIgniter4/issues/4477) +- Logger [\#4460](https://github.com/codeigniter4/CodeIgniter4/issues/4460) +- Bug: Improve creation of scaffolds with the CLI [\#4441](https://github.com/codeigniter4/CodeIgniter4/issues/4441) +- Request: Feature Test Optimization [\#4438](https://github.com/codeigniter4/CodeIgniter4/issues/4438) +- request.getVar not populated with GET parameters | unexpected behavior [\#4418](https://github.com/codeigniter4/CodeIgniter4/issues/4418) +- Running via CLI - Only Default Controller works [\#4415](https://github.com/codeigniter4/CodeIgniter4/issues/4415) +- Parser content typehint \[strict\_types=1\] [\#4412](https://github.com/codeigniter4/CodeIgniter4/issues/4412) +- Toolbar::setFiles\(\) requires int \[strict\_types=1\] [\#4408](https://github.com/codeigniter4/CodeIgniter4/issues/4408) +- FeatureTest currently supports file testing? [\#4405](https://github.com/codeigniter4/CodeIgniter4/issues/4405) +- Bug: set404Override now working in group rotes [\#4400](https://github.com/codeigniter4/CodeIgniter4/issues/4400) +- Dynamic URL [\#4394](https://github.com/codeigniter4/CodeIgniter4/issues/4394) +- ErrorException preg\_replace\_callback\(\): Unknown modifier '{' SYSTEMPATH/View/Parser.php at line 584 [\#4367](https://github.com/codeigniter4/CodeIgniter4/issues/4367) +- Feature: In HTTP Feature Testing, delivering in body in application/json format [\#4362](https://github.com/codeigniter4/CodeIgniter4/issues/4362) +- \[Dev\] Database Live Tests should depart from using deprecated CIDatabaseTestCase [\#4351](https://github.com/codeigniter4/CodeIgniter4/issues/4351) +- Bug: Migration in module \(different namespace\) do not find migrations [\#4348](https://github.com/codeigniter4/CodeIgniter4/issues/4348) +- Bug: getVar does not look at $\_SESSION as documentation suggests [\#4284](https://github.com/codeigniter4/CodeIgniter4/issues/4284) +- QBSelect, QBFrom, other properties cannot be accessed, modified from the model. [\#4255](https://github.com/codeigniter4/CodeIgniter4/issues/4255) +- Dev: Restrictions on trait "ResponseTrait" [\#4238](https://github.com/codeigniter4/CodeIgniter4/issues/4238) +- ResponseTrait trait \> Can the description support array? [\#4237](https://github.com/codeigniter4/CodeIgniter4/issues/4237) +- Feature: add old data in afterUpdate model event [\#4234](https://github.com/codeigniter4/CodeIgniter4/issues/4234) +- Dev: [\#4233](https://github.com/codeigniter4/CodeIgniter4/issues/4233) +- Cache unable to write to /var/www/html/ci4test/writable/cache/ [\#4227](https://github.com/codeigniter4/CodeIgniter4/issues/4227) +- Documentation: multiple databases setup in the ENV also need to be setup in the database config file [\#4218](https://github.com/codeigniter4/CodeIgniter4/issues/4218) +- Documentation: $this-\>request-\>setLocale\(\) is missing in documentation [\#4091](https://github.com/codeigniter4/CodeIgniter4/issues/4091) +- vars in .env sometimes returns null [\#3992](https://github.com/codeigniter4/CodeIgniter4/issues/3992) +- parseRequestURI dose not override globals\['server'\] ? [\#3976](https://github.com/codeigniter4/CodeIgniter4/issues/3976) +- Feature: Spark header Suppression [\#3918](https://github.com/codeigniter4/CodeIgniter4/issues/3918) +- Feature: AJAX filters don't work [\#2314](https://github.com/codeigniter4/CodeIgniter4/issues/2314) +- Request: Bulk route filters with parameters [\#2078](https://github.com/codeigniter4/CodeIgniter4/issues/2078) +- Need a global way to set config values dynamically [\#1661](https://github.com/codeigniter4/CodeIgniter4/issues/1661) +- Feature Request : support the db config instead of .env while the core is initialzed [\#1618](https://github.com/codeigniter4/CodeIgniter4/issues/1618) +- TODO Database BaseConnection needs better connections [\#1253](https://github.com/codeigniter4/CodeIgniter4/issues/1253) + +**Merged pull requests:** + +- Fix nullable type not showing in SQL string [\#4696](https://github.com/codeigniter4/CodeIgniter4/pull/4696) ([paulbalandan](https://github.com/paulbalandan)) +- Add reference to cache repo [\#4694](https://github.com/codeigniter4/CodeIgniter4/pull/4694) ([MGatner](https://github.com/MGatner)) +- Allow CI Environments [\#4692](https://github.com/codeigniter4/CodeIgniter4/pull/4692) ([MGatner](https://github.com/MGatner)) +- Add URI cast [\#4691](https://github.com/codeigniter4/CodeIgniter4/pull/4691) ([MGatner](https://github.com/MGatner)) +- MockCache::getCacheInfo\(\) [\#4689](https://github.com/codeigniter4/CodeIgniter4/pull/4689) ([MGatner](https://github.com/MGatner)) +- Remove Psr\Cache [\#4688](https://github.com/codeigniter4/CodeIgniter4/pull/4688) ([MGatner](https://github.com/MGatner)) +- Spacing issues [\#4686](https://github.com/codeigniter4/CodeIgniter4/pull/4686) ([MGatner](https://github.com/MGatner)) +- \[Rector\] Update rector 0.11.2 and phpstan 0.12.86 [\#4685](https://github.com/codeigniter4/CodeIgniter4/pull/4685) ([samsonasik](https://github.com/samsonasik)) +- Optimize CommandRunner and Commands [\#4683](https://github.com/codeigniter4/CodeIgniter4/pull/4683) ([paulbalandan](https://github.com/paulbalandan)) +- Revert Actions minor version [\#4682](https://github.com/codeigniter4/CodeIgniter4/pull/4682) ([MGatner](https://github.com/MGatner)) +- Revert Actions minor versioning [\#4681](https://github.com/codeigniter4/CodeIgniter4/pull/4681) ([MGatner](https://github.com/MGatner)) +- Bump shivammathur/setup-php from 2 to 2.11.0 [\#4679](https://github.com/codeigniter4/CodeIgniter4/pull/4679) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Bump actions/checkout from 2 to 2.3.4 [\#4678](https://github.com/codeigniter4/CodeIgniter4/pull/4678) ([dependabot[bot]](https://github.com/apps/dependabot)) +- \[Rector\] Update rector to 0.10.22, remove symplify/composer-json-manipulator [\#4677](https://github.com/codeigniter4/CodeIgniter4/pull/4677) ([samsonasik](https://github.com/samsonasik)) +- URL Functions [\#4675](https://github.com/codeigniter4/CodeIgniter4/pull/4675) ([MGatner](https://github.com/MGatner)) +- Remove unused imports [\#4674](https://github.com/codeigniter4/CodeIgniter4/pull/4674) ([paulbalandan](https://github.com/paulbalandan)) +- Split URL Helper tests [\#4672](https://github.com/codeigniter4/CodeIgniter4/pull/4672) ([MGatner](https://github.com/MGatner)) +- \[Rector\] Apply Rector: RemoveUnusedPrivatePropertyRector [\#4671](https://github.com/codeigniter4/CodeIgniter4/pull/4671) ([samsonasik](https://github.com/samsonasik)) +- \[UG\] update line number for "managing apps" [\#4670](https://github.com/codeigniter4/CodeIgniter4/pull/4670) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Add setLocale to UG [\#4669](https://github.com/codeigniter4/CodeIgniter4/pull/4669) ([MGatner](https://github.com/MGatner)) +- UTC Time from timestamp [\#4668](https://github.com/codeigniter4/CodeIgniter4/pull/4668) ([MGatner](https://github.com/MGatner)) +- PSR: Cache [\#4667](https://github.com/codeigniter4/CodeIgniter4/pull/4667) ([MGatner](https://github.com/MGatner)) +- Limit cache filenames [\#4666](https://github.com/codeigniter4/CodeIgniter4/pull/4666) ([MGatner](https://github.com/MGatner)) +- Use descriptive failure message for `assertLogged` [\#4665](https://github.com/codeigniter4/CodeIgniter4/pull/4665) ([paulbalandan](https://github.com/paulbalandan)) +- \[Rector\] Use $containerConfigurator-\>import\(\) instead of "sets" Option [\#4664](https://github.com/codeigniter4/CodeIgniter4/pull/4664) ([samsonasik](https://github.com/samsonasik)) +- Update rector/rector requirement from 0.10.19 to 0.10.21 [\#4663](https://github.com/codeigniter4/CodeIgniter4/pull/4663) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Spark header suppression [\#4661](https://github.com/codeigniter4/CodeIgniter4/pull/4661) ([MGatner](https://github.com/MGatner)) +- Registrar and .env priority [\#4659](https://github.com/codeigniter4/CodeIgniter4/pull/4659) ([MGatner](https://github.com/MGatner)) +- Reset Single Service [\#4657](https://github.com/codeigniter4/CodeIgniter4/pull/4657) ([MGatner](https://github.com/MGatner)) +- Unify migration message format for `migrate` and `migrate:rollback` [\#4656](https://github.com/codeigniter4/CodeIgniter4/pull/4656) ([paulbalandan](https://github.com/paulbalandan)) +- \[Scripts\] Make sure bash script still works in Windows [\#4655](https://github.com/codeigniter4/CodeIgniter4/pull/4655) ([paulbalandan](https://github.com/paulbalandan)) +- change instance\(\) to createFromInstance\(\) [\#4654](https://github.com/codeigniter4/CodeIgniter4/pull/4654) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Add branch alias for develop branch [\#4652](https://github.com/codeigniter4/CodeIgniter4/pull/4652) ([paulbalandan](https://github.com/paulbalandan)) +- Refactor URI detection [\#4651](https://github.com/codeigniter4/CodeIgniter4/pull/4651) ([MGatner](https://github.com/MGatner)) +- \[Scipts\] Ensure admin/setup.sh exists before run bash admin/setup.sh [\#4650](https://github.com/codeigniter4/CodeIgniter4/pull/4650) ([samsonasik](https://github.com/samsonasik)) +- Update rector/rector requirement from 0.10.17 to 0.10.19 [\#4649](https://github.com/codeigniter4/CodeIgniter4/pull/4649) ([dependabot[bot]](https://github.com/apps/dependabot)) +- \[ci skip\] Remove ajax filter reference from docs. Fixes \#2314 [\#4648](https://github.com/codeigniter4/CodeIgniter4/pull/4648) ([lonnieezell](https://github.com/lonnieezell)) +- Internal URI handling [\#4646](https://github.com/codeigniter4/CodeIgniter4/pull/4646) ([MGatner](https://github.com/MGatner)) +- URI::removeDotSegments\(\) [\#4644](https://github.com/codeigniter4/CodeIgniter4/pull/4644) ([MGatner](https://github.com/MGatner)) +- \[Rector\] Pin "nikic/php-parser": "4.10.4" [\#4642](https://github.com/codeigniter4/CodeIgniter4/pull/4642) ([samsonasik](https://github.com/samsonasik)) +- Update to psr/log v1.1.4 [\#4641](https://github.com/codeigniter4/CodeIgniter4/pull/4641) ([paulbalandan](https://github.com/paulbalandan)) +- Update rector/rector requirement from 0.10.15 to 0.10.17 [\#4639](https://github.com/codeigniter4/CodeIgniter4/pull/4639) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Update MockCache [\#4638](https://github.com/codeigniter4/CodeIgniter4/pull/4638) ([MGatner](https://github.com/MGatner)) +- Cache Key Validation [\#4637](https://github.com/codeigniter4/CodeIgniter4/pull/4637) ([MGatner](https://github.com/MGatner)) +- \[Cache\] Allow covariant returns and optimize code [\#4635](https://github.com/codeigniter4/CodeIgniter4/pull/4635) ([paulbalandan](https://github.com/paulbalandan)) +- Refactor ComposerScripts [\#4634](https://github.com/codeigniter4/CodeIgniter4/pull/4634) ([paulbalandan](https://github.com/paulbalandan)) +- Expand Time for interface [\#4633](https://github.com/codeigniter4/CodeIgniter4/pull/4633) ([MGatner](https://github.com/MGatner)) +- Patch Log code [\#4631](https://github.com/codeigniter4/CodeIgniter4/pull/4631) ([MGatner](https://github.com/MGatner)) +- Cache Returns Types [\#4630](https://github.com/codeigniter4/CodeIgniter4/pull/4630) ([MGatner](https://github.com/MGatner)) +- Cache getMetadata\(\) Format [\#4629](https://github.com/codeigniter4/CodeIgniter4/pull/4629) ([MGatner](https://github.com/MGatner)) +- fix\(cache\): add check for redis empty results in deleteMatching [\#4628](https://github.com/codeigniter4/CodeIgniter4/pull/4628) ([yassinedoghri](https://github.com/yassinedoghri)) +- Update rector/rector requirement from 0.10.12 to 0.10.15 [\#4627](https://github.com/codeigniter4/CodeIgniter4/pull/4627) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Add fallback for Config\Cookie [\#4625](https://github.com/codeigniter4/CodeIgniter4/pull/4625) ([paulbalandan](https://github.com/paulbalandan)) +- \[Test\] Use @codeCoverageIgnore for deprecated class/method [\#4623](https://github.com/codeigniter4/CodeIgniter4/pull/4623) ([samsonasik](https://github.com/samsonasik)) +- Fix: Nested sections rendering [\#4622](https://github.com/codeigniter4/CodeIgniter4/pull/4622) ([iRedds](https://github.com/iRedds)) +- Update rector/rector requirement from 0.10.11 to 0.10.12 [\#4621](https://github.com/codeigniter4/CodeIgniter4/pull/4621) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Update phpstan/phpstan requirement from 0.12.84 to 0.12.85 [\#4620](https://github.com/codeigniter4/CodeIgniter4/pull/4620) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Bump actions/github-script from v4.0.1 to v4.0.2 [\#4614](https://github.com/codeigniter4/CodeIgniter4/pull/4614) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Update rector/rector requirement from 0.10.9 to 0.10.11 [\#4613](https://github.com/codeigniter4/CodeIgniter4/pull/4613) ([dependabot[bot]](https://github.com/apps/dependabot)) +- \[Rector\] Refactor UnderscoreToCamelCaseVariableNameRector with latest Rector compatible code [\#4612](https://github.com/codeigniter4/CodeIgniter4/pull/4612) ([samsonasik](https://github.com/samsonasik)) +- \[Feat\]\[Autoloader\] Allow autoloading non-class files [\#4611](https://github.com/codeigniter4/CodeIgniter4/pull/4611) ([paulbalandan](https://github.com/paulbalandan)) +- failValidationError can take an array of errors [\#4609](https://github.com/codeigniter4/CodeIgniter4/pull/4609) ([caswell-wc](https://github.com/caswell-wc)) +- assertJsonFragment fails gracefully with invalid json [\#4608](https://github.com/codeigniter4/CodeIgniter4/pull/4608) ([caswell-wc](https://github.com/caswell-wc)) +- Non-persistent fake [\#4607](https://github.com/codeigniter4/CodeIgniter4/pull/4607) ([caswell-wc](https://github.com/caswell-wc)) +- Fix validation of array data [\#4606](https://github.com/codeigniter4/CodeIgniter4/pull/4606) ([paulbalandan](https://github.com/paulbalandan)) +- Use realpath\(\) to fix app prioritization of validation messages [\#4605](https://github.com/codeigniter4/CodeIgniter4/pull/4605) ([paulbalandan](https://github.com/paulbalandan)) +- Optimizations for Autoloader [\#4604](https://github.com/codeigniter4/CodeIgniter4/pull/4604) ([paulbalandan](https://github.com/paulbalandan)) +- format style [\#4603](https://github.com/codeigniter4/CodeIgniter4/pull/4603) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- whitespaces [\#4602](https://github.com/codeigniter4/CodeIgniter4/pull/4602) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- \[Rector\] Apply Full PHP 7.3 Rector Set List \(Skip JsonThrowOnErrorRector & StringifyStrNeedlesRector\) [\#4601](https://github.com/codeigniter4/CodeIgniter4/pull/4601) ([samsonasik](https://github.com/samsonasik)) +- Bump actions/github-script from v3 to v4.0.1 [\#4599](https://github.com/codeigniter4/CodeIgniter4/pull/4599) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Simplify Cookie Class [\#4596](https://github.com/codeigniter4/CodeIgniter4/pull/4596) ([mostafakhudair](https://github.com/mostafakhudair)) +- Fix service methods …$params type [\#4594](https://github.com/codeigniter4/CodeIgniter4/pull/4594) ([najdanovicivan](https://github.com/najdanovicivan)) +- Fix new service replacement service provider precedence on core factory implementations [\#4593](https://github.com/codeigniter4/CodeIgniter4/pull/4593) ([element-code](https://github.com/element-code)) +- Update rector/rector requirement from 0.10.6 to 0.10.9 [\#4592](https://github.com/codeigniter4/CodeIgniter4/pull/4592) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Debug/Toolbar - Memory issue fix [\#4590](https://github.com/codeigniter4/CodeIgniter4/pull/4590) ([najdanovicivan](https://github.com/najdanovicivan)) +- BaseModel - Add public getIdValue\(\) method [\#4589](https://github.com/codeigniter4/CodeIgniter4/pull/4589) ([najdanovicivan](https://github.com/najdanovicivan)) +- Feature: Escaping array dot notation [\#4588](https://github.com/codeigniter4/CodeIgniter4/pull/4588) ([iRedds](https://github.com/iRedds)) +- Update phpstan/phpstan requirement from 0.12.83 to 0.12.84 [\#4587](https://github.com/codeigniter4/CodeIgniter4/pull/4587) ([dependabot[bot]](https://github.com/apps/dependabot)) +- extra \ in tabs area [\#4586](https://github.com/codeigniter4/CodeIgniter4/pull/4586) ([jbrahy](https://github.com/jbrahy)) +- user\_guide update: remove whitespaces, convert tabs to spaces & adjust the over-extended lines [\#4585](https://github.com/codeigniter4/CodeIgniter4/pull/4585) ([totoprayogo1916](https://github.com/totoprayogo1916)) +- Fix: BaseModel. Removed duplicate code. [\#4581](https://github.com/codeigniter4/CodeIgniter4/pull/4581) ([iRedds](https://github.com/iRedds)) +- Change Entity Namespace [\#4577](https://github.com/codeigniter4/CodeIgniter4/pull/4577) ([mostafakhudair](https://github.com/mostafakhudair)) +- \[Rector\] Remove RemoveDefaultArgumentValueRector [\#4576](https://github.com/codeigniter4/CodeIgniter4/pull/4576) ([samsonasik](https://github.com/samsonasik)) +- \[Rector\] Add Comment for reason RemoveDefaultArgumentValueRector copied to utils/Rector [\#4575](https://github.com/codeigniter4/CodeIgniter4/pull/4575) ([samsonasik](https://github.com/samsonasik)) +- \[Rector\] Copy RemoveDefaultArgumentValueRector to utils/Rector [\#4574](https://github.com/codeigniter4/CodeIgniter4/pull/4574) ([samsonasik](https://github.com/samsonasik)) +- Fix: Validation::withRequest\(\) with Content-Type: multipart/form-data [\#4571](https://github.com/codeigniter4/CodeIgniter4/pull/4571) ([iRedds](https://github.com/iRedds)) +- \[Rector\] Update Rector 0.10.6, re-enable auto imports [\#4569](https://github.com/codeigniter4/CodeIgniter4/pull/4569) ([samsonasik](https://github.com/samsonasik)) +- feat\(cache\): add deleteMatching method to remove multiple cache items [\#4567](https://github.com/codeigniter4/CodeIgniter4/pull/4567) ([yassinedoghri](https://github.com/yassinedoghri)) +- Mysql connection issue with MYSQLI\_CLIENT\_SSL\_DONT\_VERIFY\_SERVER\_CERT [\#4566](https://github.com/codeigniter4/CodeIgniter4/pull/4566) ([fedeburo](https://github.com/fedeburo)) +- Bump actions/cache from v2.1.4 to v2.1.5 [\#4564](https://github.com/codeigniter4/CodeIgniter4/pull/4564) ([dependabot[bot]](https://github.com/apps/dependabot)) +- \[Rector\] Apply Rector: UnnecessaryTernaryExpressionRector [\#4563](https://github.com/codeigniter4/CodeIgniter4/pull/4563) ([samsonasik](https://github.com/samsonasik)) +- Check intl extension loaded on check min PHP version [\#4562](https://github.com/codeigniter4/CodeIgniter4/pull/4562) ([samsonasik](https://github.com/samsonasik)) +- Prefix calls to getenv\(\) during config resolution [\#4561](https://github.com/codeigniter4/CodeIgniter4/pull/4561) ([paulbalandan](https://github.com/paulbalandan)) +- \[Rector\] Apply Rector: ChangeArrayPushToArrayAssignRector [\#4560](https://github.com/codeigniter4/CodeIgniter4/pull/4560) ([samsonasik](https://github.com/samsonasik)) +- \[Rector\] Enable check tests/system/Models [\#4557](https://github.com/codeigniter4/CodeIgniter4/pull/4557) ([samsonasik](https://github.com/samsonasik)) +- Debugging SQL Server in Actions [\#4554](https://github.com/codeigniter4/CodeIgniter4/pull/4554) ([paulbalandan](https://github.com/paulbalandan)) +- Support for masking sensitive debug data [\#4550](https://github.com/codeigniter4/CodeIgniter4/pull/4550) ([pixobit](https://github.com/pixobit)) +- Use message directly if intl is not available [\#4549](https://github.com/codeigniter4/CodeIgniter4/pull/4549) ([paulbalandan](https://github.com/paulbalandan)) +- Add compatibility for strict types [\#4548](https://github.com/codeigniter4/CodeIgniter4/pull/4548) ([paulbalandan](https://github.com/paulbalandan)) +- Removes deprecated settings in env file [\#4547](https://github.com/codeigniter4/CodeIgniter4/pull/4547) ([paulbalandan](https://github.com/paulbalandan)) +- Fix wrong argument passed in doc [\#4546](https://github.com/codeigniter4/CodeIgniter4/pull/4546) ([paulbalandan](https://github.com/paulbalandan)) +- Fix dot notation for if\_exist [\#4545](https://github.com/codeigniter4/CodeIgniter4/pull/4545) ([paulbalandan](https://github.com/paulbalandan)) +- Relocate cookie exception [\#4544](https://github.com/codeigniter4/CodeIgniter4/pull/4544) ([mostafakhudair](https://github.com/mostafakhudair)) +- \[Rector\] Apply RemoveDefaultArgumentValueRector [\#4543](https://github.com/codeigniter4/CodeIgniter4/pull/4543) ([samsonasik](https://github.com/samsonasik)) +- Fix html formatting for exceptions and errors [\#4542](https://github.com/codeigniter4/CodeIgniter4/pull/4542) ([musmanikram](https://github.com/musmanikram)) +- Create Config::Cookie Class [\#4508](https://github.com/codeigniter4/CodeIgniter4/pull/4508) ([mostafakhudair](https://github.com/mostafakhudair)) + +## [v4.1.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.0) (2021-02-01) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.0...v4.1.1) + +**Fixed bugs:** + +- Fixed an issue where **.gitattributes** was preventing framework downloads + +## [v4.1.0](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.0) (2021-01-31) + +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.0.5...v4.1.0) + +**Implemented enhancements:** + +- Rector 0.9 fixes [\#4196](https://github.com/codeigniter4/CodeIgniter4/issues/#4196) +- Cannot declare class Config\App error on running PHPUnit [\#4114](https://github.com/codeigniter4/CodeIgniter4/issues/4114) +- Backfill non-optional parameters (https://github.com/codeigniter4/CodeIgniter4/pull/3938) +- Change deprecated assertFileNotExists (https://github.com/codeigniter4/CodeIgniter4/pull/3862) + +For the changelog of v4.0, see [CHANGELOG_4.0.md](./CHANGELOG_4.0.md). diff --git a/changelogs/CHANGELOG_4.2.md b/changelogs/CHANGELOG_4.2.md new file mode 100644 index 000000000000..f0b230bc09c6 --- /dev/null +++ b/changelogs/CHANGELOG_4.2.md @@ -0,0 +1,423 @@ +# Changelog 4.2 + +## [v4.2.12](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.12) (2023-01-09) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.11...v4.2.12) + +### Fixed Bugs +* docs: fix request.rst by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7014 +* fix: `link_tag()` missing `type="application/rss+xml"` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7022 +* fix: Request::getIPaddress() causes error on CLI by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7030 +* docs: fix upgrade_database.rst by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7036 +* fix: `spark migrate:status` shows incorrect filename when format is `Y_m_d_His_` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7038 +* fix: Model::save() object when useAutoIncrement is disabled by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/7042 +* fix: define of STDOUT in CLI init() method by @jozefrebjak in https://github.com/codeigniter4/CodeIgniter4/pull/7052 +* fix: change `getFile()` function of \CodeIgniter\Events\Events to static. by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/7046 +* fix: [Email] add fallback to use gethostname() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7053 +* Fixing bug with legacy autoRoute when testing by @baycik in https://github.com/codeigniter4/CodeIgniter4/pull/7060 + +### Refactoring +* refactor: RequestTrait by rector by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7006 +* refactor: update sass output by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7026 + +## [v4.2.11](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.11) (2022-12-21) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.10...v4.2.11) + +### SECURITY +* *Attackers may spoof IP address when using proxy* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-ghw3-5qvm-3mqc) for more information. +* *Potential Session Handlers Vulnerability* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-6cq5-8cj7-g558) for more information. + +### Fixed Bugs +* fix: Request::getIPAddress() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6820 +* fix: Model cannot insert when $useAutoIncrement is false by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6827 +* fix: View Parser regexp does not support UTF-8 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6835 +* Handle key generation when key is not present in .env by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6839 +* Fix: Controller Test withBody() by @MGatner in https://github.com/codeigniter4/CodeIgniter4/pull/6843 +* fix: body assigned via options array in CURLRequest class by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/6854 +* Fix CreateDatabase leaving altered database config in connection by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6856 +* fix: cast to string all values except arrays in Header class by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/6862 +* add missing @method Query grouping in Model by @paul45 in https://github.com/codeigniter4/CodeIgniter4/pull/6874 +* fix: `composer update` might cause error "Failed to open directory" by @LeMyst in https://github.com/codeigniter4/CodeIgniter4/pull/6833 +* fix: required PHP extentions by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6897 +* fix: Use Services for the FeatureTestTrait request. by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/6966 +* fix: FileLocator::locateFile() bug with a similar namespace name by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/6964 +* fix: socket connection in RedisHandler class by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/6972 +* fix: `spark namespaces` cannot show a namespace with mutilple paths by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6977 +* fix: Undefined constant "CodeIgniter\Debug\VENDORPATH" by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6985 +* fix: large HTTP input crashes framework by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6984 +* fix: empty paths for `rewrite.php` by @datamweb in https://github.com/codeigniter4/CodeIgniter4/pull/6991 +* fix: `PHPStan` $cols not defined in `CLI` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/6994 +* Fix MigrationRunnerTest for Windows by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6855 +* fix: turn off `Xdebug` note when running phpstan by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/6851 +* Fix ShowTableInfoTest to pass on Windows by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6853 +* Fix MigrateStatusTest for Windows by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6866 +* Fix ShowTableInfoTest when migration records are numerous by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6868 +* Fix CreateDatabaseTest to not leave database by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6867 +* Fix coverage merge warning by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6885 +* fix: replace tabs to spaces by @zl59503020 in https://github.com/codeigniter4/CodeIgniter4/pull/6898 +* fix: slack links by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6907 +* Fix typo in database/queries.rst by @philFernandez in https://github.com/codeigniter4/CodeIgniter4/pull/6920 +* Fix testInsertWithSetAndEscape to make not time dependent by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6974 +* fix: remove unnecessary global variables in rewrite.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6973 + +## [v4.2.10](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.10) (2022-11-05) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.9...v4.2.10) + +### Fixed Bugs +* docs: fix PHPDoc types in Session by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6796 +* fix: output "0" at the end of toolbar js when Kint::$enabled_mode is false by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6809 + +### Refactoring +* Refactor assertHeaderEmitted and assertHeaderNotEmitted by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6806 +* fix: variable types for PHPStan 1.9.0 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6810 + +## [v4.2.9](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.9) (2022-10-30) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.8...v4.2.9) + +**Hotfix release to fix PHPUnit errors (see https://github.com/codeigniter4/CodeIgniter4/pull/6794)** + +## [v4.2.8](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.8) (2022-10-30) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.7...v4.2.8) + +### Fixed Bugs +* Fix DotEnv class turning `export` to empty string by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6625 +* Remove unneeded `$logger` property in `Session` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6647 +* fix: Add missing CLIRequest::getCookie() by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6646 +* fix: routes registration bug by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6644 +* Bug: showError in CLI/BaseCommand use hardcoded error view path by @fpoy in https://github.com/codeigniter4/CodeIgniter4/pull/6657 +* fix: getGetPost() and getPostGet() when index is null by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/6675 +* fix: add missing methods to BaseConnection by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6712 +* fix: bug that esc() accepts invalid context '0' by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6722 +* fix: [Postgres] reset binds when replace() method is called multiple times in the context by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/6728 +* fix: [SQLSRV] _getResult() return object for preparedQuery class by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/6718 +* Fix error handler callback by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6724 +* bug: Supply mixin for TestResponse by @MGatner in https://github.com/codeigniter4/CodeIgniter4/pull/6756 +* fix: CodeIgniter::run() doesn't respect $returnResponse by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6737 +* Bug: ResponseTest::testSetLastModifiedWithDateTimeObject depends on time by @fpoy in https://github.com/codeigniter4/CodeIgniter4/pull/6683 +* fix: workaround for Faker deprecation errors in PHP 8.2 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6758 +* Add .gitattributes to framework by @totoprayogo1916 in https://github.com/codeigniter4/CodeIgniter4/pull/6774 +* Delete admin/module directory by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6775 + +## [v4.2.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.7) (2022-10-06) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.6...v4.2.7) + +### SECURITY +* *Secure or HttpOnly flag set in Config\Cookie is not reflected in Cookies issued* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-745p-r637-7vvp) for more information. + +### Breaking Changes +* fix: make Time::__toString() database-compatible on any locale by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6461 +* fix: set_cookie() does not use Config\Cookie values by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6544 +* fix: `required_without` rule logic in `Validation` class. by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/6589 + +### Fixed Bugs +* fix: typos in messages in Language/en/Email.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6517 +* fix: table attribute cannot applied on td element by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/6538 +* add: set up "script_name" to handle every request by index.php file. by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/6522 +* fix: CSP autoNonce = false by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6570 +* fix: inconsistent new line view in `date_helper` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/6582 +* fix: safe_mailto() does not work with CSP by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6604 +* fix: script_tag() does not work with CSP by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6611 +* fix: `$cleanValidationRules` does not work in Model updates by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6588 +* Fixed a bug that URLs with trailing newlines do not become invalid in validation. by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/6618 +* fix: missing `valid_json` in Validation Language by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/6624 +* fix: default values for Session Redis Handler by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6614 + +### Enhancements +* Update coding-standards version by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6537 +* chore: update ThirdParty Kint to 4.2.2 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6583 + +### Refactoring +* Refactor: CodeIgniter::generateCacheName() by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6498 +* refactor: replace `global $app` with Services by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/6524 +* refactor: small refactoring in view() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6546 +* refactor: replace utf8_encode() with mb_convert_encoding() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6564 +* refactor: make $precision int in View Filter round by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6566 + +## [v4.2.6](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.6) (2022-09-04) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.5...v4.2.6) + +### Fixed Bugs +* fix: AssertionError occurs when using Validation in CLI by @daycry in https://github.com/codeigniter4/CodeIgniter4/pull/6452 +* fix: [Validation] JSON data may cause "Array to string conversion" error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6467 +* Fix fatal error gets turned to `0` severity on shutdown handler by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6472 +* Fix redis cache increment/decrement methods by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6473 +* Fix broken caching system when array of allowed parameters used by @JavaDeveloperKiev in https://github.com/codeigniter4/CodeIgniter4/pull/6475 +* fix: Strict Validation Rules greater_than/less_than by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6492 + +### Refactoring +* refactor: fix PHPStan errors by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6470 +* Bump `friendsofphp/php-cs-fixer` to `~3.11.0` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6471 +* Fix overlooked coding style violations by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6491 + +## [v4.2.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.5) (2022-08-28) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.4...v4.2.5) + +### Breaking Changes +* Add $cached param to BaseConnection::tableExists() by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6364 +* Fix validation custom error asterisk field by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/6378 + +### Fixed Bugs +* fix: Email class may not log an error when it fails to send by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6362 +* fix: Response::download() causes TypeError by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6361 +* fix: command usages by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6402 +* Fix: The subquery adds a prefix for the table alias. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6390 +* Fix Sqlite Table::createTable() by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6396 +* docs: add missing `@method` `groupBy()` in Model by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6433 +* fix: CLIRequest Erros in CLI by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6421 +* fix: Call to undefined method CodeIgniter\HTTP\CLIRequest::getLocale() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6442 + +### Enhancements +* chore: update Kint to 4.2.0 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6436 + +### Refactoring +* refactor: add test for DownloadResponse by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6375 +* refactor: ValidationTest by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6382 +* refactor: remove unused `_parent_name` in BaseBuilder::objectToArray() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6427 +* Remove unneeded abstract `handle()` method by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6434 + +## [v4.2.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.4) (2022-08-13) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.3...v4.2.4) + +**Hotfix release to fix download errors (see https://github.com/codeigniter4/CodeIgniter4/pull/6361)** + +## [v4.2.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.3) (2022-08-06) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.2...v4.2.3) + +* SECURITY: Improve CSRF protection (for Shield CSRF security fix) + +## [v4.2.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.2) (2022-08-05) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.1...v4.2.2) + +### Breaking Changes +* fix: when running on CLI, two Request objects were used in the system by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6089 +* fix: Builder insert()/update() does not accept an object by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6216 +* fix: create table if not exists when indexes already exist by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6249 +* fix: page cache saves Response data before running after filters by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6282 +* fix: random_string('crypto') may return string less than $len or ErrorException by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6334 + +### Fixed Bugs +* Fixed: BaseBuilder increment/decrement do not reset state after a query by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6146 +* fix: SQLite3\Connection\getIndexData() error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6152 +* fix: `is_image` causes PHP 8.1 deprecated error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6157 +* fix: prepared query is executed when using QueryBuilder by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6164 +* fix: Time::getAge() calculation by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6159 +* fix: Session cookies are sent twice with Ajax by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6167 +* fix: QueryBuilder breaks select when escape is false by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5118 +* fix: PHPDoc return type in ControllerTestTrait methods by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6168 +* fix: `$routes->group('/', ...)` creates the route `foo///bar` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6186 +* fix: use lang('HTTP.pageNotFound') on production 404 page by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6202 +* fix: BaseConnection may create dynamic property by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6198 +* fix: Email SMTP may throw Uncaught ErrorException by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6184 +* fix: CSP reportOnly behavior by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6201 +* fix: lang() causes Error on CLI by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6209 +* fix: multiple pagers with models do not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6211 +* fix: tweak empty line output of `spark db:table` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6215 +* fix: custom validation error is cleared when calling setRule() twice by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6241 +* Fix: Validation of fields with a leading asterisk. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6243 +* fix: Call to undefined method CodeIgniter\Pager\PagerRenderer::getDetails() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6251 +* fix: exceptionHandler may cause HTTPException: Unknown HTTP status code by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6254 +* fix: invalid INSERT/DELETE query when Query Builder uses table alias by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5376 +* fix: Add db port entry into env file. by @nalakapws in https://github.com/codeigniter4/CodeIgniter4/pull/6250 +* fix: update `.gitattributes` by @totoprayogo1916 in https://github.com/codeigniter4/CodeIgniter4/pull/6256 +* fix: format_number() can't be used on CLI by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6263 +* fix: add parameter checking for max_size by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6261 +* fix: route name is not displayed in Exception message by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6269 +* fix: `spark routes` shows 404 error when using regex by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6279 +* fix: Entity::hasChanged() returns wrong result to mapped property by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6285 +* fix: unable to add more than one file to FileCollection constructor by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6291 +* fix: Security::derandomize() may cause hex2bin() error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6292 +* fix: use getenv() instead of $_SERVER in detectEnvironment() by @fcosrno in https://github.com/codeigniter4/CodeIgniter4/pull/6257 +* fix: OCI8 uses deprecated Entity by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6323 +* fix: Parse error occurs before PHP version check by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6327 +* fix: 404 page might display Exception message in production environment by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6333 + +### Refactoring +* refactor: replace $e->getMessage() with $e in log_message() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6182 +* refactor: add CompleteDynamicPropertiesRector by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6187 +* refactor: debug toolbar by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6272 +* refactor: Exception exit code by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6286 +* chore: Remove Vagrant by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6314 +* refactor: CSRF protection by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6320 + +## [v4.2.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.1) (2022-06-16) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.0...v4.2.1) + +### Breaking Changes +* Fix MIME guessing of extension from type by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6059 +* fix: get_cookie() may not use the cookie prefix by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6082 + +### Fixed Bugs +* fix: get_cookie() does not take Config\Cookie::$prefix by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6080 +* fix: session cookie name bug by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6091 +* fix: Session Handlers do not take Config\Cookie by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6081 +* fix: reverse routing does not work with full classname starting with `\` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6104 +* fix: insert error message in QueryBuilder by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6108 +* fix: `spark routes` shows "ERROR: 404" by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6098 +* fix: Time::setTestNow() does not work with fa Locale by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6116 +* fix: `migrate --all` causes `Class "SQLite3" not found` error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6117 +* fix: event DBQuery is not fired on failed query when DBDebug is true by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6127 +* fix: `Time::humanize()` causes error with ar locale by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6120 +* Fix decorators by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/6090 +* Fix lost error message by test when after testInsertResultFail. by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/6113 +* test: fix forgetting to restore DBDebug value by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6115 + +### Refactoring +* Apply AutoRouterImproved::translateURIDashes() by @pjsde in https://github.com/codeigniter4/CodeIgniter4/pull/6084 +* Remove useless catch by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6095 +* Move preload.php example to starter app by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/6088 +* style: compile sass by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6099 + +## [v4.2.0](https://github.com/codeigniter4/CodeIgniter4/tree/v4.2.0) (2022-06-03) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.9...v4.2.0) + +### Breaking Changes +* Validation: support placeholders for anything by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5545 +* Fix: Validation. Error key for field with asterisk by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5609 +* Improve exception logging by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5684 +* fix: spark can't use options on PHP 7.4 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5836 +* fix: [Autoloader] Composer classmap usage by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5850 +* fix: using multiple CLI::color() in CLI::write() outputs strings with wrong color by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5893 +* refactor: [Router] extract a class for auto-routing by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5877 +* feat: Debugbar request microtime by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5958 +* refactor: `system/bootstrap.php` only loads files and registers autoloader by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5972 +* fix: `dot_array_search()` unexpected behavior by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5940 +* feat: QueryBuilder join() raw SQL string support by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5875 +* fix: change BaseService::reset() $initAutoloader to true by default by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6020 + +### Fixed Bugs +* chore: update admin/framework/composer.json Kint by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5531 +* fix: BaseConnection::getConnectDuration() number_format(): Passing null to parameter by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5536 +* Fix: Debug toolbar selectors by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5544 +* Fix: Toolbar. ciDebugBar.showTab() context. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5554 +* Refactor Database Collector display by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5553 +* fix: add missing Migration lang item by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5557 +* feat: add Validation Strict Rules by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5445 +* fix: `Time::createFromTimestamp()` sets incorrect time when specifying timezone by @totoprayogo1916 in https://github.com/codeigniter4/CodeIgniter4/pull/5588 +* fix: Entity's isset() and unset() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5497 +* Fix: Deletion timestamp of the Model is updated when a record that has been soft-deleted is deleted again by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5578 +* Fix: Added alias escaping in subquery by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5601 +* fix: spark migrate:status does not show status with different namespaces by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5605 +* BaseService - Use lowercase key in resetSingle by @najdanovicivan in https://github.com/codeigniter4/CodeIgniter4/pull/5596 +* Fix `array_flatten_with_dots` ignores empty array values by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5606 +* fix: debug toolbar Routes Params output by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5619 +* fix: DownloadResponse memory leak by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5623 +* fix: spark does not show Exception by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5638 +* fix: Config CSRF $redirect does not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5665 +* fix: do not call header() if headers have already been sent by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5680 +* fix: $routes->setDefaultMethod() does not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5682 +* fix: debug toolbar vars response headers includes request headers by @zl59503020 in https://github.com/codeigniter4/CodeIgniter4/pull/5701 +* fix: 404 override controller does not output Response object body by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5703 +* fix: auto routes incorrectly display route filters with GET method by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5712 +* fix: Model::paginate() missing argument $group by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5699 +* Fix options are not passed to Command $params by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5206 +* fix: forceGlobalSecureRequests break URI schemes other than HTTP by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5730 +* fix: TypeError when `$tokenRandomize = true` and no token posted by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5742 +* fix: $builder->ignore()->insertBatch() only ignores on first iteration by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5672 +* fix: app/Config/Routes.php is loaded twice on Windows by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5780 +* fix: table name is double prefixed when LIKE clause by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5778 +* fix: Publisher $restrictions regex to FCPATH by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5793 +* fix: Timer::getElapsedTime() returns incorrect value by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5798 +* bug: Publisher $restrictions regex typo by @MGatner in https://github.com/codeigniter4/CodeIgniter4/pull/5800 +* fix: [Validation] valid_date ErrorException when the field is not sent by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5804 +* fix: [Pager] can't get correct current page from segment by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5803 +* fix: bug that allows dynamic controllers to be used by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5814 +* config: remove App\ and Config\ in autoload.psr-4 in app starter composer.json by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5824 +* fix: failover's DBPrefix not working by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5816 +* fix: Validation returns incorrect errors after Redirect with Input by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5844 +* feat: [Parser] add configs to change conditional delimiters by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5842 +* fix: Commands::discoverCommands() loads incorrect classname by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5849 +* fix: Publisher::discover() loads incorrect classname by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5858 +* fix: validation errors in Model are not cleared when running validation again by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5861 +* fix: Parser fails with `({variable})` in loop by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5840 +* fix: [BaseConfig] string value is set from environment variable even if it should be int/float by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5779 +* fix: add Escaper Exception classes in $coreClassmap by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5891 +* fix: Composer PSR-4 overwrites Config\Autoload::$psr4 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5902 +* fix: Reverse Routing does not take into account the default namespace by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5936 +* fix: [Validation] Fields with an asterisk throws exception by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5938 +* fix: GDHandler::convert() does not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5969 +* fix: Images\Handlers\GDHandler Implicit conversion from float to int loses precision by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5965 +* fix: GDHandler::save() removes transparency by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5971 +* fix: route limit to subdomains does not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5961 +* fix: Model::_call() static analysis by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5970 +* fix: invalid css in error_404.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5978 +* Fix: Route placeholder (:any) with {locale} by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6003 +* Changing the subquery builder for the Oracle by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5999 +* fix: CURLRequest request body is not reset on the next request by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6014 +* Bug: The SQLSRV driver ignores the port value from the config. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6036 +* fix: `set_radio()` not working as expected by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6037 +* fix: add config for SQLite3 Foreign Keys by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6050 +* fix: Ignore non-HTML responses in storePreviousURL by @tearoom6 in https://github.com/codeigniter4/CodeIgniter4/pull/6012 +* fix: SQLite3\Table::copyData() does not escape column names by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6055 +* Fix `slash_item()` erroring when property fetched does not exist on `Config\App` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/6058 + +### New Features +* Feature Add Oracle driver by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/2487 +* feat: new improved auto router by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5889 +* feat: new improved auto router `spark routes` command by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5953 +* feat: `db:table` command by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5979 + +### Enhancements +* feat: CSP enhancements by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5516 +* Feature: Subqueries in the FROM section by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5510 +* Added new View Decorators. by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/5567 +* feat: auto routes listing by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5590 +* Feature: "spark routes" command shows routes with closure. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5651 +* feat: `spark routes` shows filters by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5628 +* Allow calling getQuery() multiple times, and other improvements by @vlakoff in https://github.com/codeigniter4/CodeIgniter4/pull/5127 +* feat: add Controller::validateData() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5639 +* feat: can add route handler as callable by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5713 +* Checking if the subquery uses the same object as the main query by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5743 +* Feature: Subquery for SELECT by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5736 +* Extend Validation from BaseConfig so Registrars can add rules. by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/5789 +* config: add mime type for webp by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5838 +* feat: add `$includeDir` option to `get_filenames()` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5862 +* feat: throws exception when controller name in routes contains `/` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5885 +* [PHPStan] Prepare for PHPStan 1.6.x-dev by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/5876 +* [Rector] Add back SimplifyUselessVariableRector by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/5911 +* Redirecting Routes. Placeholders. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5916 +* script_tag(): cosmetic for value-less attributes by @xlii-chl in https://github.com/codeigniter4/CodeIgniter4/pull/5884 +* feat: QueryBuilder raw SQL string support by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5817 +* improve Router Exception message by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5984 +* feat: DBForge::addField() `default` value raw SQL string support by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5957 +* Add sample file for preloading by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5974 +* Feature. QueryBuilder. Query union. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6015 +* feat: `getFieldData()` returns nullable data on PostgreSQL by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5981 + +### Refactoring +* refactor: add Factories::models() to suppress PHPStan error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5358 +* Fixed style for PHP7.4 by @ytetsuro in https://github.com/codeigniter4/CodeIgniter4/pull/5581 +* Fix Autoloader::initialize() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5592 +* refactor: CURLRequest and the slow tests by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5593 +* Refactor `if_exist` validation with dot notation by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5607 +* refactor: small changes in Filters and Router by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5627 +* refactor: replace deprecated `getFilterForRoute()` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5624 +* refactor: make BaseController abstract by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5647 +* refactor: move logic to prevent access to initController by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5648 +* refactor: remove migrations routes by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5652 +* refactor: update Kint CSP nonce by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5657 +* Deprecate object implementations of `clean_path()` function by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5681 +* refactor: Session does not use cookies() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5656 +* refactor: replace deprecated Response::getReason() with getReasonPhrase() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5700 +* refactor: isCLI() in CLIRequest and IncomingRequest by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5653 +* refactor: CodeIgniter has context by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5650 +* Forge use statement by @mostafakhudair in https://github.com/codeigniter4/CodeIgniter4/pull/5729 +* refactor: remove `&` before $db by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5726 +* refactor: remove unneeded `&` references in ContentSecurityPolicy.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5734 +* Nonce replacement optimization. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5733 +* [Rector] Clean up skip config and re-run Rector by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/5813 +* refactor: DB Session Handler by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5696 +* Rename `Abstact` to `Abstract` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5833 +* refactor: extract RedirectResponse::withErrors() method by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5860 +* Optimizing the RouteCollection::getRoutes() method by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5918 +* refactor: add strtolower() to Request::getMethod() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5963 +* refactor: remove `$_SERVER['HTTP_HOST']` in RouteCollection by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5962 +* refactor: deprecate const `EVENT_PRIORITY_*` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6000 +* fix: replace EVENT_PRIORITY_NORMAL with Events::PRIORITY_NORMAL by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6005 +* Router class optimization. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/6004 +* Prefer `is_file()` by @MGatner in https://github.com/codeigniter4/CodeIgniter4/pull/6025 +* refactor: use get_filenames() 4th param in FileLocator by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6026 +* refactor: use get_filenames() 4th param by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6031 +* refactor: CodeIgniter $context check by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6047 +* Small change to improve code reading by @valmorflores in https://github.com/codeigniter4/CodeIgniter4/pull/6051 +* refactor: remove `CodeIgniter\Services` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6053 + +For the changelog of v4.1, see [CHANGELOG_4.1.md](./CHANGELOG_4.1.md). diff --git a/changelogs/CHANGELOG_4.3.md b/changelogs/CHANGELOG_4.3.md new file mode 100644 index 000000000000..9c1345dbbd97 --- /dev/null +++ b/changelogs/CHANGELOG_4.3.md @@ -0,0 +1,326 @@ +# Changelog 4.3 + +## [v4.3.8](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.8) (2023-08-25) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.7...v4.3.8) + +### Fixed Bugs + +* fix: [Pager] knocks down variables for View by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7758 +* fix: Model::insertBatch() causes error to non auto increment table by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7759 +* fix: [Model] updateBatch() may generate invalid SQL statement by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7787 +* fix: Model inserts cast $primaryKey value when using Entity by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7806 +* fix: instances of Validation rules are incremented each time `run()` is executed by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7815 +* fix: filter except empty by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7823 +* fix: `set_checkbox()` checks unchecked checkbox by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7818 + +### Refactoring + +* Normalize data provider names by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/7656 +* refactor: remove Model::$tempPrimaryKeyValue by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7760 +* Remove unused cast on RedisHandler by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/7786 + +## [v4.3.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.7) (2023-07-30) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.6...v4.3.7) + +### Breaking Changes + +* fix: FeatureTestTrait may change $params values passed to call(), and a few bug fixes by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7602 +* fix: auto routing legacy and $route->add() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7690 +* fix: [Model] setValidationRule() cannot use with ruleGroup by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7691 + +### Fixed Bugs + +* docs: fix incorrect description on RedirectException (1) by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7629 +* docs: fix incorrect description on RedirectException (2) by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7633 +* fix: skip http proxy added header by @jozefrebjak in https://github.com/codeigniter4/CodeIgniter4/pull/7622 +* fix: number_to_roman() param type by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7640 +* fix: [Auto Routing Improved] feature testing may use incorrect param count by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7597 +* fix: `url_to()` error message by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7652 +* fix: [ViewCells] caching by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7663 +* fix: [ViewCells] when there are cells with the same short name, only the first cell is loaded by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7686 +* Fix number comparison in number_to_amount() in number_helper.php by @sba in https://github.com/codeigniter4/CodeIgniter4/pull/7701 +* fix: wrong Config classname to config() in Toolbar by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7735 +* fix: $sensitiveDataInTrace does not work by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7725 + +### Enhancements + +* Remove PHPStan from pre-commit hook by @lonnieezell in https://github.com/codeigniter4/CodeIgniter4/pull/7618 + +### Refactoring + +* refactor: remove unused property in Encryption\Handlers\BaseHandler by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7579 +* refactor: use ::class to config() param by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7611 +* refactor: remove unused non-empty array in RequestTrait by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7620 +* refactor: [Cache] simplify code of `FileHandler::getItem()` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/7667 +* refactor: replace `config(Paths::class)` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7702 + +## [v4.3.6](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.6) (2023-06-18) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.5...v4.3.6) + +### Breaking Changes + +* fix: [Validation] DBGroup is ignored when checking the value of a placeholder by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7549 +* fix: [Auto Routing Improved] feature testing may not find controller/method by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7543 + +### Fixed Bugs + +* fix: feature test with validation by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7548 +* fix: [Postgre] Semicolon in the connection parameters break the DSN string by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/7552 +* fix: [QueryBuilder] incorrect SQL without space before "ON DUPLICATE KEY UPDATE" by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7564 +* fix: wrong classname in exception message in Cell by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7569 +* fix: `imagecreatefrompng()` gd-png: libpng warning by @ping-yee in https://github.com/codeigniter4/CodeIgniter4/pull/7570 + +### Refactoring + +* refactor: remove unneeded code in IncomingRequest by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7525 +* refactor: View by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7534 +* refactor: [Entity] fix incorrect return value by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7542 +* refactor: Database::initDriver() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7553 +* refactor: remove Factories::models() by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/7566 +* refactor: Validation::processRules() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7565 +* refactor: [Auto Routing Improved] ensure $httpVerb is lower case by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7575 + +## [v4.3.5](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.5) (2023-05-21) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.4...v4.3.5) + +### SECURITY + +* *Remote Code Execution Vulnerability in Validation Placeholders* was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-m6m8-6gq8-c9fj) for more information. +* fix: Session::stop() does not destroy session by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7503 + +### Fixed Bugs + +* docs: remove incorrect @property in ResponseTrait by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7495 +* fix: validation error when a closure is used in combination with permit_empty or if_exist rules by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/7492 +* fix: standardize behavior of `make:cell` and `Cells` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/7481 +* fix: PostgreSQL getVersion() logic by @marekmosna in https://github.com/codeigniter4/CodeIgniter4/pull/7488 +* fix: PostgreSQL getVersion() output by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7509 + +### Enhancements + +* feat: user guide dark mode by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/7463 + +### Refactoring + +* refactor: Entity variable by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7499 + +## [v4.3.4](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.4) (2023-04-27) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.3...v4.3.4) + +### Breaking Changes + +* fix: redirect status code by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7445 +* fix: [SQLite3][Postgres][SQLSRV][OCI8] Forge::modifyColumn() changes NULL constraint incorrectly by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7371 + +### Fixed Bugs + +* fix: view cell cannot locate the auto-generated view file by @sammyskills in https://github.com/codeigniter4/CodeIgniter4/pull/7392 +* fix: CURLRequest - clear response headers between requests by @michalsn in https://github.com/codeigniter4/CodeIgniter4/pull/7398 +* fix: [Auto Routing Improved] spark routes shows invalid routes by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7419 +* fix: remove $insertID in make:model template by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7443 +* fix: add missing 'make:cell' in app/Config/Generators.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7458 + +### Refactoring + +* refactor: Security::getPostedToken() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7377 + +## [v4.3.3](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.3) (2023-03-26) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.2...v4.3.3) + +### Fixed Bugs + +* docs: fix $systemDirectory path in existing project. by @jozefrebjak in https://github.com/codeigniter4/CodeIgniter4/pull/7289 +* docs: fix message.rst and improve content_negotiation.rst by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7280 +* fix: Encryption CI3 compatibility by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7273 +* fix: [QueryBuilder] RawSql causes error when using like() and countAllResults() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7277 +* fix: handling of null bytes in `Exceptions::renderBacktrace()` by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/7306 +* fix: incorrect metadata querying of Redis cache by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/7307 +* fix: [Email] add missing TLS 1.3 support by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7317 +* docs: add warning to random_string() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7333 +* fix: random_string() numeric by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7336 +* docs: add note for addColumn() and NULL by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7342 +* fix: respondNoContent() returns Kint script in development mode by @anggadarkprince in https://github.com/codeigniter4/CodeIgniter4/pull/7347 +* fix: use first exception in exceptionHandler() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7341 +* fix: random_string() alpha alnum nozero by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7344 +* fix: migrate:rollback -b negative number by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7350 +* fix: site_url() does not support protocol-relative links by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7353 +* docs: add uri_string() BC in v4.3.2 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7356 +* fix: Cache FileHandler error when there is a folder in cache dir by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7361 + +### Refactoring + +* refactor: consistent header name case by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7299 + +## [v4.3.2](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.2) (2023-02-18) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.1...v4.3.2) + +### Breaking Changes + +* fix: base_url() removes trailing slash in baseURL by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7200 +* fix: remove parameter $relative in `uri_string()` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7135 + +### Fixed Bugs + +* docs: fix incorrect sample code in view_parser by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7103 +* docs: add missing items in upgrade_430.rst/v4.3.0.rst by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7119 +* fix: remove `All` from `Options All -Indexes` in .htaccess by @sba in https://github.com/codeigniter4/CodeIgniter4/pull/7093 +* fix: bug on stuck content-type header in Feature Testing by @baycik in https://github.com/codeigniter4/CodeIgniter4/pull/7112 +* fix: ordering `Validation` show error by call `setRule()` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/7149 +* fix: [QueryBuilder] where() generates incorrect SQL when using RawSql by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/7147 +* fix: [QueryBuilder] RawSql passed to set() disappears without error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7148 +* fix: [Parser] local_currency causes "Passing null to parameter" by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7157 +* fix: [Parser] `!` does not work if delimiters are changed by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7142 +* fix: Throttler token time calculation by @rumpfc in https://github.com/codeigniter4/CodeIgniter4/pull/7160 +* fix: [QueryBuilder] getOperatorFromWhereKey() misses EXISTS, BETWEEN by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7155 +* docs: Correcting documentation mistakes in upgrading from one version to another by @objecttothis in https://github.com/codeigniter4/CodeIgniter4/pull/7191 +* fix: [Session] `Redis` connect to protocol `TLS` by @ddevsr in https://github.com/codeigniter4/CodeIgniter4/pull/7187 +* fix: Autoloader may not add Composer package's namespaces by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7193 +* fix: add try/catch to real_path() in clean_path() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7195 +* fix: cannot create shared View instance when using debugbar by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7172 +* fix: RouteCollection::getRegisteredControllers() may not return all controllers by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7174 +* fix: `spark routes` shows incorrect hostname routes by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7176 +* docs: add missing composer.json in Mandatory File Changes by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7170 +* fix: stack trace displayed when Exception handler runs out of memory is useless by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7212 +* fix: support for display of error message using wildcard (*) by @sammyskills in https://github.com/codeigniter4/CodeIgniter4/pull/7226 +* fix: routing behavior when $uriProtocol is QUERY_STRING by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7199 +* fix: site_url() does not use alt Config by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7215 +* docs: add missing @method having() in Model by @paul45 in https://github.com/codeigniter4/CodeIgniter4/pull/7258 + +### Enhancements + +* add `application/vnd.microsoft.portable-executable` and `application/x-dosexec` by @totoprayogo1916 in https://github.com/codeigniter4/CodeIgniter4/pull/7144 + +### Refactoring + +* refactor: add PHPDoc types in RouteCollection by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7129 +* refactor: URI::parseStr() by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7154 +* refactor: error_exception.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7171 +* [Rector] Apply Rector to app/Views by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/7169 +* refactor: Update PHPDoc Common::config by @maniaba in https://github.com/codeigniter4/CodeIgniter4/pull/7224 + +## [v4.3.1](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.1) (2023-01-14) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.3.0...v4.3.1) + +### Fixed Bugs + +* fix: Email config in the .env doesn't appear as expected by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7078 +* fix: TypeError in Validation is_unique/is_not_unique by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7085 +* fix: revert method name resetQuery() changed accidentally by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7083 +* fix: handling float in Validation Strcit Rules (greater_than, greater_than_equal_to, less_than, less_than_equal_to) by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7098 +* docs: add missing instruction for Config/Exceptions in PHP 8.2 by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7100 +* fix: Call to undefined method Composer\InstalledVersions::getAllRawData() error by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/7107 + +### Refactoring + +* [Rector] Enable AddDefaultValueForUndefinedVariableRector by @samsonasik in https://github.com/codeigniter4/CodeIgniter4/pull/7088 + +## [v4.3.0](https://github.com/codeigniter4/CodeIgniter4/tree/v4.3.0) (2023-01-10) +[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.2.12...v4.3.0) + +### Breaking Changes + +* fix: throws DatabaseException in DB connections by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6163 +* config: DB Error always throws Exception CI_DBUG by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6183 +* Config Property Types by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6214 +* refactor: loading app/Config/routes.php by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6293 +* fix: exceptionHandler may return invalid HTTP status code by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6228 +* feat: add Form helpers for Validation Errors by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6384 +* fix: ValidationInterface by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6253 +* fix: types in database classes by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6527 +* fix: ResponseInterface (1) by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/6556 +* Improve BaseConnection::getForeignKeyData() and Forge::addForeignKey() by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6468 +* Refactor BaseBuilder *Batch() Methods by @sclubricants in https://github.com/codeigniter4/CodeIgniter4/pull/6536 +* refactor: remove `type="text/javascript"` in ` - - - - - - - - - - Dashboard for {{full_path}} - - - - - - - -
-
-
-
- -
-
-
-
-
-
-
-

Classes

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - -{{insufficient_coverage_classes}} - -
ClassCoverage
-
-
-
-

Project Risks

-
- - - - - - - - -{{project_risks_classes}} - -
ClassCRAP
-
-
-
-
-
-

Methods

-
-
-
-
-

Coverage Distribution

-
- -
-
-
-

Complexity

-
- -
-
-
-
-
-

Insufficient Coverage

-
- - - - - - - - -{{insufficient_coverage_methods}} - -
MethodCoverage
-
-
-
-

Project Risks

-
- - - - - - - - -{{project_risks_methods}} - -
MethodCRAP
-
-
-
- -
- - - - - - - - - - {{name}} - {{classes_bar}} -
{{classes_tested_percent}}
-
{{classes_number}}
- {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{crap}} - {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- - - - {{icon}}{{name}} - {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{classes_bar}} -
{{classes_tested_percent}}
-
{{classes_number}}
- - -
-
- {{percent}}% covered ({{level}}) -
-
- - {{name}} - {{methods_bar}} -
{{methods_tested_percent}}
-
{{methods_number}}
- {{crap}} - {{lines_bar}} -
{{lines_executed_percent}}
-
{{lines_number}}
- - -/** -* @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document);!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:0/0}function r(n){return null===n?0/0:+n}function u(n){return!isNaN(n)}function i(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function l(){this._=Object.create(null)}function s(n){return(n+="")===pa||n[0]===va?va+n:n}function f(n){return(n+="")[0]===va?n.slice(1):n}function h(n){return s(n)in this._}function g(n){return(n=s(n))in this._&&delete this._[n]}function p(){var n=[];for(var t in this._)n.push(f(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 m(){this._=Object.create(null)}function y(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=da.length;r>e;++e){var u=da[e]+t;if(u in n)return u}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function Z(n){return ya(n,Sa),n}function V(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var l=ka.get(n);return l&&(n=l,c=B),a?t?u:r:t?b:i}function $(n,t){return function(e){var r=ta.event;ta.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ta.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-"+ ++Aa,u="click"+r,i=ta.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ea&&(Ea="onselectstart"in e?!1:x(e.style,"userSelect")),Ea){var o=n(e).style,a=o[Ea];o[Ea]="none"}return function(n){if(i.on(r,null),Ea&&(o[Ea]=a),n){var t=function(){i.on(u,null)};i.on(u,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 u=r.createSVGPoint();if(0>Na){var i=t(n);if(i.scrollX||i.scrollY){r=ta.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Na=!(o.f||o.e),r.remove()}}return Na?(u.x=e.pageX,u.y=e.pageY):(u.x=e.clientX,u.y=e.clientY),u=u.matrixTransform(n.getScreenCTM().inverse()),[u.x,u.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ta.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 nt(n){return n>1?0:-1>n?qa:Math.acos(n)}function tt(n){return n>1?Ra:-1>n?-Ra:Math.asin(n)}function et(n){return((n=Math.exp(n))-1/n)/2}function rt(n){return((n=Math.exp(n))+1/n)/2}function ut(n){return((n=Math.exp(2*n))-1)/(n+1)}function it(n){return(n=Math.sin(n/2))*n}function ot(){}function at(n,t,e){return this instanceof at?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof at?new at(n.h,n.s,n.l):bt(""+n,_t,at):new at(n,t,e)}function ct(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,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,i=2*e-o,new mt(u(n+120),u(n),u(n-120))}function lt(n,t,e){return this instanceof lt?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof lt?new lt(n.h,n.c,n.l):n instanceof ft?gt(n.l,n.a,n.b):gt((n=wt((n=ta.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new lt(n,t,e)}function st(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new ft(e,Math.cos(n*=Da)*t,Math.sin(n)*t)}function ft(n,t,e){return this instanceof ft?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof ft?new ft(n.l,n.a,n.b):n instanceof lt?st(n.h,n.c,n.l):wt((n=mt(n)).r,n.g,n.b):new ft(n,t,e)}function ht(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=pt(u)*Xa,r=pt(r)*$a,i=pt(i)*Ba,new mt(dt(3.2404542*u-1.5371385*r-.4985314*i),dt(-.969266*u+1.8760108*r+.041556*i),dt(.0556434*u-.2040259*r+1.0572252*i))}function gt(n,t,e){return n>0?new lt(Math.atan2(e,t)*Pa,Math.sqrt(t*t+e*e),n):new lt(0/0,0/0,n)}function pt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function vt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function dt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mt(n,t,e){return this instanceof mt?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mt?new mt(n.r,n.g,n.b):bt(""+n,mt,ct):new mt(n,t,e)}function yt(n){return new mt(n>>16,n>>8&255,255&n)}function Mt(n){return yt(n)+""}function xt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ga.get(n.toLowerCase()))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function _t(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),new at(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=vt((.4124564*n+.3575761*t+.1804375*e)/Xa),u=vt((.2126729*n+.7151522*t+.072175*e)/$a),i=vt((.0193339*n+.119192*t+.9503041*e)/Ba);return ft(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Nt(t,e,n,r)}}function Nt(n,t,e,r){function u(){var n,t=c.status;if(!t&&zt(c)||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return void o.error.call(i,r)}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=ta.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=ta.event;ta.event=n;try{o.progress.call(i,c)}finally{ta.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ra(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},ta.rebind(i,o,"on"),null==r?i:i.get(Ct(r))}function Ct(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function zt(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qt(){var n=Lt(),t=Tt()-n;t>24?(isFinite(t)&&(clearTimeout(tc),tc=setTimeout(qt,t)),nc=0):(nc=1,rc(qt))}function Lt(){var n=Date.now();for(ec=Ka;ec;)n>=ec.t&&(ec.f=ec.c(n-ec.t)),ec=ec.n;return n}function Tt(){for(var n,t=Ka,e=1/0;t;)t.f?t=n?n.n=t.n:Ka=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r&&e?function(n,t){for(var u=n.length,i=[],o=0,a=r[0],c=0;u>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),i.push(n.substring(u-=a,u+a)),!((c+=a+1)>t));)a=r[o=(o+1)%r.length];return i.reverse().join(e)}:y;return function(n){var e=ic.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",c=e[4]||"",l=e[5],s=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1,y=!0;switch(h&&(h=+h.substring(1)),(l||"0"===r&&"="===o)&&(l=r="0",o="="),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":y=!1;case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=oc.get(g)||Ut;var M=l&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>p){var c=ta.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=y?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);!l&&f&&(x=i(x,1/0));var S=v.length+x.length+b.length+(M?0:u.length),k=s>S?new Array(S=s-S+1).join(r):"";return M&&(x=i(k+x,k.length?s-b.length:1/0)),u+=v,n=x+b,("<"===o?u+n+k:">"===o?k+u+n:"^"===o?k.substring(0,S>>=1)+u+n+k.substring(S):u+(M?n:k+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ft(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new cc(e-1)),1),e}function i(n,e){return t(n=new cc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||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{cc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{cc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ht(n);return c.floor=c,c.round=Ht(r),c.ceil=Ht(u),c.offset=Ht(i),c.range=a,n}function Ht(n){return function(t,e){try{cc=jt;var r=new jt;return r._=t,n(r,e)._}finally{cc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++aa;){if(r>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in sc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=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 u(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 i(n,t,e){E.lastIndex=0;var r=E.exec(t.slice(e));return r?(n.m=A.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,N.c.toString(),t,r)}function c(n,t,r){return e(n,N.x.toString(),t,r)}function l(n,t,r){return e(n,N.X.toString(),t,r)}function s(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{cc=jt;var t=new cc;return t._=n,r(t)}finally{cc=Date}}var r=t(n);return e.parse=function(n){try{cc=jt;var t=r.parse(n);return t&&t._}finally{cc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var M=ta.map(),x=Yt(v),b=Zt(v),_=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),A=Zt(y);p.forEach(function(n,t){M.set(n.toLowerCase(),t)});var N={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+ac.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(ac.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(ac.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:s,S:re,U:Xt,w:Vt,W:$t,x:c,X:l,y:Wt,Y:Bt,Z:Jt,"%":oe};return t}function It(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(ta.requote).join("|")+")","i")}function Zt(n){for(var t=new l,e=-1,r=n.length;++e68?1900:2e3)}function Kt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=ga(t)/60|0,u=ga(t)%60;return e+It(r,"0",2)+It(u,"0",2)}function oe(n,t,e){hc.lastIndex=0;var r=hc.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);yc.add(Math.atan2(h,f)),r=n,u=c,i=l}var t,e,r,u,i;Mc.point=function(o,a){Mc.point=n,r=(t=o)*Da,u=Math.cos(a=(e=a)*Da/2+qa/4),i=Math.sin(a)},Mc.lineEnd=function(){n(t,e)}}function pe(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 ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(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 me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Me(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 xe(n){return[Math.atan2(n[1],n[0]),tt(n[2])]}function be(n,t){return ga(n[0]-t[0])a;++a)u.point((e=n[a])[0],e[1]);return void u.lineEnd()}var c=new qe(e,n,null,!0),l=new qe(e,null,c,!1);c.o=l,i.push(c),o.push(l),c=new qe(r,n,null,!1),l=new qe(r,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),ze(i),ze(o),i.length){for(var a=0,c=e,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function ze(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r0){for(b||(i.polygonStart(),b=!0),i.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Te))}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:l,polygonStart:function(){y.point=s,y.lineStart=f,y.lineEnd=h,g=[],p=[]},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=l,g=ta.merge(g);var n=Fe(m,p);g.length?(b||(i.polygonStart(),b=!0),Ce(g,De,n,e,i)):n&&(b||(i.polygonStart(),b=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),b&&(i.polygonEnd(),b=!1),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},M=Re(),x=t(M),b=!1;return y}}function Te(n){return n.length>1}function Re(){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 De(n,t){return((n=n.x)[0]<0?n[1]-Ra-Ca:Ra-n[1])-((t=t.x)[0]<0?t[1]-Ra-Ca:Ra-t[1])}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?qa:-qa,c=ga(i-e);ga(c-qa)0?Ra:-Ra),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=qa&&(ga(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*Ra,r.point(-qa,u),r.point(0,u),r.point(qa,u),r.point(qa,0),r.point(qa,-u),r.point(0,-u),r.point(-qa,-u),r.point(-qa,0),r.point(-qa,u);else if(ga(n[0]-t[0])>Ca){var i=n[0]a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+qa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+qa/4,M=Math.sin(y),x=Math.cos(y),b=m-h,_=b>=0?1:-1,w=_*b,S=w>qa,k=p*M;if(yc.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),i+=S?b+_*La:b,S^h>=e^m>=e){var E=de(pe(f),pe(n));Me(E);var A=de(u,E);Me(A);var N=(S^b>=0?-1:1)*tt(A[2]);(r>N||r===N&&(E[0]||E[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=m,p=M,v=x,f=n}}return(-Ca>i||Ca>i&&0>yc)^1&o}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?qa:-qa),h):0;if(!e&&(l=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(be(e,g)||be(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&be(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return s|(l&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),l=a[0],s=c-l*l;if(!s)return!e&&n;var f=i*c/s,h=-i*l/s,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),M=m*m-y*(ve(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=ye(d,(-m-x)/y);if(me(b,p),b=xe(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=ga(A-qa)A;if(!N&&k>E&&(_=k,k=E,E=_),C?N?k+E>0^b[1]<(ga(b[0]-w)qa^(w<=b[0]&&b[0]<=S)){var z=ye(d,(-m+x)/y);return me(z,p),[b,xe(z)]}}}function u(t,e){var r=o?n:qa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ga(i)>Ca,c=gr(n,6*Da);return Le(t,e,c,o?[0,-n]:[-qa,n-qa])}function Oe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Ie(n,t,e,r){function u(r,u){return ga(r[0]-n)0?0:3:ga(r[0]-e)0?2:1:ga(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(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 c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&Q(l,i,n)>0&&++t:i[1]<=r&&Q(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:e,s>1?r:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,b=_=0/0}function g(){v&&(p(y,M),x&&w&&A.rejoin(),v.push(A.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=s(n,t);if(d&&m.push([n,t]),S)y=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}};N(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,m,y,M,x,b,_,w,S,k,E=a,A=Re(),N=Oe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=ta.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Ce(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n){var t=0,e=qa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*qa/180,e=n[1]*qa/180):[t/qa*180,e/qa*180]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,tt((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){Dc+=u*n-r*t,r=n,u=t}var t,e,r,u;Hc.point=function(i,o){Hc.point=n,t=r=i,e=u=o},Hc.lineEnd=function(){n(t,e)}}function Xe(n,t){Pc>n&&(Pc=n),n>jc&&(jc=n),Uc>t&&(Uc=t),t>Fc&&(Fc=t)}function $e(){function n(n,t){o.push("M",n,",",t,i)}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 u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function We(n,t){_c+=n,wc+=t,++Sc}function Je(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);kc+=o*(t+n)/2,Ec+=o*(e+r)/2,Ac+=o,We(t=n,e=r)}var t,e;Ic.point=function(r,u){Ic.point=n,We(t=r,e=u)}}function Ge(){Ic.point=We}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);kc+=o*(r+n)/2,Ec+=o*(u+t)/2,Ac+=o,o=u*n-r*t,Nc+=o*(r+n),Cc+=o*(u+t),zc+=3*o,We(r=n,u=t)}var t,e,r,u;Ic.point=function(i,o){Ic.point=n,We(t=r=i,e=u=o)},Ic.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,La)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(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=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(M,x,y,b,_,w,M=o[0],x=o[1],y=e,b=i[0],_=i[1],w=i[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c -},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,M=f-e,x=y*y+M*M;if(x>4*i&&d--){var b=a+g,_=c+p,w=l+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),E=ga(ga(w)-1)i||ga((y*z+M*q)/x-.5)>.3||o>a*g+c*p+l*v)&&(u(t,e,r,a,c,l,N,C,E,b/=S,_/=S,w,d,m),m.point(N,C),u(N,C,E,b,_,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Da),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Pa,e*Pa])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(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 ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*Da,n[1]*Da),[n[0]*h+c,l-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Pa,n[1]*Pa]}function r(){a=Ae(o=lr(m,M,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,M=0,x=0,b=Lc,_=y,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=or(b(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Lc):He((w=+n)*Da),u()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Ie(n[0][0],n[0][1],n[1][0],n[1][1]):y,u()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Da,d=n[1]%360*Da,r()):[v*Pa,d*Pa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Da,M=n[1]%360*Da,x=n.length>2?n[2]%360*Da:0,r()):[m*Pa,M*Pa,x*Pa]},ta.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*Da,e*Da)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>qa?n-La:-qa>n?n+La:n,t]}function lr(n,t,e){return n?t||e?Ae(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function sr(n){return function(t,e){return t+=n,[t>qa?t-La:-qa>t?t+La:t,e]}}function fr(n){var t=sr(n);return t.invert=sr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*r+a*u;return[Math.atan2(c*i-s*o,a*r-l*u),tt(s*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*r+s*u),tt(s*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*La)):(u=n+o*La,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=xe([e,-r*Math.cos(s),-r*Math.sin(s)]))[0],l[1])}}function pr(n,t){var e=pe(t);e[0]-=n,Me(e);var r=nt(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function vr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function Mr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(it(r-t)+u*o*it(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*s,u=e*l+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Pa,Math.atan2(o,Math.sqrt(r*r+u*u))*Pa]}:function(){return[n*Pa,t*Pa]};return p.distance=h,p}function xr(){function n(n,u){var i=Math.sin(u*=Da),o=Math.cos(u),a=ga((n*=Da)-t),c=Math.cos(a);Yc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Zc.point=function(u,i){t=u*Da,e=Math.sin(i*=Da),r=Math.cos(i),Zc.point=n},Zc.lineEnd=function(){Zc.point=Zc.lineEnd=b}}function br(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function _r(n,t){function e(n,t){o>0?-Ra+Ca>t&&(t=-Ra+Ca):t>Ra-Ca&&(t=Ra-Ca);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(qa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=K(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ra]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ga(u)u;u++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function zr(n,t){return n[0]-t[0]||n[1]-t[1]}function qr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Lr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],l=e[1],s=t[1]-c,f=r[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Tr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=el.pop()||new Rr;return t.site=n,t}function Pr(n){Xr(n),Qc.remove(n),el.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&ga(e-c.circle.x)s;++s)l=a[s],c=a[s-1],Kr(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=Jr(c.site,l.site,null,u),Vr(c),Vr(l)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Qc._;a;)if(r=Fr(a,o)-i,r>Ca)a=a.L;else{if(u=i-Hr(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Qc.insert(t,c),t||e){if(t===e)return Xr(t),e=Dr(t.site),Qc.insert(c,e),c.edge=e.edge=Jr(t.site,c.site),Vr(t),void Vr(e);if(!e)return void(c.edge=Jr(t.site,c.site));Xr(t),Xr(e);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=e.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,M=v*v+d*d,x={x:(d*y-g*M)/m+s,y:(h*M-v*y)/m+f};Kr(e.edge,l,p,x),c.edge=Jr(l,n,null,x),e.edge=Jr(n,p,null,x),Vr(t),Vr(e)}}function Fr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,l=c-t;if(!l)return a;var s=a-r,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+r:(r+a)/2}function Hr(n,t){var e=n.N;if(e)return Fr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Ir(n){for(var t,e,r,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Kc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),r=s.x,u=s.y,l=a[++o%c].start(),t=l.x,e=l.y,(ga(r-t)>Ca||ga(u-e)>Ca)&&(a.splice(o,0,new Qr(Gr(i.site,s,ga(r-f)Ca?{x:f,y:ga(t-f)Ca?{x:ga(e-p)Ca?{x:h,y:ga(t-h)Ca?{x:ga(e-g)=-za)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=rl.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,M=tl._;M;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};e={x:d,y:l}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/r,y:c};e={x:(l-u)/r,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xi||f>o||r>h||u>g)){if(p=n.point){var p,v=t-n.x,d=e-n.y,m=v*v+d*d;if(c>m){var y=Math.sqrt(c=m);r=t-y,u=e-y,i=t+y,o=e+y,a=p}}for(var M=n.nodes,x=.5*(s+h),b=.5*(f+g),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:l(n,s,f,x,b);break;case 1:l(n,x,f,h,b);break;case 2:l(n,s,b,x,g);break;case 3:l(n,x,b,h,g)}}}(n,r,u,i,o),a}function gu(n,t){n=ta.rgb(n),t=ta.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+xt(Math.round(e+i*n))+xt(Math.round(r+o*n))+xt(Math.round(u+a*n))}}function pu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=mu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function vu(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function du(n,t){var e,r,u,i=il.lastIndex=ol.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(e=il.exec(n))&&(r=ol.exec(t));)(u=r.index)>i&&(u=t.slice(i,u),a[o]?a[o]+=u:a[++o]=u),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,c.push({i:o,x:vu(e,r)})),i=ol.lastIndex;return ir;++r)a[(e=c[r]).i]=e.x(n);return a.join("")})}function mu(n,t){for(var e,r=ta.interpolators.length;--r>=0&&!(e=ta.interpolators[r](n,t)););return e}function yu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(mu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Mu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function bu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function wu(n){return n*n*n}function Su(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 ku(n){return function(t){return Math.pow(t,n)}}function Eu(n){return 1-Math.cos(n*Ra)}function Au(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Cu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/La*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*La/t)}}function zu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function qu(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 Lu(n,t){n=ta.hcl(n),t=ta.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return st(e+i*n,r+o*n,u+a*n)+""}}function Tu(n,t){n=ta.hsl(n),t=ta.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){n=ta.lab(n),t=ta.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ht(e+i*n,r+o*n,u+a*n)+""}}function Du(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Pu(n){var t=[n.a,n.b],e=[n.c,n.d],r=ju(t),u=Uu(t,e),i=ju(Fu(e,t,-u))||0;t[0]*e[1]180?s+=360:s-l>180&&(l+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:vu(l,s)})):s&&r.push(r.pop()+"rotate("+s+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:vu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:vu(g[0],p[0])},{i:e-2,x:vu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i=0;)e.push(u[r])}function Qu(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(i=n.children)&&(u=i.length))for(var u,i,o=-1;++oe;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function si(n){return n.reduce(fi,0)}function fi(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function pi(n){return[ta.min(n),ta.max(n)]}function vi(n,t){return n.value-t.value}function di(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function mi(n,t){n._pack_next=t,t._pack_prev=n}function yi(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Mi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(xi),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(u=e[1],u.x=u.r,u.y=0,t(u),l>2))for(i=e[2],wi(r,u,i),t(i),di(r,i),r._pack_prev=i,di(i,u),u=r._pack_next,o=3;l>o;o++){wi(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(yi(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!yi(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,M=Math.max(M,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=M,e.forEach(bi)}}function xi(n){n._pack_next=n._pack_prev=n}function bi(n){delete n._pack_next,delete n._pack_prev}function _i(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i=0;)t=u[i],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Ci(n,t,e){return n.a.parent===t.parent?n.a:e}function zi(n){return 1+ta.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Li(n){var t=n.children;return t&&t.length?Li(t[0]):n}function Ti(n){var t,e=n.children;return e&&(t=e.length)?Ti(e[t-1]):n}function Ri(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Di(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function Pi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Pi(n.range())}function ji(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Fi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Hi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ml}function Oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Oi:ji,c=r?Iu:Ou;return o=u(n,t,c,e),a=u(t,n,c,mu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Du)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Xi(n,t)},i.tickFormat=function(t,e){return $i(n,t,e)},i.nice=function(t){return Zi(n,t),u()},i.copy=function(){return Ii(n,t,e,r)},u()}function Yi(n,t){return ta.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Zi(n,t){return Fi(n,Hi(Vi(n,t)[2]))}function Vi(n,t){null==t&&(t=10);var e=Pi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Xi(n,t){return ta.range.apply(ta,Vi(n,t))}function $i(n,t,e){var r=Vi(n,t);if(e){var u=ic.exec(e);if(u.shift(),"s"===u[8]){var i=ta.formatPrefix(Math.max(ga(r[0]),ga(r[1])));return u[7]||(u[7]="."+Bi(i.scale(r[2]))),u[8]="f",e=ta.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Wi(u[8],r)),e=u.join("")}else e=",."+Bi(r[2])+"f";return ta.format(e)}function Bi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Wi(n,t){var e=Bi(t[2]);return n in yl?Math.abs(e-Bi(Math.max(ga(t[0]),ga(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ji(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Fi(r.map(u),e?Math:xl);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=Pi(r),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(e){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return Ml;arguments.length<2?t=Ml:"function"!=typeof t&&(t=ta.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ji(n.copy(),t,e,r)},Yi(o,n)}function Gi(n,t,e){function r(t){return n(u(t))}var u=Ki(t),i=Ki(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Xi(e,n)},r.tickFormat=function(n,t){return $i(e,n,t)},r.nice=function(n){return r.domain(Zi(e,n))},r.exponent=function(o){return arguments.length?(u=Ki(t=o),i=Ki(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Gi(n.copy(),t,e)},Yi(r,n)}function Ki(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Qi(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return ta.range(n.length).map(function(n){return t+e*n})}var u,i,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new l;for(var i,o=-1,a=r.length;++oe?[0/0,0/0]:[e>0?a[e-1]:n[0],et?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return to(n,t,e)},u()}function eo(n,t){function e(e){return e>=e?t[ta.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 eo(n,t)},e}function ro(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 Xi(n,t)},t.tickFormat=function(t,e){return $i(n,t,e)},t.copy=function(){return ro(n)},t}function uo(){return 0}function io(n){return n.innerRadius}function oo(n){return n.outerRadius}function ao(n){return n.startAngle}function co(n){return n.endAngle}function lo(n){return n&&n.padAngle}function so(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function fo(n,t,e,r,u){var i=n[0]-t[0],o=n[1]-t[1],a=(u?r:-r)/Math.sqrt(i*i+o*o),c=a*o,l=-a*i,s=n[0]+c,f=n[1]+l,h=t[0]+c,g=t[1]+l,p=(s+h)/2,v=(f+g)/2,d=h-s,m=g-f,y=d*d+m*m,M=e-r,x=s*g-h*f,b=(0>m?-1:1)*Math.sqrt(M*M*y-x*x),_=(x*m-d*b)/y,w=(-x*d-m*b)/y,S=(x*m+d*b)/y,k=(-x*d+m*b)/y,E=_-p,A=w-v,N=S-p,C=k-v;return E*E+A*A>N*N+C*C&&(_=S,w=k),[[_-c,w-l],[_*e/M,w*e/M]]}function ho(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f1&&u.push("H",r[0]),u.join("")}function mo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?go(n):n[0]+_o(n,Lo(n))}function Ro(n){for(var t,e,r,u=-1,i=n.length;++ur)return s();var u=i[i.active];u&&(--i.count,delete i[i.active],u.event&&u.event.interrupt.call(n,n.__data__,u.index)),i.active=r,o.event&&o.event.start.call(n,n.__data__,t),o.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&v.push(r)}),h=o.ease,f=o.duration,ta.timer(function(){return p.c=l(e||1)?Ne:l,1},0,a)}function l(e){if(i.active!==r)return 1;for(var u=e/f,a=h(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,n.__data__,t),s()):void 0}function s(){return--i.count?delete i[r]:delete n[e],1}var f,h,g=o.delay,p=ec,v=[];return p.t=g+a,u>=g?c(u-g):void(p.c=c)},0,a)}}function Bo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function Wo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function Jo(n){return n.toISOString()}function Go(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=ta.bisect(Vl,u);return i==Vl.length?[t.year,Vi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Vl[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Ko(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Ko(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Pi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Ko(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Go(n.copy(),t,e)},Yi(r,n)}function Ko(n){return new Date(n)}function Qo(n){return JSON.parse(n.responseText)}function na(n){var t=ua.createRange();return t.selectNode(ua.body),t.createContextualFragment(n.responseText)}var ta={version:"3.5.5"},ea=[].slice,ra=function(n){return ea.call(n)},ua=this.document;if(ua)try{ra(ua.documentElement.childNodes)[0].nodeType}catch(ia){ra=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}),ua)try{ua.createElement("DIV").style.setProperty("opacity",0,"")}catch(oa){var aa=this.Element.prototype,ca=aa.setAttribute,la=aa.setAttributeNS,sa=this.CSSStyleDeclaration.prototype,fa=sa.setProperty;aa.setAttribute=function(n,t){ca.call(this,n,t+"")},aa.setAttributeNS=function(n,t,e){la.call(this,n,t,e+"")},sa.setProperty=function(n,t,e){fa.call(this,n,t+"",e)}}ta.ascending=e,ta.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},ta.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ur&&(e=r)}else{for(;++u=r){e=r;break}for(;++ur&&(e=r)}return e},ta.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ue&&(e=r)}else{for(;++u=r){e=r;break}for(;++ue&&(e=r)}return e},ta.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},ta.sum=function(n,t){var e,r=0,i=n.length,o=-1;if(1===arguments.length)for(;++o1?c/(s-1):void 0},ta.deviation=function(){var n=ta.variance.apply(this,arguments);return n?Math.sqrt(n):n};var ha=i(e);ta.bisectLeft=ha.left,ta.bisect=ta.bisectRight=ha.right,ta.bisector=function(n){return i(1===n.length?function(t,r){return e(n(t),r)}:n)},ta.shuffle=function(n,t,e){(i=arguments.length)<3&&(e=n.length,2>i&&(t=0));for(var r,u,i=e-t;i;)u=Math.random()*i--|0,r=n[i+t],n[i+t]=n[u+t],n[u+t]=r;return n},ta.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ta.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},ta.zip=function(){if(!(r=arguments.length))return[];for(var n=-1,t=ta.min(arguments,o),e=new Array(t);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ga=Math.abs;ta.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,u=[],i=a(ga(e)),o=-1;if(n*=i,t*=i,e*=i,0>e)for(;(r=n+e*++o)>t;)u.push(r/i);else for(;(r=n+e*++o)=i.length)return r?r.call(u,o):e?o.sort(e):o;for(var c,s,f,h,g=-1,p=o.length,v=i[a++],d=new l;++g=i.length)return n;var r=[],u=o[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,u={},i=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(ta.map,e,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return o[i.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},ta.set=function(n){var t=new m;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},c(m,{has:h,add:function(n){return this._[s(n+="")]=!0,n},remove:g,values:p,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,f(t))}}),ta.behavior={},ta.rebind=function(n,t){for(var e,r=1,u=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}},ta.event=null,ta.requote=function(n){return n.replace(ma,"\\$&")};var ma=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ya={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},Ma=function(n,t){return t.querySelector(n)},xa=function(n,t){return t.querySelectorAll(n)},ba=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(ba=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(Ma=function(n,t){return Sizzle(n,t)[0]||null},xa=Sizzle,ba=Sizzle.matchesSelector),ta.selection=function(){return ta.select(ua.documentElement)};var _a=ta.selection.prototype=[];_a.select=function(n){var t,e,r,u,i=[];n=N(n);for(var o=-1,a=this.length;++o=0&&(e=n.slice(0,t),n=n.slice(t+1)),wa.hasOwnProperty(e)?{space:wa[e],local:n}:n}},_a.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ta.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))},_a.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,u=-1;if(t=e.classList){for(;++uu){if("string"!=typeof n){2>u&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>u){var i=this.node();return t(i).getComputedStyle(i,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},_a.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))},_a.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},_a.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},_a.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},_a.insert=function(n,t){return n=j(n),t=N(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},_a.remove=function(){return this.each(F)},_a.data=function(n,t){function e(n,e){var r,u,i,o=n.length,f=e.length,h=Math.min(o,f),g=new Array(f),p=new Array(f),v=new Array(o);if(t){var d,m=new l,y=new Array(o);for(r=-1;++rr;++r)p[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,a.push(p),c.push(g),s.push(v)}var r,u,i=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++ii;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return A(u)},_a.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},_a.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},_a.size=function(){var n=0;return Y(this,function(){++n}),n};var Sa=[];ta.selection.enter=Z,ta.selection.enter.prototype=Sa,Sa.append=_a.append,Sa.empty=_a.empty,Sa.node=_a.node,Sa.call=_a.call,Sa.size=_a.size,Sa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=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 ka=ta.map({mouseenter:"mouseover",mouseleave:"mouseout"});ua&&ka.forEach(function(n){"on"+n in ua&&ka.remove(n)});var Ea,Aa=0;ta.mouse=function(n){return J(n,k())};var Na=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ta.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,u=0,i=t.length;i>u;++u)if((r=t[u]).identifier===e)return J(n,r)},ta.behavior.drag=function(){function n(){this.on("mousedown.drag",i).on("touchstart.drag",o)}function e(n,t,e,i,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],p|=n|e,M=r,g({type:"drag",x:r[0]+l[0],y:r[1]+l[1],dx:n,dy:e}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&ta.event.target===f),g({type:"dragend"}))}var l,s=this,f=ta.event.target,h=s.parentNode,g=r.of(s,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=ta.select(e(f)).on(i+d,a).on(o+d,c),y=W(f),M=t(h,v);u?(l=u.apply(s,arguments),l=[l.x-M[0],l.y-M[1]]):l=[0,0],g({type:"dragstart"})}}var r=E(n,"drag","dragstart","dragend"),u=null,i=e(b,ta.mouse,t,"mousemove","mouseup"),o=e(G,ta.touch,y,"touchmove","touchend");return n.origin=function(t){return arguments.length?(u=t,n):u},ta.rebind(n,r,"on")},ta.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?ra(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Ca=1e-6,za=Ca*Ca,qa=Math.PI,La=2*qa,Ta=La-Ca,Ra=qa/2,Da=qa/180,Pa=180/qa,Ua=Math.SQRT2,ja=2,Fa=4;ta.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=rt(v),o=i/(ja*h)*(e*ut(Ua*t+v)-et(v));return[r+o*l,u+o*s,i*e/rt(Ua*t+v)]}return[r+n*l,u+n*s,i*Math.exp(Ua*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],l=o-r,s=a-u,f=l*l+s*s,h=Math.sqrt(f),g=(c*c-i*i+Fa*f)/(2*i*ja*h),p=(c*c-i*i-Fa*f)/(2*c*ja*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ua;return e.duration=1e3*y,e},ta.behavior.zoom=function(){function n(n){n.on(q,f).on(Oa+".zoom",g).on("dblclick.zoom",p).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 u(n){k.k=Math.max(N[0],Math.min(N[1],n))}function i(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},u(Math.pow(2,o)),i(d=e,r),t=ta.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 c(n){z++||n({type:"zoomstart"})}function l(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function s(n){--z||n({type:"zoomend"}),d=null}function f(){function n(){f=1,i(ta.mouse(u),g),l(a)}function r(){h.on(L,null).on(T,null),p(f&&ta.event.target===o),s(a)}var u=this,o=ta.event.target,a=D.of(u,arguments),f=0,h=ta.select(t(u)).on(L,n).on(T,r),g=e(ta.mouse(u)),p=W(u);Dl.call(u),c(a)}function h(){function n(){var n=ta.touches(p);return g=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ta.event.target;ta.select(t).on(x,r).on(b,a),_.push(t);for(var e=ta.event.changedTouches,u=0,i=e.length;i>u;++u)d[e[u].identifier]=null;var c=n(),l=Date.now();if(1===c.length){if(500>l-M){var s=c[0];o(p,s,d[s.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=l}else if(c.length>1){var s=c[0],f=c[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function r(){var n,t,e,r,o=ta.touches(p);Dl.call(p);for(var a=0,c=o.length;c>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var s=(s=e[0]-n[0])*s+(s=e[1]-n[1])*s,f=m&&Math.sqrt(s/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],u(f*g)}M=null,i(n,t),l(v)}function a(){if(ta.event.touches.length){for(var t=ta.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}ta.selectAll(_).on(y,null),w.on(q,f).on(R,h),E(),s(v)}var g,p=this,v=D.of(p,arguments),d={},m=0,y=".zoom-"+ta.event.changedTouches[0].identifier,x="touchmove"+y,b="touchend"+y,_=[],w=ta.select(p),E=W(p);t(),c(v),w.on(q,null).on(R,t)}function g(){var n=D.of(this,arguments);y?clearTimeout(y):(v=e(d=m||ta.mouse(this)),Dl.call(this),c(n)),y=setTimeout(function(){y=null,s(n)},50),S(),u(Math.pow(2,.002*Ha())*k.k),i(d,v),l(n)}function p(){var n=ta.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ta.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,m,y,M,x,b,_,w,k={x:0,y:0,k:1},A=[960,500],N=Ia,C=250,z=0,q="mousedown.zoom",L="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=E(n,"zoomstart","zoom","zoomend");return Oa||(Oa="onwheel"in ua?(Ha=function(){return-ta.event.deltaY*(ta.event.deltaMode?120:1)},"wheel"):"onmousewheel"in ua?(Ha=function(){return ta.event.wheelDelta},"mousewheel"):(Ha=function(){return-ta.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Tl?ta.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},c(n)}).tween("zoom:zoom",function(){var e=A[0],r=A[1],u=d?d[0]:e/2,i=d?d[1]:r/2,o=ta.interpolateZoom([(u-k.x)/k.k,(i-k.y)/k.k,e/k.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:u-r[0]*a,y:i-r[1]*a,k:a},l(n)}}).each("interrupt.zoom",function(){s(n)}).each("end.zoom",function(){s(n)}):(this.__chart__=k,c(n),l(n),s(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:+t},a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(N=null==t?Ia:[+t[0],+t[1]],n):N},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(A=t&&[+t[0],+t[1]],n):A},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},ta.rebind(n,D,"on")};var Ha,Oa,Ia=[0,1/0];ta.color=ot,ot.prototype.toString=function(){return this.rgb()+""},ta.hsl=at;var Ya=at.prototype=new ot;Ya.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,this.l/n)},Ya.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,n*this.l)},Ya.rgb=function(){return ct(this.h,this.s,this.l)},ta.hcl=lt;var Za=lt.prototype=new ot;Za.brighter=function(n){return new lt(this.h,this.c,Math.min(100,this.l+Va*(arguments.length?n:1)))},Za.darker=function(n){return new lt(this.h,this.c,Math.max(0,this.l-Va*(arguments.length?n:1)))},Za.rgb=function(){return st(this.h,this.c,this.l).rgb()},ta.lab=ft;var Va=18,Xa=.95047,$a=1,Ba=1.08883,Wa=ft.prototype=new ot;Wa.brighter=function(n){return new ft(Math.min(100,this.l+Va*(arguments.length?n:1)),this.a,this.b)},Wa.darker=function(n){return new ft(Math.max(0,this.l-Va*(arguments.length?n:1)),this.a,this.b)},Wa.rgb=function(){return ht(this.l,this.a,this.b)},ta.rgb=mt;var Ja=mt.prototype=new ot;Ja.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),new mt(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mt(u,u,u)},Ja.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mt(n*this.r,n*this.g,n*this.b)},Ja.hsl=function(){return _t(this.r,this.g,this.b)},Ja.toString=function(){return"#"+xt(this.r)+xt(this.g)+xt(this.b)};var Ga=ta.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});Ga.forEach(function(n,t){Ga.set(n,yt(t))}),ta.functor=Et,ta.xhr=At(y),ta.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=Nt(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=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 u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(s>=l)return o;if(u)return u=!1,i;var t=s;if(34===n.charCodeAt(t)){for(var e=t;e++s;){var r=n.charCodeAt(s++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(s)&&(++s,++a);else if(r!==c)continue;return n.slice(t,s-a)}return n.slice(t)}for(var r,u,i={},o={},a=[],l=n.length,s=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,f++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new m,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},ta.csv=ta.dsv(",","text/csv"),ta.tsv=ta.dsv(" ","text/tab-separated-values");var Ka,Qa,nc,tc,ec,rc=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ta.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Qa?Qa.n=i:Ka=i,Qa=i,nc||(tc=clearTimeout(tc),nc=1,rc(qt))},ta.timer.flush=function(){Lt(),Tt()},ta.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var uc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);ta.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=ta.round(n,Rt(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)))),uc[8+e/3]};var ic=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,oc=ta.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=ta.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),ac=ta.time={},cc=Date;jt.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(){lc.setUTCDate.apply(this._,arguments)},setDay:function(){lc.setUTCDay.apply(this._,arguments)},setFullYear:function(){lc.setUTCFullYear.apply(this._,arguments)},setHours:function(){lc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){lc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){lc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){lc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){lc.setUTCSeconds.apply(this._,arguments)},setTime:function(){lc.setTime.apply(this._,arguments)}};var lc=Date.prototype;ac.year=Ft(function(n){return n=ac.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ac.years=ac.year.range,ac.years.utc=ac.year.utc.range,ac.day=Ft(function(n){var t=new cc(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}),ac.days=ac.day.range,ac.days.utc=ac.day.utc.range,ac.dayOfYear=function(n){var t=ac.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=ac[n]=Ft(function(n){return(n=ac.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=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ac[n+"s"]=e.range,ac[n+"s"].utc=e.utc.range,ac[n+"OfYear"]=function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)}}),ac.week=ac.sunday,ac.weeks=ac.sunday.range,ac.weeks.utc=ac.sunday.utc.range,ac.weekOfYear=ac.sundayOfYear;var sc={"-":"",_:" ",0:"0"},fc=/^\s*\d+/,hc=/^%/;ta.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var gc=ta.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"]});ta.format=gc.numberFormat,ta.geo={},ce.prototype={s:0,t:0,add:function(n){le(n,this.t,pc),le(pc.s,this.s,this),this.s?this.t+=pc.t:this.s=pc.t -},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var pc=new ce;ta.geo.stream=function(n,t){n&&vc.hasOwnProperty(n.type)?vc[n.type](n,t):se(n,t)};var vc={Feature:function(n,t){se(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*qa+n:n,Mc.lineStart=Mc.lineEnd=Mc.point=b}};ta.geo.bounds=function(){function n(n,t){M.push(x=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=pe([t*Da,e*Da]);if(m){var u=de(m,r),i=[u[1],-u[0],0],o=de(i,u);Me(o),o=xe(o);var c=t-p,l=c>0?1:-1,v=o[0]*Pa*l,d=ga(c)>180;if(d^(v>l*p&&l*t>v)){var y=o[1]*Pa;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>l*p&&l*t>v)){var y=-o[1]*Pa;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t)}else n(t,e);m=r,p=t}function e(){b.point=t}function r(){x[0]=s,x[1]=h,b.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=ga(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Mc.point(n,e),t(n,e)}function i(){Mc.lineStart()}function o(){u(v,d),Mc.lineEnd(),ga(y)>Ca&&(s=-(h=180)),x[0]=s,x[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nyc?(s=-(h=180),f=-(g=90)):y>Ca?g=90:-Ca>y&&(f=-90),x[0]=s,x[1]=h}};return function(n){g=h=-(s=f=1/0),M=[],ta.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,u=M[0],i=[u];t>r;++r)e=M[r],l(e[0],u)||l(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,s=e[0],h=u[1])}return M=x=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),ta.geo.centroid=function(n){xc=bc=_c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,qc);var t=Nc,e=Cc,r=zc,u=t*t+e*e+r*r;return za>u&&(t=kc,e=Ec,r=Ac,Ca>bc&&(t=_c,e=wc,r=Sc),u=t*t+e*e+r*r,za>u)?[0/0,0/0]:[Math.atan2(e,t)*Pa,tt(r/Math.sqrt(u))*Pa]};var xc,bc,_c,wc,Sc,kc,Ec,Ac,Nc,Cc,zc,qc={sphere:b,point:_e,lineStart:Se,lineEnd:ke,polygonStart:function(){qc.lineStart=Ee},polygonEnd:function(){qc.lineStart=Se}},Lc=Le(Ne,Pe,je,[-qa,-qa/2]),Tc=1e9;ta.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Ie(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ta.geo.conicEqualArea=function(){return Ye(Ze)}).raw=Ze,ta.geo.albers=function(){return ta.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ta.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=ta.geo.albers(),o=ta.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ta.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},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?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var l=i.scale(),s=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,r=o.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Ca,f+.12*l+Ca],[s-.214*l-Ca,f+.234*l-Ca]]).stream(c).point,u=a.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Ca,f+.166*l+Ca],[s-.115*l-Ca,f+.234*l-Ca]]).stream(c).point,n},n.scale(1070)};var Rc,Dc,Pc,Uc,jc,Fc,Hc={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Dc=0,Hc.lineStart=Ve},polygonEnd:function(){Hc.lineStart=Hc.lineEnd=Hc.point=b,Rc+=ga(Dc/2)}},Oc={point:Xe,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Ic={point:We,lineStart:Je,lineEnd:Ge,polygonStart:function(){Ic.lineStart=Ke},polygonEnd:function(){Ic.point=We,Ic.lineStart=Je,Ic.lineEnd=Ge}};ta.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),ta.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Rc=0,ta.geo.stream(n,u(Hc)),Rc},n.centroid=function(n){return _c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,u(Ic)),zc?[Nc/zc,Cc/zc]:Ac?[kc/Ac,Ec/Ac]:Sc?[_c/Sc,wc/Sc]:[0/0,0/0]},n.bounds=function(n){return jc=Fc=-(Pc=Uc=1/0),ta.geo.stream(n,u(Oc)),[[Pc,Uc],[jc,Fc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||tr(n):y,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new $e:new Qe(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(ta.geo.albersUsa()).context(null)},ta.geo.transform=function(n){return{stream:function(t){var e=new er(t);for(var r in n)e[r]=n[r];return e}}},er.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()}},ta.geo.projection=ur,ta.geo.projectionMutator=ir,(ta.geo.equirectangular=function(){return ur(ar)}).raw=ar.invert=ar,ta.geo.rotation=function(n){function t(t){return t=n(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t}return n=lr(n[0]%360*Da,n[1]*Da,n.length>2?n[2]*Da:0),t.invert=function(t){return t=n.invert(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t},t},cr.invert=ar,ta.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=lr(-n[0]*Da,-n[1]*Da,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Pa,n[1]*=Pa}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=gr((t=+r)*Da,u*Da),n):t},n.precision=function(r){return arguments.length?(e=gr(t*Da,(u=+r)*Da),n):u},n.angle(90)},ta.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Da,u=n[1]*Da,i=t[1]*Da,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),l=Math.cos(u),s=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=l*s-c*f*a)*e),c*s+l*f*a)},ta.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ta.range(Math.ceil(i/d)*d,u,d).map(h).concat(ta.range(Math.ceil(l/m)*m,c,m).map(g)).concat(ta.range(Math.ceil(r/p)*p,e,p).filter(function(n){return ga(n%d)>Ca}).map(s)).concat(ta.range(Math.ceil(a/v)*v,o,v).filter(function(n){return ga(n%m)>Ca}).map(f))}var e,r,u,i,o,a,c,l,s,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],l=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[i,l],[u,c]]},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(y)):[[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],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,s=vr(a,o,90),f=dr(r,e,y),h=vr(l,c,90),g=dr(i,u,y),n):y},n.majorExtent([[-180,-90+Ca],[180,90-Ca]]).minorExtent([[-180,-80-Ca],[180,80+Ca]])},ta.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=mr,u=yr;return n.distance=function(){return ta.geo.distance(t||r.apply(this,arguments),e||u.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?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},ta.geo.interpolate=function(n,t){return Mr(n[0]*Da,n[1]*Da,t[0]*Da,t[1]*Da)},ta.geo.length=function(n){return Yc=0,ta.geo.stream(n,Zc),Yc};var Yc,Zc={sphere:b,point:b,lineStart:xr,lineEnd:b,polygonStart:b,polygonEnd:b},Vc=br(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ta.geo.azimuthalEqualArea=function(){return ur(Vc)}).raw=Vc;var Xc=br(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},y);(ta.geo.azimuthalEquidistant=function(){return ur(Xc)}).raw=Xc,(ta.geo.conicConformal=function(){return Ye(_r)}).raw=_r,(ta.geo.conicEquidistant=function(){return Ye(wr)}).raw=wr;var $c=br(function(n){return 1/n},Math.atan);(ta.geo.gnomonic=function(){return ur($c)}).raw=$c,Sr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ra]},(ta.geo.mercator=function(){return kr(Sr)}).raw=Sr;var Bc=br(function(){return 1},Math.asin);(ta.geo.orthographic=function(){return ur(Bc)}).raw=Bc;var Wc=br(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ta.geo.stereographic=function(){return ur(Wc)}).raw=Wc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ra]},(ta.geo.transverseMercator=function(){var n=kr(Er),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=Er,ta.geom={},ta.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(e),i=Et(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(zr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=Cr(a),s=Cr(c),f=s[0]===l[0],h=s[s.length-1]===l[l.length-1],g=[];for(t=l.length-1;t>=0;--t)g.push(n[a[l[t]][2]]);for(t=+f;t=r&&l.x<=i&&l.y>=u&&l.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];s.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ca)*Ca,y:Math.round(o(n,t)/Ca)*Ca,i:t}})}var r=Ar,u=Nr,i=r,o=u,a=ul;return n?t(n):(t.links=function(n){return iu(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 iu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(Yr),c=-1,l=a.length,s=a[l-1].edge,f=s.l===o?s.r:s.l;++c=l,h=r>=s,g=h<<1|f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=su()),f?u=l:a=l,h?o=s:c=s,i(n,t,e,r,u,o,a,c)}var s,f,h,g,p,v,d,m,y,M=Et(a),x=Et(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)s=n[g],s.xm&&(m=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var b=+M(s=n[g],g),_=+x(s,g);v>b&&(v=b),d>_&&(d=_),b>m&&(m=b),_>y&&(y=_),f.push(b),h.push(_)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=su();if(k.add=function(n){i(k,n,+M(n,++g),+x(n,g),v,d,m,y)},k.visit=function(n){fu(n,k,v,d,m,y)},k.find=function(n){return hu(k,n[0],n[1],v,d,m,y)},g=-1,null==t){for(;++g=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=cl.get(e)||al,r=ll.get(r)||y,Mu(r(e.apply(null,ea.call(arguments,1))))},ta.interpolateHcl=Lu,ta.interpolateHsl=Tu,ta.interpolateLab=Ru,ta.interpolateRound=Du,ta.transform=function(n){var t=ua.createElementNS(ta.ns.prefix.svg,"g");return(ta.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Pu(e?e.matrix:sl)})(n)},Pu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var sl={a:1,b:0,c:0,d:1,e:0,f:0};ta.interpolateTransform=Hu,ta.layout={},ta.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/d){if(p>c){var l=t.charge/c;n.px-=i*l,n.py-=o*l}return!0}if(t.point&&c&&p>c){var l=t.pointCharge/c;n.px-=i*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=ta.event.x,n.py=ta.event.y,a.resume()}var e,r,u,i,o,a={},c=ta.dispatch("start","tick","end"),l=[1,1],s=.9,f=fl,h=hl,g=-30,p=gl,v=.1,d=.64,m=[],M=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,y,x,b=m.length,_=M.length;for(e=0;_>e;++e)a=M[e],f=a.source,h=a.target,y=h.x-f.x,x=h.y-f.y,(p=y*y+x*x)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,y*=p,x*=p,h.x-=y*(d=f.weight/(h.weight+f.weight)),h.y-=x*d,f.x+=y*(d=1-d),f.y+=x*d);if((d=r*v)&&(y=l[0]/2,x=l[1]/2,e=-1,d))for(;++e0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),ta.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=M[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,l=o.length;++at;++t)(r=m[t]).index=t,r.weight=0;for(t=0;s>t;++t)r=M[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;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),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 f)for(t=0;s>t;++t)u[t]=+f.call(this,M[t],t);else for(t=0;s>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;s>t;++t)i[t]=+h.call(this,M[t],t);else for(t=0;s>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=ta.behavior.drag().origin(y).on("dragstart.force",Xu).on("drag.force",t).on("dragend.force",$u)),arguments.length?void this.on("mouseover.force",Bu).on("mouseout.force",Wu).call(e):e},ta.rebind(a,c,"on")};var fl=20,hl=1,gl=1/0;ta.layout.hierarchy=function(){function n(u){var i,o=[u],a=[];for(u.depth=0;null!=(i=o.pop());)if(a.push(i),(l=e.call(n,i,i.depth))&&(c=l.length)){for(var c,l,s;--c>=0;)o.push(s=l[c]),s.parent=i,s.depth=i.depth+1;r&&(i.value=0),i.children=l}else r&&(i.value=+r.call(n,i,i.depth)||0),delete i.children;return Qu(u,function(n){var e,u;t&&(e=n.children)&&e.sort(t),r&&(u=n.parent)&&(u.value+=n.value)}),a}var t=ei,e=ni,r=ti;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&&(Ku(t,function(n){n.children&&(n.value=0)}),Qu(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},ta.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,l=-1;for(r=t.value?r/t.value:0;++lf?-1:1),p=(f-c*g)/ta.sum(l),v=ta.range(c),d=[];return null!=e&&v.sort(e===pl?function(n,t){return l[t]-l[n]}:function(n,t){return e(o[n],o[t])}),v.forEach(function(n){d[n]={data:o[n],value:a=l[n],startAngle:s,endAngle:s+=a*p+g,padAngle:h}}),d}var t=Number,e=pl,r=0,u=La,i=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?(u=t,n):u},n.padAngle=function(t){return arguments.length?(i=t,n):i},n};var pl={};ta.layout.stack=function(){function n(a,c){if(!(h=a.length))return a;var l=a.map(function(e,r){return t.call(n,e,r)}),s=l.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,s,c);l=ta.permute(l,f),s=ta.permute(s,f);var h,g,p,v,d=r.call(n,s,c),m=l[0].length;for(p=0;m>p;++p)for(u.call(n,l[0][p],v=d[p],s[0][p][1]),g=1;h>g;++g)u.call(n,l[g][p],v+=s[g-1][p][1],s[g][p][1]);return a}var t=y,e=ai,r=ci,u=oi,i=ui,o=ii;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:vl.get(t)||ai,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:dl.get(t)||ci,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var vl=ta.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(li),i=n.map(si),o=ta.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,l=[],s=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],l.push(e)):(c+=i[e],s.push(e));return s.reverse().concat(l)},reverse:function(n){return ta.range(n.length).reverse()},"default":ai}),dl=ta.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,u=0;s>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];s>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;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ci});ta.layout.histogram=function(){function n(n,i){for(var o,a,c=[],l=n.map(e,this),s=r.call(this,l,i),f=u.call(this,s,l,i),i=-1,h=l.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=s[0]&&a<=s[1]&&(o=c[ta.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=pi,u=hi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=Et(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return gi(n,t)}:Et(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ta.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],l=u[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Qu(a,function(n){n.r=+s(n.value)}),Qu(a,Mi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;Qu(a,function(n){n.r+=f}),Qu(a,Mi),Qu(a,function(n){n.r-=f})}return _i(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,e=ta.layout.hierarchy().sort(vi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},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},Gu(n,e)},ta.layout.tree=function(){function n(n,u){var s=o.call(this,n,u),f=s[0],h=t(f);if(Qu(h,e),h.parent.m=-h.z,Ku(h,r),l)Ku(f,i);else{var g=f,p=f,v=f;Ku(f,function(n){n.xp.x&&(p=n),n.depth>v.depth&&(v=n)});var d=a(g,p)/2-g.x,m=c[0]/(p.x+a(p,g)/2+d),y=c[1]/(v.depth||1);Ku(f,function(n){n.x=(n.x+d)*m,n.y=n.depth*y})}return s}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var u,i=t.children,o=0,a=i.length;a>o;++o)r.push((i[o]=u={_:i[o],parent:t,children:(u=i[o].children)&&u.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=u);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){Ni(n);var i=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-i):n.z=i}else r&&(n.z=r.z+a(n._,r._));n.parent.A=u(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function u(n,t,e){if(t){for(var r,u=n,i=n,o=t,c=u.parent.children[0],l=u.m,s=i.m,f=o.m,h=c.m;o=Ei(o),u=ki(u),o&&u;)c=ki(c),i=Ei(i),i.a=n,r=o.z+f-u.z-l+a(o._,u._),r>0&&(Ai(Ci(o,n,e),n,r),l+=r,s+=r),f+=o.m,l+=u.m,h+=c.m,s+=i.m;o&&!Ei(i)&&(i.t=o,i.m+=f-s),u&&!ki(c)&&(c.t=u,c.m+=l-h,e=n)}return e}function i(n){n.x*=c[0],n.y=n.depth*c[1]}var o=ta.layout.hierarchy().sort(null).value(null),a=Si,c=[1,1],l=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(l=null==(c=t)?i:null,n):l?null:c},n.nodeSize=function(t){return arguments.length?(l=null==(c=t)?null:i,n):l?c:null},Gu(n,o)},ta.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],l=0;Qu(c,function(n){var t=n.children;t&&t.length?(n.x=qi(t),n.y=zi(t)):(n.x=o?l+=e(n,o):0,n.y=0,o=n)});var s=Li(c),f=Ti(c),h=s.x-e(s,f)/2,g=f.x+e(f,s)/2;return Qu(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=ta.layout.hierarchy().sort(null).value(null),e=Si,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Gu(n,t)},ta.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++ut?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,l=f(e),s=[],h=i.slice(),p=1/0,v="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),s.area=0;(c=h.length)>0;)s.push(o=h[c-1]),s.area+=o.area,"squarify"!==g||(a=r(s,v))<=p?(h.pop(),p=a):(s.area-=s.pop().area,u(s,v,l,!1),v=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(u(s,v,l,!0),s.length=s.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++oe&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,l=e.y,s=t?c(n.area/t):0;if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++ie.dx)&&(s=e.dx);++ie&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=ta.random.normal.apply(ta,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ta.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}}},ta.scale={};var ml={floor:y,ceil:y};ta.scale.linear=function(){return Ii([0,1],[0,1],mu,!1)};var yl={s:1,g:1,p:1,r:1,e:1};ta.scale.log=function(){return Ji(ta.scale.linear().domain([0,1]),10,!0,[1,10])};var Ml=ta.format(".0e"),xl={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ta.scale.pow=function(){return Gi(ta.scale.linear(),1,[0,1])},ta.scale.sqrt=function(){return ta.scale.pow().exponent(.5)},ta.scale.ordinal=function(){return Qi([],{t:"range",a:[[]]})},ta.scale.category10=function(){return ta.scale.ordinal().range(bl)},ta.scale.category20=function(){return ta.scale.ordinal().range(_l)},ta.scale.category20b=function(){return ta.scale.ordinal().range(wl)},ta.scale.category20c=function(){return ta.scale.ordinal().range(Sl)};var bl=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(Mt),_l=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(Mt),wl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(Mt),Sl=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(Mt);ta.scale.quantile=function(){return no([],[])},ta.scale.quantize=function(){return to(0,1,[0,1])},ta.scale.threshold=function(){return eo([.5],[0,1])},ta.scale.identity=function(){return ro([0,1])},ta.svg={},ta.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),l=Math.max(0,+r.apply(this,arguments)),s=o.apply(this,arguments)-Ra,f=a.apply(this,arguments)-Ra,h=Math.abs(f-s),g=s>f?0:1;if(n>l&&(p=l,l=n,n=p),h>=Ta)return t(l,g)+(n?t(n,1-g):"")+"Z";var p,v,d,m,y,M,x,b,_,w,S,k,E=0,A=0,N=[];if((m=(+c.apply(this,arguments)||0)/2)&&(d=i===kl?Math.sqrt(n*n+l*l):+i.apply(this,arguments),g||(A*=-1),l&&(A=tt(d/l*Math.sin(m))),n&&(E=tt(d/n*Math.sin(m)))),l){y=l*Math.cos(s+A),M=l*Math.sin(s+A),x=l*Math.cos(f-A),b=l*Math.sin(f-A);var C=Math.abs(f-s-2*A)<=qa?0:1;if(A&&so(y,M,x,b)===g^C){var z=(s+f)/2;y=l*Math.cos(z),M=l*Math.sin(z),x=b=null}}else y=M=0;if(n){_=n*Math.cos(f-E),w=n*Math.sin(f-E),S=n*Math.cos(s+E),k=n*Math.sin(s+E);var q=Math.abs(s-f+2*E)<=qa?0:1;if(E&&so(_,w,S,k)===1-g^q){var L=(s+f)/2;_=n*Math.cos(L),w=n*Math.sin(L),S=k=null}}else _=w=0;if((p=Math.min(Math.abs(l-n)/2,+u.apply(this,arguments)))>.001){v=l>n^g?0:1;var T=null==S?[_,w]:null==x?[y,M]:Lr([y,M],[S,k],[x,b],[_,w]),R=y-T[0],D=M-T[1],P=x-T[0],U=b-T[1],j=1/Math.sin(Math.acos((R*P+D*U)/(Math.sqrt(R*R+D*D)*Math.sqrt(P*P+U*U)))/2),F=Math.sqrt(T[0]*T[0]+T[1]*T[1]);if(null!=x){var H=Math.min(p,(l-F)/(j+1)),O=fo(null==S?[_,w]:[S,k],[y,M],l,H,g),I=fo([x,b],[_,w],l,H,g);p===H?N.push("M",O[0],"A",H,",",H," 0 0,",v," ",O[1],"A",l,",",l," 0 ",1-g^so(O[1][0],O[1][1],I[1][0],I[1][1]),",",g," ",I[1],"A",H,",",H," 0 0,",v," ",I[0]):N.push("M",O[0],"A",H,",",H," 0 1,",v," ",I[0])}else N.push("M",y,",",M);if(null!=S){var Y=Math.min(p,(n-F)/(j-1)),Z=fo([y,M],[S,k],n,-Y,g),V=fo([_,w],null==x?[y,M]:[x,b],n,-Y,g);p===Y?N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",V[1],"A",n,",",n," 0 ",g^so(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-g," ",Z[1],"A",Y,",",Y," 0 0,",v," ",Z[0]):N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",Z[0])}else N.push("L",_,",",w)}else N.push("M",y,",",M),null!=x&&N.push("A",l,",",l," 0 ",C,",",g," ",x,",",b),N.push("L",_,",",w),null!=S&&N.push("A",n,",",n," 0 ",q,",",1-g," ",S,",",k);return N.push("Z"),N.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=io,r=oo,u=uo,i=kl,o=ao,a=co,c=lo;return n.innerRadius=function(t){return arguments.length?(e=Et(t),n):e},n.outerRadius=function(t){return arguments.length?(r=Et(t),n):r},n.cornerRadius=function(t){return arguments.length?(u=Et(t),n):u},n.padRadius=function(t){return arguments.length?(i=t==kl?kl:Et(t),n):i},n.startAngle=function(t){return arguments.length?(o=Et(t),n):o},n.endAngle=function(t){return arguments.length?(a=Et(t),n):a},n.padAngle=function(t){return arguments.length?(c=Et(t),n):c},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Ra;return[Math.cos(t)*n,Math.sin(t)*n]},n};var kl="auto";ta.svg.line=function(){return ho(y)};var El=ta.map({linear:go,"linear-closed":po,step:vo,"step-before":mo,"step-after":yo,basis:So,"basis-open":ko,"basis-closed":Eo,bundle:Ao,cardinal:bo,"cardinal-open":Mo,"cardinal-closed":xo,monotone:To});El.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Al=[0,2/3,1/3,0],Nl=[0,1/3,2/3,0],Cl=[0,1/6,2/3,1/6];ta.svg.line.radial=function(){var n=ho(Ro);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},mo.reverse=yo,yo.reverse=mo,ta.svg.area=function(){return Do(y)},ta.svg.area.radial=function(){var n=Do(Ro);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},ta.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),l=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+u(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)-Ra,s=l.call(n,u,r)-Ra;return{r:i,a0:o,a1:s,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(s),i*Math.sin(s)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>qa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=mr,o=yr,a=Po,c=ao,l=co;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(l=Et(t),n):l},n},ta.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=mr,e=yr,r=Uo;return n.source=function(e){return arguments.length?(t=Et(e),n):t},n.target=function(t){return arguments.length?(e=Et(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ta.svg.diagonal.radial=function(){var n=ta.svg.diagonal(),t=Uo,e=n.projection;return n.projection=function(n){return arguments.length?e(jo(t=n)):t},n},ta.svg.symbol=function(){function n(n,r){return(zl.get(t.call(this,n,r))||Oo)(e.call(this,n,r))}var t=Ho,e=Fo;return n.type=function(e){return arguments.length?(t=Et(e),n):t},n.size=function(t){return arguments.length?(e=Et(t),n):e},n};var zl=ta.map({circle:Oo,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*Ll)),e=t*Ll;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/ql),e=t*ql/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ta.svg.symbolTypes=zl.keys();var ql=Math.sqrt(3),Ll=Math.tan(30*Da);_a.transition=function(n){for(var t,e,r=Tl||++Ul,u=Xo(n),i=[],o=Rl||{time:Date.now(),ease:Su,delay:0,duration:250},a=-1,c=this.length;++ai;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Yo(u,this.namespace,this.id)},Pl.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(u){u[r][e].tween.set(n,t)})},Pl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(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 i(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?Hu:mu,a=ta.ns.qualify(n);return Zo(this,"attr."+n,t,a.local?i:u)},Pl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=ta.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Pl.style=function(n,e,r){function u(){this.style.removeProperty(n)}function i(e){return null==e?u:(e+="",function(){var u,i=t(this).getComputedStyle(this,null).getPropertyValue(n);return i!==e&&(u=mu(i,e),function(t){this.style.setProperty(n,u(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 Zo(this,"style."+n,e,i)},Pl.styleTween=function(n,e,r){function u(u,i){var o=e.call(this,u,i,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,u)},Pl.text=function(n){return Zo(this,"text",n,Vo)},Pl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Pl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ta.ease.apply(ta,arguments)),Y(this,function(r){r[e][t].ease=n}))},Pl.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,u,i){r[e][t].delay=+n.call(r,r.__data__,u,i)}:(n=+n,function(r){r[e][t].delay=n}))},Pl.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,u,i){r[e][t].duration=Math.max(1,n.call(r,r.__data__,u,i))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Pl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var u=Rl,i=Tl;try{Tl=e,Y(this,function(t,u,i){Rl=t[r][e],n.call(t,t.__data__,u,i)})}finally{Rl=u,Tl=i}}else Y(this,function(u){var i=u[r][e];(i.event||(i.event=ta.dispatch("start","end","interrupt"))).on(n,t)});return this},Pl.transition=function(){for(var n,t,e,r,u=this.id,i=++Ul,o=this.namespace,a=[],c=0,l=this.length;l>c;c++){a.push(n=[]);for(var t=this[c],s=0,f=t.length;f>s;s++)(e=t[s])&&(r=e[o][u],$o(e,s,o,i,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Yo(a,o,i)},ta.svg.axis=function(){function n(n){n.each(function(){var n,l=ta.select(this),s=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):y:t,p=l.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ca),d=ta.transition(p.exit()).style("opacity",Ca).remove(),m=ta.transition(p.order()).style("opacity",1),M=Math.max(u,0)+o,x=Ui(f),b=l.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ta.transition(b));v.append("line"),v.append("text");var w,S,k,E,A=v.select("line"),N=m.select("line"),C=p.select("text").text(g),z=v.select("text"),q=m.select("text"),L="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=Bo,w="x",k="y",S="x2",E="y2",C.attr("dy",0>L?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+L*i+"V0H"+x[1]+"V"+L*i)):(n=Wo,w="y",k="x",S="y2",E="x2",C.attr("dy",".32em").style("text-anchor",0>L?"end":"start"),_.attr("d","M"+L*i+","+x[0]+"H0V"+x[1]+"H"+L*i)),A.attr(E,L*u),z.attr(k,L*M),N.attr(S,0).attr(E,L*u),q.attr(w,0).attr(k,L*M),f.rangeBand){var T=f,R=T.rangeBand()/2;s=f=function(n){return T(n)+R}}else s.rangeBand?s=f:d.call(n,f,s);v.call(n,s,f),m.call(n,f,f)})}var t,e=ta.scale.linear(),r=jl,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Fl?t+"":jl,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var jl="bottom",Fl={top:1,right:1,bottom:1,left:1};ta.svg.brush=function(){function n(t){t.each(function(){var t=ta.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",i).on("touchstart.brush",i),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,y);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Hl[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 c,f=ta.transition(t),h=ta.transition(o);l&&(c=Ui(l),h.attr("x",c[0]).attr("width",c[1]-c[0]),r(f)),s&&(c=Ui(s),h.attr("y",c[0]).attr("height",c[1]-c[0]),u(f)),e(f)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+f[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",f[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1]-f[0])}function u(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function i(){function i(){32==ta.event.keyCode&&(C||(M=null,q[0]-=f[1],q[1]-=h[1],C=2),S())}function v(){32==ta.event.keyCode&&2==C&&(q[0]+=f[1],q[1]+=h[1],C=0,S())}function d(){var n=ta.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ta.event.altKey?(M||(M=[(f[0]+f[1])/2,(h[0]+h[1])/2]),q[0]=f[+(n[0]s?(u=r,r=s):u=s),v[0]!=r||v[1]!=u?(e?a=null:o=null,v[0]=r,v[1]=u,!0):void 0}function y(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ta.select("body").style("cursor",null),L.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,_=ta.select(ta.event.target),w=c.of(b,arguments),k=ta.select(b),E=_.datum(),A=!/^(n|s)$/.test(E)&&l,N=!/^(e|w)$/.test(E)&&s,C=_.classed("extent"),z=W(b),q=ta.mouse(b),L=ta.select(t(b)).on("keydown.brush",i).on("keyup.brush",v);if(ta.event.changedTouches?L.on("touchmove.brush",d).on("touchend.brush",y):L.on("mousemove.brush",d).on("mouseup.brush",y),k.interrupt().selectAll("*").interrupt(),C)q[0]=f[0]-q[0],q[1]=h[0]-q[1];else if(E){var T=+/w$/.test(E),R=+/^n/.test(E);x=[f[1-T]-q[0],h[1-R]-q[1]],q[0]=f[T],q[1]=h[R]}else ta.event.altKey&&(M=q.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ta.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,c=E(n,"brushstart","brush","brushend"),l=null,s=null,f=[0,0],h=[0,0],g=!0,p=!0,v=Ol[0];return n.event=function(n){n.each(function(){var n=c.of(this,arguments),t={x:f,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Tl?ta.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,f=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=yu(f,t.x),r=yu(h,t.y);return o=a=null,function(u){f=t.x=e(u),h=t.y=r(u),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?(l=t,v=Ol[!l<<1|!s],n):l},n.y=function(t){return arguments.length?(s=t,v=Ol[!l<<1|!s],n):s},n.clamp=function(t){return arguments.length?(l&&s?(g=!!t[0],p=!!t[1]):l?g=!!t:s&&(p=!!t),n):l&&s?[g,p]:l?g:s?p:null},n.extent=function(t){var e,r,u,i,c;return arguments.length?(l&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),o=[e,r],l.invert&&(e=l(e),r=l(r)),e>r&&(c=e,e=r,r=c),(e!=f[0]||r!=f[1])&&(f=[e,r])),s&&(u=t[0],i=t[1],l&&(u=u[1],i=i[1]),a=[u,i],s.invert&&(u=s(u),i=s(i)),u>i&&(c=u,u=i,i=c),(u!=h[0]||i!=h[1])&&(h=[u,i])),n):(l&&(o?(e=o[0],r=o[1]):(e=f[0],r=f[1],l.invert&&(e=l.invert(e),r=l.invert(r)),e>r&&(c=e,e=r,r=c))),s&&(a?(u=a[0],i=a[1]):(u=h[0],i=h[1],s.invert&&(u=s.invert(u),i=s.invert(i)),u>i&&(c=u,u=i,i=c))),l&&s?[[e,u],[r,i]]:l?[e,r]:s&&[u,i])},n.clear=function(){return n.empty()||(f=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!l&&f[0]==f[1]||!!s&&h[0]==h[1]},ta.rebind(n,c,"on")};var Hl={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Ol=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Il=ac.format=gc.timeFormat,Yl=Il.utc,Zl=Yl("%Y-%m-%dT%H:%M:%S.%LZ");Il.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Jo:Zl,Jo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Jo.toString=Zl.toString,ac.second=Ft(function(n){return new cc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ac.seconds=ac.second.range,ac.seconds.utc=ac.second.utc.range,ac.minute=Ft(function(n){return new cc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ac.minutes=ac.minute.range,ac.minutes.utc=ac.minute.utc.range,ac.hour=Ft(function(n){var t=n.getTimezoneOffset()/60;return new cc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ac.hours=ac.hour.range,ac.hours.utc=ac.hour.utc.range,ac.month=Ft(function(n){return n=ac.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ac.months=ac.month.range,ac.months.utc=ac.month.utc.range;var Vl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Xl=[[ac.second,1],[ac.second,5],[ac.second,15],[ac.second,30],[ac.minute,1],[ac.minute,5],[ac.minute,15],[ac.minute,30],[ac.hour,1],[ac.hour,3],[ac.hour,6],[ac.hour,12],[ac.day,1],[ac.day,2],[ac.week,1],[ac.month,1],[ac.month,3],[ac.year,1]],$l=Il.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",Ne]]),Bl={range:function(n,t,e){return ta.range(Math.ceil(n/e)*e,+t,e).map(Ko)},floor:y,ceil:y};Xl.year=ac.year,ac.scale=function(){return Go(ta.scale.linear(),Xl,$l)};var Wl=Xl.map(function(n){return[n[0].utc,n[1]]}),Jl=Yl.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",Ne]]);Wl.year=ac.year.utc,ac.scale.utc=function(){return Go(ta.scale.linear(),Wl,Jl)},ta.text=At(function(n){return n.responseText}),ta.json=function(n,t){return Nt(n,"application/json",Qo,t)},ta.html=function(n,t){return Nt(n,"text/html",na,t)},ta.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(ta):"object"==typeof module&&module.exports&&(module.exports=ta),this.d3=ta}();/*! - * Bootstrap v3.3.4 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/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)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(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){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(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.4",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);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.4",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)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},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")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&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);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).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.4",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"));return a>this.$items.length-1||0>a?void 0: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(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0: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.4",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){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(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 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.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('',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(this.options.viewport.selector||this.options.viewport),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.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),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.leave=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)),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);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-mp.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.width&&(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(){return this.$tip=this.$tip||a(this.options.template)},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))),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)})};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.4",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.4",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.4",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 c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},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=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);/*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl - * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT - * */ - -!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;bf&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+=""),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g"); -x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f); -var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&Cp&&C>1;){E=[],C--;for(var F=0;F(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,dM&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
open:"+b.yAxis.tickFormat()(c.open)+"
close:"+b.yAxis.tickFormat()(c.close)+"
high"+b.yAxis.tickFormat()(c.high)+"
low:"+b.yAxis.tickFormat()(c.low)+"
"}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&Eg&&E>1;){F=[],E--;for(var G=0;G(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,dN&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D] -}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),k.pointActive(function(){return!1}),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},focusMargin:{get:function(){return x},set:function(a){x.top=void 0!==a.top?a.top:x.top,x.right=void 0!==a.right?a.right:x.right,x.bottom=void 0!==a.bottom?a.bottom:x.bottom,x.left=void 0!==a.left?a.left:x.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),h.pointActive(function(){return!1}),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},focusMargin:{get:function(){return r},set:function(a){r.top=void 0!==a.top?a.top:r.top,r.right=void 0!==a.right?a.right:r.right,r.bottom=void 0!==a.bottom?a.bottom:r.bottom,r.left=void 0!==a.left?a.left:r.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(G){return E.reset(),G.each(function(b){var G=k-j.left-j.right,H=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var I=0;if(z&&b.length&&(z=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),v){var J=d3.layout.stack().offset(w).values(function(a){return a.values}).y(r)(!b.length&&z?z:b);J.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=I++,J[c]=b[c]):c>0&&J[c-1].nonStackable&&J[c].values.map(function(a,b){a.y0-=J[c-1].values[b].y,a.y1=a.y0+a.y})}),b=J}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),v&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var K=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b,yErr:s(a,c)}})});m.domain(d||d3.merge(K).map(function(a){return a.x})).rangeBands(f||[0,G],C),n.domain(e||d3.extent(d3.merge(d3.merge(K).map(function(a){var c=a.y;v&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y);var d=a.yErr;return d?d.length?[c+d[0],c+d[1]]:(d=Math.abs(d),[c-d,c+d]):[c]})).concat(t))).range(g||[H,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var L=p.selectAll("g.nv-wrap.nv-multibar").data([b]),M=L.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),N=M.append("defs"),O=M.append("g"),P=L.select("g");O.append("g").attr("class","nv-groups"),L.attr("transform","translate("+j.left+","+j.top+")"),N.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),L.select("#nv-edge-clip-"+o+" rect").attr("width",G).attr("height",H),P.attr("clip-path",u?"url(#nv-edge-clip-"+o+")":"");var Q=L.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});Q.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var R=E.transition(Q.exit().selectAll("g.nv-bar"),"multibarExit",Math.min(100,B)).attr("y",function(a){var c=i(0)||0;return v&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();R.delay&&R.delay(function(a,b){var c=b*(B/(F+1))-b;return c}),Q.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return x(a,b)}).style("stroke",function(a,b){return x(a,b)}),Q.style("stroke-opacity",1).style("fill-opacity",.75);var S=Q.selectAll("g.nv-bar").data(function(a){return z&&!b.length?z.values:a.values});S.exit().remove();var T=S.enter().append("g").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,c,d){var e=v&&!b[d].nonStackable?0:d*m.rangeBand()/b.length,f=i(v&&!b[d].nonStackable?a.y0:0)||0;return"translate("+e+","+f+")"});T.append("rect").attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(v&&!b[d].nonStackable?1:b.length)}).style("fill",function(a,b,c){return x(a,c,b)}).style("stroke",function(a,b,c){return x(a,c,b)}),S.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0].values[0],0)&&(T.append("polyline"),S.select("polyline").attr("fill","none").attr("stroke",function(a,b,c){return y(a,c,b)}).attr("points",function(a,c){var d=s(a,c),e=.8*m.rangeBand()/(2*(v?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return n(a)-n(0)});var f=[[-e,d[0]],[e,d[0]],[0,d[0]],[0,d[1]],[-e,d[1]],[e,d[1]]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=m.rangeBand()/(2*(v?1:b.length)),e=r(a,c)<0?n(r(a,c))-n(0):0;return"translate("+d+", "+e+")"})),S.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),A&&(c||(c=b.map(function(){return!0})),S.select("rect").style("fill",function(a,b,d){return d3.rgb(A(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(A(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var U=S.watchTransition(E,"multibar",Math.min(250,B)).delay(function(a,c){return c*B/b[0].values.length});v?U.attr("transform",function(a,c,d){var e=0;e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1);var f=0;b[d].nonStackable&&(f=a.series*m.rangeBand()/b.length,b.length!==I&&(f=b[d].nonStackableSeries*m.rangeBand()/(2*I)));var g=f+m(q(a,c));return"translate("+g+","+e+")"}).select("rect").attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/I;return b.length!==I&&(e=m.rangeBand()/(2*I)),e}return m.rangeBand()}):U.attr("transform",function(a,c){var d=a.series*m.rangeBand()/b.length+m(q(a,c)),e=r(a,c)<0?n(0):n(0)-n(r(a,c))<1?n(0)-1:n(r(a,c))||0;return"translate("+d+","+e+")"}).select("rect").attr("width",m.rangeBand()/b.length).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(F=b[0].values.length)}),E.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=!0,v=!1,w="zero",x=a.utils.defaultColor(),y=a.utils.defaultColor(),z=!1,A=null,B=500,C=.1,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,B),F=0;return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return v},set:function(a){v=a}},stackOffset:{get:function(){return w},set:function(a){w=a}},clipEdge:{get:function(){return u},set:function(a){u=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return z -},set:function(a){z=a}},groupSpacing:{get:function(){return C},set:function(a){C=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return B},set:function(a){B=a,E.reset(B)}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}},barColor:{get:function(){return A},set:function(b){A=b?a.utils.getColor(b):null}},errorBarColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return F.reset(),m.each(function(b){var m=k-j.left-j.right,D=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),x&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),x&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var G=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1,yErr:s(a,b)}})});o.domain(d||d3.merge(G).map(function(a){return a.x})).rangeBands(f||[0,D],B),p.domain(e||d3.extent(d3.merge(d3.merge(G).map(function(a){var b=a.y;x&&(b=a.y>0?a.y1+a.y:a.y1);var c=a.yErr;return c?c.length?[b+c[0],b+c[1]]:(c=Math.abs(c),[b-c,b+c]):[b]})).concat(t))),p.range(y&&!x?g||[p.domain()[0]<0?A:0,m-(p.domain()[1]>0?A:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var H=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),I=H.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),J=(I.append("defs"),I.append("g"));H.select("g")}J.append("g").attr("class","nv-groups"),H.attr("transform","translate("+j.left+","+j.top+")");var K=H.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});K.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),K.exit().watchTransition(F,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),K.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),K.watchTransition(F,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var L=K.selectAll("g.nv-bar").data(function(a){return a.values});L.exit().remove();var M=L.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(x?a.y0:0)+","+(x?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});M.append("rect").attr("width",0).attr("height",o.rangeBand()/(x?1:b.length)),L.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),E.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),E.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){E.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){E.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){E.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){E.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0].values[0],0)&&(M.append("polyline"),L.select("polyline").attr("fill","none").attr("stroke",function(a,b,c){return w(a,c,b)}).attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(x?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(x?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),M.append("text"),y&&!x?(L.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=C(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+C(Math.abs(d[1]))+"-"+C(Math.abs(d[0])):c+"±"+C(Math.abs(d))}),L.watchTransition(F,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):L.selectAll("text").text(""),z&&!x?(M.append("text").classed("nv-bar-label",!0),L.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),L.watchTransition(F,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):L.selectAll("text.nv-bar-label").text(""),L.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),L.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),x?L.watchTransition(F,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):L.watchTransition(F,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),F.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=a.utils.defaultColor(),x=!1,y=!1,z=!1,A=60,B=.1,C=d3.format(",.2f"),D=250,E=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),F=a.utils.renderWatch(E,D);return b.dispatch=E,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return x},set:function(a){x=a}},showValues:{get:function(){return y},set:function(a){y=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return C},set:function(a){C=a}},valuePadding:{get:function(){return A},set:function(a){A=a}},groupSpacing:{get:function(){return B},set:function(a){B=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return D},set:function(a){D=a,F.reset(D)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}},errorBarColor:{get:function(){return w},set:function(b){w=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?B:A;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},D.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?B:A;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},D.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.seriesIndex].yAxis?B:A;a.point.x=x.x()(a.point),a.point.y=x.y()(a.point),D.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function E(a){var b=2===j[a.data.series].yAxis?B:A;a.value=v.x()(a.data),a.series={value:v.y()(a.data),color:a.color},D.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var F=d3.select(this);a.utils.initSVG(F),b.update=function(){F.transition().call(b)},b.container=this;var G=a.utils.availableWidth(g,F,e),H=a.utils.availableHeight(h,F,e),I=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),J=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),K=j.filter(function(a){return"scatter"==a.type&&1==a.yAxis}),L=j.filter(function(a){return"scatter"==a.type&&2==a.yAxis}),M=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),N=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),O=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),P=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,F),b;F.selectAll(".nv-noData").remove();var Q=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),R=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(Q.concat(R)),function(a){return a.x})).range([0,G]);var S=F.selectAll("g.wrap.multiChart").data([j]),T=S.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");T.append("g").attr("class","nv-x nv-axis"),T.append("g").attr("class","nv-y1 nv-axis"),T.append("g").attr("class","nv-y2 nv-axis"),T.append("g").attr("class","lines1Wrap"),T.append("g").attr("class","lines2Wrap"),T.append("g").attr("class","scatters1Wrap"),T.append("g").attr("class","scatters2Wrap"),T.append("g").attr("class","bars1Wrap"),T.append("g").attr("class","bars2Wrap"),T.append("g").attr("class","stack1Wrap"),T.append("g").attr("class","stack2Wrap"),T.append("g").attr("class","legendWrap");var U=S.select("g"),V=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var W=C.align()?G/2:G,X=C.align()?W:0;C.width(W),C.color(V),U.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(C),e.top!=C.height()&&(e.top=C.height(),H=a.utils.availableHeight(h,F,e)),U.select(".legendWrap").attr("transform","translate("+X+","+-e.top+")")}r.width(G).height(H).interpolate(m).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(G).height(H).interpolate(m).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"scatter"==j[b].type})),u.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"scatter"==j[b].type})),v.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),w.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),x.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),y.width(G).height(H).color(V.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),U.attr("transform","translate("+e.left+","+e.top+")");var Y=U.select(".lines1Wrap").datum(I.filter(function(a){return!a.disabled})),Z=U.select(".scatters1Wrap").datum(K.filter(function(a){return!a.disabled})),$=U.select(".bars1Wrap").datum(M.filter(function(a){return!a.disabled})),_=U.select(".stack1Wrap").datum(O.filter(function(a){return!a.disabled})),ab=U.select(".lines2Wrap").datum(J.filter(function(a){return!a.disabled})),bb=U.select(".scatters2Wrap").datum(L.filter(function(a){return!a.disabled})),cb=U.select(".bars2Wrap").datum(N.filter(function(a){return!a.disabled})),db=U.select(".stack2Wrap").datum(P.filter(function(a){return!a.disabled})),eb=O.length?O.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],fb=P.length?P.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(Q).concat(eb),function(a){return a.y})).range([0,H]),q.domain(d||d3.extent(d3.merge(R).concat(fb),function(a){return a.y})).range([0,H]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),x.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),y.yDomain(q.domain()),O.length&&d3.transition(_).call(x),P.length&&d3.transition(db).call(y),M.length&&d3.transition($).call(v),N.length&&d3.transition(cb).call(w),I.length&&d3.transition(Y).call(r),J.length&&d3.transition(ab).call(s),K.length&&d3.transition(Z).call(t),L.length&&d3.transition(bb).call(u),z._ticks(a.utils.calcTicksX(G/100,j)).tickSize(-H,0),U.select(".nv-x.nv-axis").attr("transform","translate(0,"+H+")"),d3.transition(U.select(".nv-x.nv-axis")).call(z),A._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-G,0),d3.transition(U.select(".nv-y1.nv-axis")).call(A),B._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-G,0),d3.transition(U.select(".nv-y2.nv-axis")).call(B),U.select(".nv-y1.nv-axis").classed("nv-disabled",Q.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),U.select(".nv-y2.nv-axis").classed("nv-disabled",R.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),C.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",l),u.dispatch.on("elementMouseover.tooltip",l),t.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),x.dispatch.on("elementMouseover.tooltip",n),y.dispatch.on("elementMouseover.tooltip",n),x.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),y.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",E),w.dispatch.on("elementMouseover.tooltip",E),v.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){D.hidden(!0)}),v.dispatch.on("elementMousemove.tooltip",function(){D.position({top:d3.event.pageY,left:d3.event.pageX})()}),w.dispatch.on("elementMousemove.tooltip",function(){D.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.scatter().yScale(p),u=a.models.scatter().yScale(q),v=a.models.multiBar().stacked(!1).yScale(p),w=a.models.multiBar().stacked(!1).yScale(q),x=a.models.stackedArea().yScale(p),y=a.models.stackedArea().yScale(q),z=a.models.axis().scale(o).orient("bottom").tickPadding(5),A=a.models.axis().scale(p).orient("left"),B=a.models.axis().scale(q).orient("right"),C=a.models.legend().height(30),D=a.models.tooltip(),E=d3.dispatch();return b.dispatch=E,b.lines1=r,b.lines2=s,b.scatters1=t,b.scatters2=u,b.bars1=v,b.bars2=w,b.stack1=x,b.stack2=y,b.xAxis=z,b.yAxis1=A,b.yAxis2=B,b.tooltip=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return D.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),D.enabled(!!b)}},tooltipContent:{get:function(){return D.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),D.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a),x.x(a),y.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),t.y(a),u.y(a),x.y(a),y.y(a),v.y(a),w.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),x.useVoronoi(a),y.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b -}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;Mc)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+k+"-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+k+"-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var o=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){o(a,L.elementClick)}).on("dblclick",function(a){o(a,L.elementDblClick)}).on("mouseover",function(a){o(a,L.elementMouseover)}).on("mouseout",function(a){o(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b) -}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b)}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();/*! - -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;da?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.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===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(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,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):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(U,"='$1']"),!(!c.matchesSelector||!p||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&&D.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.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:X,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(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===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 X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.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(ca,da).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("(^|"+L+")"+a+"("+L+"|$)"))&&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(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},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;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/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=J(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(R,"$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(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).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:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!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 Z.test(a.nodeName)},input:function(a){return Y.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:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(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}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};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=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0: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){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; - -return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.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 m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("