diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000000..2a2130d6e838 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,509 @@ +# Configuration file for https://circleci.com/gh/angular/angular.js + +# Note: YAML anchors allow an object to be re-used, reducing duplication. +# The ampersand declares an alias for an object, then later the `<<: *name` +# syntax dereferences it. +# See http://blog.daemonl.com/2016/02/yaml.html +# To validate changes, use an online parser, eg. +# http://yaml-online-parser.appspot.com/ + +# CircleCI configuration version +# Version 2.1 allows for extra config reuse features +# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse +version: 2.1 + +# Workspace persisted by the `setup` job to share build artifacts with other jobs. +# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs +# https://circleci.com/blog/deep-diving-into-circleci-workspaces/ +var_workspace_location: &workspace_location ~/ + +# Executor Definitions +# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors +# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version. +# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.) +executors: + default-executor: + parameters: + resource_class: + type: string + default: medium + docker: + - image: circleci/node:14.16.1@sha256:b094e85848b43209ca83d9bb114d406fe62c75cb73b18c9d8eb1a9c6462c97d4 + resource_class: << parameters.resource_class >> + working_directory: ~/ng + cloud-sdk: + description: The docker container to use when running gcp-gcs commands + docker: + - image: google/cloud-sdk:alpine@sha256:7d0cae28cb282b76f2d9babe278c63c910d54f0cceca7a65fdf6806e2b43882e + working_directory: ~/ng + + +# Filter Definitions + +# Filter to run a job on all branches and any `v1.X.Y(-Z)` tags. +# Since the jobs need to run on tagged builds too, a `tags` section has to be explicitly specified. +# (The `branches` section could be omitted, since it defaults to all branches - just being explicit +# here). +# See also https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag. +var-filter-run-always: &run-always + filters: + branches: + only: /.*/ + tags: + only: /v1\.\d+\.\d.*/ + +# Filter to run a job when code might need to be deployed - i.e. on builds for the `master` branch. +# (Further checks are needed to determine whether a deployment is actually needed, but these are not +# possible via filters.) +var-filter-run-on-master: &run-on-master + filters: + branches: + only: + - master + tags: + ignore: /.*/ + +# Filter to run a job when code/docs might need to be deployed - i.e. on tagged builds and on builds +# for master and `v1.*.x` branches. +# (Further checks are needed to determine whether a deployment is actually needed, but these are not +# possible via filters.) +var-filter-run-on-tags-and-master-and-version-branches: &run-on-tags-and-master-and-version-branches + filters: + branches: + only: + - master + - /v1\.\d+\.x/ + tags: + only: /v1\.\d+\.\d.*/ + +# Filter to run a job when docs might need to be deployed - i.e. on builds for `v1.*.x` branches, +# which might correspond to the stable branch. +# (Further checks are needed to determine whether a deployment is actually needed, but these are not +# possible via filters.) +var-filter-run-on-version-branches: &run-on-version-branches + filters: + branches: + only: + - /v1\.\d+\.x/ + tags: + ignore: /.*/ + + +# Command Definitions +# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands +commands: + skip_on_pr_and_fork_builds: + description: Skip a job on pull request and fork builds + steps: + - run: + name: Skip this job if this is a pull request or fork build + # Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this + # step can be run before `init_environment`. + command: > + if [[ -n "$CIRCLE_PR_NUMBER" ]] || + [[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] || + [[ "$CIRCLE_PROJECT_REPONAME" != "angular.js" ]]; then + echo "Skipping this job, because this is either a pull request or a fork build." + circleci step halt + fi + + skip_unless_stable_branch: + description: Skip a job unless this is the stable branch + steps: + - run: + name: Skip this job unless this is the stable branch + command: > + if [[ "$DIST_TAG" != "latest" ]]; then + echo "Skipping deployment, because this is not the stable branch." + circleci step halt + fi + + skip_unless_tag_or_master_or_stable_branch: + description: Skip a job unless this is a tag or the master or stable branch + steps: + - run: + name: Skip this job unless this is a tag or the master or stable branch + command: > + if [[ "$CI_GIT_TAG" == "false" ]] && + [[ "$CI_BRANCH" != "master" ]] && + [[ "$DIST_TAG" != "latest" ]]; then + echo "Skipping this job, because this is neither a tag nor the master or stable branch." + circleci step halt + fi + + + custom_attach_workspace: + description: Attach workspace at a predefined location + steps: + - attach_workspace: + at: *workspace_location + + # Java is needed for running the Closure Compiler (during the `minall` task). + install_java: + description: Install java + steps: + - run: + name: Install java + command: | + sudo apt-get update + # Install java runtime + sudo apt-get install default-jre + + # Initializes the CI environment by setting up common environment variables. + init_environment: + description: Initializing environment (setting up variables) + steps: + - run: + name: Set up environment + environment: + CIRCLE_GIT_BASE_REVISION: << pipeline.git.base_revision >> + CIRCLE_GIT_REVISION: << pipeline.git.revision >> + command: ./.circleci/env.sh + - run: + # Configure git as the CircleCI `checkout` command does. + # This is needed because we only checkout on the setup job. + # Add GitHub to known hosts + name: Configure git + command: | + mkdir -p ~/.ssh + echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts + git config --global url."ssh://git@github.com".insteadOf "https://github.com/" || true + git config --global gc.auto 0 || true + + init_saucelabs_environment: + description: Sets up a domain that resolves to the local host. + steps: + - run: + name: Preparing environment for running tests on Saucelabs. + command: | + # For SauceLabs jobs, we set up a domain which resolves to the machine which launched + # the tunnel. We do this because devices are sometimes not able to properly resolve + # `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not + # resolve to anything on SauceLabs VMs ensures that such requests are always resolved + # through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM). + # More context can be found in: https://github.com/angular/angular/pull/35171. + setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local" + setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev) + - run: + # Sets up a local domain in the machine's host file that resolves to the local + # host. This domain is helpful in Saucelabs tests where devices are not able to + # properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel. + name: Setting up alias domain for local host. + command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts + + start_saucelabs: + steps: + - run: + name: Starting Saucelabs tunnel service + command: ./lib/saucelabs/sauce-service.sh start-ready-wait + + stop_saucelabs: + steps: + - run: + name: Stopping Saucelabs tunnel service + command: ./lib/saucelabs/sauce-service.sh stop + + run_e2e_tests: + parameters: + specs: + type: string + steps: + - custom_attach_workspace + - init_environment + - init_saucelabs_environment + - start_saucelabs + - run: + command: yarn grunt test:circleci-protractor --specs="<< parameters.specs >>" + no_output_timeout: 30m + - stop_saucelabs + + run_e2e_tests_jquery: + parameters: + specs: + type: string + steps: + - custom_attach_workspace + - init_environment + - init_saucelabs_environment + - start_saucelabs + - run: + environment: + USE_JQUERY: 1 + command: yarn grunt test:circleci-protractor --specs="<< parameters.specs >>" + no_output_timeout: 30m + - stop_saucelabs + +# Job definitions +# Jobs can include parameters that are passed in the workflow job invocation. +# https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs +jobs: + setup: + executor: default-executor + steps: + - checkout + - init_environment + - install_java + - run: + name: Running Yarn install + command: yarn install --frozen-lockfile --non-interactive + # Yarn's requests sometimes take more than 10mins to complete. + no_output_timeout: 45m + - run: yarn grunt package + # Persist any changes at this point to be reused by further jobs. + # **NOTE**: To add new content to the workspace, always persist on the same root. + - persist_to_workspace: + root: *workspace_location + paths: + - ./ng + + lint: + executor: default-executor + steps: + - custom_attach_workspace + - init_environment + - run: yarn grunt ci-checks + - run: yarn commitplease "$CI_COMMIT_RANGE" + - run: yarn grunt validate-angular-files + + unit-test: + executor: + name: default-executor + steps: + - custom_attach_workspace + - init_environment + - install_java + - init_saucelabs_environment + - run: yarn grunt test:promises-aplus + - run: + command: yarn grunt test:jqlite --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:modules --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:docs --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + + unit-test-jquery: + executor: + name: default-executor + steps: + - custom_attach_workspace + - init_environment + - init_saucelabs_environment + - run: + command: yarn grunt test:jquery --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:jquery-2.2 --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:jquery-2.1 --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + + e2e-test-1: + executor: + name: default-executor + steps: + - run_e2e_tests: + specs: test/e2e/tests/**/*.js + + e2e-test-2a: + executor: + name: default-executor + steps: + - run_e2e_tests: + specs: build/docs/ptore2e/example-ng*/**/default_test.js + + e2e-test-2b: + executor: + name: default-executor + steps: + - run_e2e_tests: + specs: "build/docs/ptore2e/!(example-ng*)/**/default_test.js" + + e2e-test-jquery-1: + executor: + name: default-executor + steps: + - run_e2e_tests_jquery: + specs: test/e2e/tests/**/*.js + + e2e-test-jquery-2a: + executor: + name: default-executor + steps: + - run_e2e_tests_jquery: + specs: build/docs/ptore2e/example-ng*/**/jquery_test.js + + e2e-test-jquery-2b: + executor: + name: default-executor + steps: + - run_e2e_tests_jquery: + specs: build/docs/ptore2e/!(example-ng*)/**/jquery_test.js + + prepare-deployment: + executor: + name: default-executor + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + - run: yarn grunt prepareDeploy + # Write the deployment files to the workspace to be used by deploy-docs and deploy-code + - persist_to_workspace: + root: *workspace_location + paths: + - ./ng + + # The `deploy-code-files` job should only run when all of these conditions are true for the build: + # - It is for the `angular/angular.js` repository (not a fork). + # - It is not for a pull request. + # - It is for a tag or the master branch or the stable branch(*). + # + # *: The stable branch is the one that has the value `latest` in `package.json > distTag`. + deploy-code-files: + executor: + name: cloud-sdk + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + - skip_unless_tag_or_master_or_stable_branch + - run: ls scripts/code.angularjs.org-firebase/deploy + - run: + name: Authenticate and configure Docker + command: | + echo $GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=- + gcloud --quiet config set project ${GOOGLE_PROJECT_ID} + - run: + name: Sync files to code.angularjs.org + command: | + gsutil -m rsync -r scripts/code.angularjs.org-firebase/deploy gs://code-angularjs-org-338b8.appspot.com + + # The `deploy-code-firebase` job should only run when all of these conditions are true for the build: + # - It is for the `angular/angular.js` repository (not a fork). + # - It is not for a pull request. + # - It is for the master branch. + # (This is enforced via job filters, so we don't need to a step to check it here.) + deploy-code-firebase: + executor: + name: default-executor + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + # Install dependencies for Firebase functions to prevent parsing errors during deployment. + # See https://github.com/angular/angular.js/pull/16453. + - run: + name: Install dependencies in `scripts/code.angularjs.org-firebase/functions/`. + working_directory: scripts/code.angularjs.org-firebase/functions + command: yarn install --frozen-lockfile --ignore-engines --non-interactive + - run: + name: Deploy to Firebase from `scripts/code.angularjs.org-firebase/`. + working_directory: scripts/code.angularjs.org-firebase + command: | + # Do not use `yarn firebase` as that causes the Firebase CLI to look for `firebase.json` + # in the root directory, even if run from inside `scripts/code.angularjs.org-firebase/`. + firebase=$(yarn bin)/firebase + $firebase use + $firebase deploy --message "Commit:\ $CI_COMMIT" --non-interactive --token "$FIREBASE_TOKEN" + + # The `deploy-docs` job should only run when all of these conditions are true for the build: + # - It is for the `angular/angular.js` repository (not a fork). + # - It is not for a pull request. + # - It is for the stable branch(*). + # + # *: The stable branch is the one that has the value `latest` in `package.json > distTag`. + deploy-docs: + executor: + name: default-executor + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + - skip_unless_stable_branch + # Install dependencies for Firebase functions to prevent parsing errors during deployment. + # See https://github.com/angular/angular.js/pull/16453. + - run: + name: Install dependencies in `scripts/docs.angularjs.org-firebase/functions/`. + working_directory: scripts/docs.angularjs.org-firebase/functions + command: yarn install --frozen-lockfile --ignore-engines --non-interactive + - run: + name: Deploy to Firebase from `scripts/docs.angularjs.org-firebase/`. + working_directory: scripts/docs.angularjs.org-firebase + command: | + # Do not use `yarn firebase` as that causes the Firebase CLI to look for `firebase.json` + # in the root directory, even if run from inside `scripts/docs.angularjs.org-firebase/`. + firebase=$(yarn bin)/firebase + $firebase use + $firebase deploy --message "Commit:\ $CI_COMMIT" --non-interactive --token "$FIREBASE_TOKEN" + +workflows: + version: 2 + default_workflow: + jobs: + - setup: + <<: *run-always + - lint: + <<: *run-always + requires: + - setup + - unit-test: + <<: *run-always + requires: + - setup + - unit-test-jquery: + <<: *run-always + requires: + - setup + - e2e-test-1: + <<: *run-always + requires: + - setup + - e2e-test-2a: + <<: *run-always + requires: + - setup + - e2e-test-2b: + <<: *run-always + requires: + - setup + - e2e-test-jquery-1: + <<: *run-always + requires: + - setup + - e2e-test-jquery-2a: + <<: *run-always + requires: + - setup + - e2e-test-jquery-2b: + <<: *run-always + requires: + - setup + - prepare-deployment: + <<: *run-on-tags-and-master-and-version-branches + requires: + - setup + - lint + - unit-test + - unit-test-jquery + - e2e-test-1 + - e2e-test-2a + - e2e-test-2b + - e2e-test-jquery-1 + - e2e-test-jquery-2a + - e2e-test-jquery-2b + - deploy-code-files: + <<: *run-on-tags-and-master-and-version-branches + requires: + - prepare-deployment + - deploy-code-firebase: + <<: *run-on-master + requires: + - prepare-deployment + - deploy-docs: + <<: *run-on-version-branches + requires: + - prepare-deployment diff --git a/.circleci/env-helpers.inc.sh b/.circleci/env-helpers.inc.sh new file mode 100644 index 000000000000..5fa1263e112f --- /dev/null +++ b/.circleci/env-helpers.inc.sh @@ -0,0 +1,73 @@ +#################################################################################################### +# Helpers for defining environment variables for CircleCI. +# +# In CircleCI, each step runs in a new shell. The way to share ENV variables across steps is to +# export them from `$BASH_ENV`, which is automatically sourced at the beginning of every step (for +# the default `bash` shell). +# +# See also https://circleci.com/docs/2.0/env-vars/#using-bash_env-to-set-environment-variables. +#################################################################################################### + +# Set and print an environment variable. +# +# Use this function for setting environment variables that are public, i.e. it is OK for them to be +# visible to anyone through the CI logs. +# +# Usage: `setPublicVar ` +function setPublicVar() { + setSecretVar $1 "$2"; + echo "$1=$2"; +} + +# Set (without printing) an environment variable. +# +# Use this function for setting environment variables that are secret, i.e. should not be visible to +# everyone through the CI logs. +# +# Usage: `setSecretVar ` +function setSecretVar() { + # WARNING: Secrets (e.g. passwords, access tokens) should NOT be printed. + # (Keep original shell options to restore at the end.) + local -r originalShellOptions=$(set +o); + set +x -eu -o pipefail; + + echo "export $1=\"${2:-}\";" >> $BASH_ENV; + + # Restore original shell options. + eval "$originalShellOptions"; +} + + +# Create a function to set an environment variable, when called. +# +# Use this function for creating setter for public environment variables that require expensive or +# time-consuming computaions and may not be needed. When needed, you can call this function to set +# the environment variable (which will be available through `$BASH_ENV` from that point onwards). +# +# Arguments: +# - ``: The name of the environment variable. The generated setter function will be +# `setPublicVar_`. +# - ``: The code to run to compute the value for the variable. Since this code should be +# executed lazily, it must be properly escaped. For example: +# ```sh +# # DO NOT do this: +# createPublicVarSetter MY_VAR "$(whoami)"; # `whoami` will be evaluated eagerly +# +# # DO this isntead: +# createPublicVarSetter MY_VAR "\$(whoami)"; # `whoami` will NOT be evaluated eagerly +# ``` +# +# Usage: `createPublicVarSetter ` +# +# Example: +# ```sh +# createPublicVarSetter MY_VAR 'echo "FOO"'; +# echo $MY_VAR; # Not defined +# +# setPublicVar_MY_VAR; +# source $BASH_ENV; +# echo $MY_VAR; # FOO +# ``` +function createPublicVarSetter() { + echo "setPublicVar_$1() { setPublicVar $1 \"$2\"; }" >> $BASH_ENV; +} diff --git a/.circleci/env.sh b/.circleci/env.sh new file mode 100755 index 000000000000..338371017ccb --- /dev/null +++ b/.circleci/env.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Variables +readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") +readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; + +# Load helpers and make them available everywhere (through `$BASH_ENV`). +source $envHelpersPath; +echo "source $envHelpersPath;" >> $BASH_ENV; + +#################################################################################################### +# Define PUBLIC environment variables for CircleCI. +#################################################################################################### +# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. +#################################################################################################### +setPublicVar CI "$CI" +setPublicVar PROJECT_ROOT "$projectDir"; +# This is the branch being built; e.g. `pull/12345` for PR builds. +setPublicVar CI_BRANCH "$CIRCLE_BRANCH"; +setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL"; +setPublicVar CI_COMMIT "$CIRCLE_SHA1"; +setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}"; +setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}"; +setPublicVar CI_GIT_TAG "${CIRCLE_TAG:-false}"; +setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION"; +setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}"; +setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME"; +setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME"; +setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME"; +setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME"; + + +#################################################################################################### +# Define SauceLabs environment variables for CircleCI. +#################################################################################################### +setPublicVar BROWSER_PROVIDER "saucelabs" + +# The currently latest-1 version of desktop Safari on Saucelabs (v12.0) is unstable and disconnects +# consistently. The latest version (v12.1) works fine. +# TODO: Add `SL_Safari-1` back, once it no longer corresponds to v12.0. +setPublicVar BROWSERS "SL_Chrome,SL_Chrome-1,\ +SL_Firefox,SL_Firefox-1,\ +SL_Safari,\ +SL_iOS,SL_iOS-1,\ +SL_IE_9,SL_IE_10,SL_IE_11,\ +SL_EDGE,SL_EDGE-1" + +setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log +setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock +setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock +setPublicVar SAUCE_TUNNEL_IDENTIFIER "angularjs-framework-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}" +# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not +# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout. +setPublicVar SAUCE_READY_FILE_TIMEOUT 120 + +#################################################################################################### +# Define additional environment variables +#################################################################################################### + +# NOTE: Make sure the tools used to compute this are available in all executors in `config.yml`. +setPublicVar DIST_TAG $( cat package.json | grep distTag | sed -E 's/^\s*"distTag"\s*:\s*"([^"]+)"\s*,\s*$/\1/' ) + +#################################################################################################### +#################################################################################################### +## Source `$BASH_ENV` to make the variables available immediately. ## +## *** NOTE: This must remain the last command in this script. *** ## +#################################################################################################### +#################################################################################################### +source $BASH_ENV; diff --git a/.editorconfig b/.editorconfig index f6a54e4dd2c5..a6bc2855214e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# http://editorconfig.org +# https://editorconfig.org root = true diff --git a/.eslintignore b/.eslintignore index 315b45467795..6d8222eb45db 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,6 +1,4 @@ -bower_components/** build/** -docs/bower_components/** docs/app/assets/js/angular-bootstrap/** docs/config/templates/** node_modules/** @@ -9,3 +7,4 @@ src/angular.bind.js src/ngParseExt/ucd.js i18n/closure/** tmp/** +vendor/** diff --git a/.eslintrc-node.json b/.eslintrc-node.json index 643f345d88e0..c16a8a883837 100644 --- a/.eslintrc-node.json +++ b/.eslintrc-node.json @@ -1,8 +1,13 @@ { "extends": "./.eslintrc-base.json", - "env": { "browser": false, "node": true - } + }, + "parserOptions": { + "ecmaVersion": 2017 + }, + "plugins": [ + "promise" + ] } diff --git a/.eslintrc-todo.json b/.eslintrc-todo.json index 7e89110084b7..a7b24d7a05b0 100644 --- a/.eslintrc-todo.json +++ b/.eslintrc-todo.json @@ -15,7 +15,7 @@ // Stylistic issues "block-spacing": ["error", "always"], "comma-spacing": "error", - "id-blacklist": ["error", "event"], + "id-denylist": ["error", "event"], "indent": ["error", 2], "key-spacing": ["error", { "beforeColon": false, "afterColon": true, "mode": "minimum" }], "object-curly-spacing": ["error", "never"], diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 50ed8a2b8f13..f5513f23390c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,27 +1,42 @@ -***Note*: for support questions, please use one of these channels: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.** - -**Do you want to request a *feature* or report a *bug*?** - - - -**What is the current behavior?** - - - -**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).** - - - -**What is the expected behavior?** - - - -**What is the motivation / use case for changing the behavior?** - - - -**Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.** - - - -**Other information (e.g. stacktraces, related issues, suggestions how to fix)** +# AngularJS is in LTS mode +We are no longer accepting changes that are not critical bug fixes into this project. +See https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c for more detail. + + + + + +**I'm submitting a ...** + +- [ ] regression from 1.7.0 +- [ ] security issue +- [ ] issue caused by a new browser version +- [ ] other + +**Current behavior:** + + +**Expected / new behavior:** + + +**Minimal reproduction of the problem with instructions:** + + +**AngularJS version:** 1.8.x + + +**Browser:** [all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView | Opera XX ] + + +**Anything else:** + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9a79ea9890ee..fd23b045065a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,11 @@ -**What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)** +# AngularJS is in LTS mode +We are no longer accepting changes that are not critical bug fixes into this project. +See https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c for more detail. + +**Does this PR fix a regression since 1.7.0, a security flaw, or a problem caused by a new browser version?** + + **What is the current behavior? (You can also link to an open issue here)** @@ -15,9 +21,9 @@ **Please check if the PR fulfills these requirements** -- [ ] The commit message follows our guidelines: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format -- [ ] Tests for the changes have been added (for bug fixes / features) -- [ ] Docs have been added / updated (for bug fixes / features) +- [ ] The commit message follows our [guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) +- [ ] Fix/Feature: [Docs](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#documentation) have been added/updated +- [ ] Fix/Feature: Tests have been added; existing tests pass **Other information**: diff --git a/.gitignore b/.gitignore index e897180b89d1..9641ed4fd609 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,9 @@ performance/temp*.html *~ *.swp angular.js.tmproj -/node_modules/ -bower_components/ +node_modules/ angular.xcodeproj +.firebase/ .idea *.iml .agignore @@ -19,7 +19,9 @@ angular.xcodeproj libpeerconnection.log npm-debug.log /tmp/ -/scripts/bower/bower-* .vscode *.log -*.stackdump \ No newline at end of file +*.stackdump +scripts/code.angularjs.org-firebase/deploy +scripts/docs.angularjs.org-firebase/deploy +scripts/docs.angularjs.org-firebase/functions/content diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000000..f1a2dc0b18e0 --- /dev/null +++ b/.mailmap @@ -0,0 +1,29 @@ +Andres Ornelas +Caitlin Potter +Caitlin Potter +Di Peng +Di Peng +Georgios Kalpakas +Georgios Kalpakas +Julie Ralph +Lucas Galfaso +Martin Staffa +Martin Staffa +Matias Niemelä +Michał Gołębiowski-Owczarek +Misko Hevery +Misko Hevery +Igor Minar +Igor Minar +Igor Minar +Igor Minar +Pawel Kozlowski +Peter Bacon Darwin +Rodric Haddad +Shahar Talmi +Shahar Talmi +Shyam Seshadri +Shyam Seshadri +Vojta Jina +Vojta Jina +Vojta Jina diff --git a/.nvmrc b/.nvmrc index 1e8b31496214..6b17d228d335 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -6 +14.16.1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e51e815aa7c4..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,61 +0,0 @@ -language: node_js -sudo: false -node_js: - - '6' - -cache: - directories: - - node_modules - - bower_components - - docs/bower_components - -branches: - except: - - /^g3_.*$/ - -env: - matrix: - - JOB=ci-checks - - JOB=unit BROWSER_PROVIDER=saucelabs - - JOB=docs-e2e BROWSER_PROVIDER=saucelabs - - JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs - - JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs - global: - - CXX=g++-4.8 # node 4 likes the G++ v4.8 compiler - - SAUCE_USERNAME=angular-ci - - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987 - - LOGS_DIR=/tmp/angular-build/logs - - BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready - -# node 4 likes the G++ v4.8 compiler -# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 - -before_install: - - curl -o- -L https://raw.githubusercontent.com/yarnpkg/yarn/2a0afc73210c7a82082585283e518eeb88ca19ae/scripts/install-latest.sh | bash -s -- --version 0.17.9 - - export PATH=$HOME/.yarn/bin:$PATH - -before_script: - - du -sh ./node_modules ./bower_components/ ./docs/bower_components/ || true - - ./scripts/travis/before_build.sh - -script: - - ./scripts/travis/build.sh - -after_script: - - ./scripts/travis/tear_down_browser_provider.sh - - ./scripts/travis/print_logs.sh - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2 - - http://104.197.9.155:8484/hubot/travis/activity #hubot-server - on_success: always # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always - on_start: always # default: false diff --git a/CHANGELOG.md b/CHANGELOG.md index b9fd6c9a0098..eaad69f26da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,2696 @@ +**AngularJS support has officially ended as of January 2022. +[See what ending support means](https://docs.angularjs.org/misc/version-support-status) +and [read the end of life announcement](https://goo.gle/angularjs-end-of-life).** + +**Visit [angular.io](https://angular.io) for the actively supported Angular.** + + +# 1.8.2 ultimate-farewell (2020-10-21) + +One final release of AngularJS in order to update package README files on npm. + + +# 1.8.2 meteoric-mining (2020-10-21) + +## Bug Fixes +- **$sceDelegate:** ensure that `resourceUrlWhitelist()` is identical to `trustedResourceUrlList()` + ([e41f01](https://github.com/angular/angular.js/commit/e41f018959934bfbf982ba996cd654b1fce88d43), + [#17090](https://github.com/angular/angular.js/issues/17090)) + + + +# 1.8.1 mutually-supporting (2020-09-30) + +## Bug Fixes +- **$sanitize:** do not trigger CSP alert/report in Firefox and Chrome + ([2fab3d](https://github.com/angular/angular.js/commit/2fab3d4e00f4fe35bfa3cf255160cb97404baf24)) + +## Refactorings + +- **SanitizeUriProvider:** remove usages of whitelist + ([76738102](https://github.com/angular/angular.js/commit/767381020d88bda2855ac87ca6f00748907e14ff)) +- **httpProvider:** remove usages of whitelist and blacklist + ([c953af6b](https://github.com/angular/angular.js/commit/c953af6b8cfeefe4acc0ca358550eed5da8cfe00)) +- **sceDelegateProvider:** remove usages of whitelist and blacklist + ([a206e267](https://github.com/angular/angular.js/commit/a206e2675c351c3cdcde3402978126774c1c5df9)) + +## Deprecation Notices + +- Deprecated ~~`$compileProvider.aHrefSanitizationWhitelist`~~. + It is now [`aHrefSanitizationTrustedUrlList`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationTrustedUrlList). +- Deprecated ~~`$compileProvider.imgSrcSanitizationWhitelist`~~. + It is now [`imgSrcSanitizationTrustedUrlList`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationTrustedUrlList). +- Deprecated ~~`$httpProvider.xsrfWhitelistedOrigins`~~. + It is now [`xsrfTrustedOrigins`](https://docs.angularjs.org/api/ng/provider/$httpProvider#xsrfTrustedOrigins). +- Deprecated ~~`$sceDelegateProvider.resourceUrlWhitelist`~~. + It is now [`trustedResourceUrlList`](https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#trustedResourceUrlList). +- Deprecated ~~`$sceDelegateProvider.resourceUrlBlacklist`~~. + It is now [`bannedResourceUrlList`](https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#bannedResourceUrlList). + +For the purposes of backward compatibility, the previous symbols are aliased to their new symbol. + + + +# 1.8.0 nested-vaccination (2020-06-01) + +_This release contains a breaking change to resolve a security issue which was discovered by +Krzysztof Kotowicz(@koto); and independently by Esben Sparre Andreasen (@esbena) while +performing a Variant Analysis of [CVE-2020-11022](https://github.com/advisories/GHSA-gxr4-xjj5-5px2) +which itself was found and reported by Masato Kinugawa (@masatokinugawa)._ + +## Bug Fixes +- **jqLite:** + - prevent possible XSS due to regex-based HTML replacement + ([2df43c](https://github.com/angular/angular.js/commit/2df43c07779137d1bddf7f3b282a1287a8634acd)) + +## Breaking Changes + +### **jqLite** due to: + - **[2df43c](https://github.com/angular/angular.js/commit/2df43c07779137d1bddf7f3b282a1287a8634acd)**: prevent possible XSS due to regex-based HTML replacement + +JqLite no longer turns XHTML-like strings like `
` to sibling elements `
` +when not in XHTML mode. Instead it will leave them as-is. The browser, in non-XHTML mode, will convert these to: +`
`. + +This is a security fix to avoid an XSS vulnerability if a new jqLite element is created from a user-controlled HTML string. +If you must have this functionality and understand the risk involved then it is posible to restore the original behavior by calling + +```js +angular.UNSAFE_restoreLegacyJqLiteXHTMLReplacement(); +``` + +But you should adjust your code for this change and remove your use of this function as soon as possible. + +Note that this only patches jqLite. If you use jQuery 3.5.0 or newer, please read the [jQuery 3.5 upgrade guide](https://jquery.com/upgrade-guide/3.5/) for more details about the workarounds. + + + +# 1.7.9 pollution-eradication (2019-11-19) + +## Bug Fixes +- **angular.merge:** do not merge __proto__ property + ([726f49](https://github.com/angular/angular.js/commit/726f49dcf6c23106ddaf5cfd5e2e592841db743a)) +
(Thanks to the [Snyk Security Research Team](https://snyk.io/blog/snyk-research-team-discovers-severe-prototype-pollution-security-vulnerabilities-affecting-all-versions-of-lodash/) for identifyng this issue.) +- **ngStyle:** correctly remove old style when new style value is invalid + ([5edd25](https://github.com/angular/angular.js/commit/5edd25364f617083363dc2bd61f9230b38267578), + [#16860](https://github.com/angular/angular.js/issues/16860), + [#16868](https://github.com/angular/angular.js/issues/16868)) + + + +# 1.7.8 enthusiastic-oblation (2019-03-11) + + +## Bug Fixes +- **required:** correctly validate required on non-input element surrounded by ngIf + ([a4c7bd](https://github.com/angular/angular.js/commit/a4c7bdccd76c39c30e33f6215da9a00cc8acde2c), + [#16830](https://github.com/angular/angular.js/issues/16830), + [#16836](https://github.com/angular/angular.js/issues/16836)) + + + +# 1.7.7 kingly-exiting (2019-02-04) + +## Bug Fixes +- **ngRequired:** set error correctly when inside ngRepeat and false by default + ([5ad4f5](https://github.com/angular/angular.js/commit/5ad4f5562c37b1cb575e3e5fddd96e9dd10408e2), + [#16814](https://github.com/angular/angular.js/issues/16814), + [#16820](https://github.com/angular/angular.js/issues/16820)) + + + +# 1.7.6 gravity-manipulation (2019-01-17) + +## Bug Fixes +- **$compile:** fix ng-prop-* with undefined values + ([772440](https://github.com/angular/angular.js/commit/772440cdaf9a9bfa40de1675e20a5f0e356089ed), + [#16797](https://github.com/angular/angular.js/issues/16797), + [#16798](https://github.com/angular/angular.js/issues/16798)) +- **compile:** properly handle false value for boolean attrs with jQuery + ([27486b](https://github.com/angular/angular.js/commit/27486bd15e70946ece2ba713e4e8654b7f9bddad), + [#16778](https://github.com/angular/angular.js/issues/16778), + [#16779](https://github.com/angular/angular.js/issues/16779)) +- **ngRepeat:** + - fix reference to last collection value remaining across linkages + ([cf919a](https://github.com/angular/angular.js/commit/cf919a6fb7fc655f3fa37a74899a797ea5b8073e)) + - fix trackBy function being invoked with incorrect scope + ([d4d103](https://github.com/angular/angular.js/commit/d4d1031bcd9b30ae6a58bd60a79bcc9d20f0f2b7), + [#16776](https://github.com/angular/angular.js/issues/16776), + [#16777](https://github.com/angular/angular.js/issues/16777)) +- **aria/ngClick:** check if element is `contenteditable` before blocking spacebar + ([289374](https://github.com/angular/angular.js/commit/289374a43c1b2fd715ddf7455db225b17afebbaf), + [#16762](https://github.com/angular/angular.js/issues/16762)) +- **input:** prevent browsers from autofilling hidden inputs + ([7cbb10](https://github.com/angular/angular.js/commit/7cbb1044fcb3576cdad791bd22ebea3dfd533ff8)) +- **Angular:** add workaround for Safari / Webdriver problem + ([eb49f6](https://github.com/angular/angular.js/commit/eb49f6b7555cfd7ab03fd35581adb6b4bd49044e)) +- **$browser:** normalize inputted URLs + ([2f72a6](https://github.com/angular/angular.js/commit/2f72a69ded53a122afad3ec28d91f9bd2f41eb4f), + [#16606](https://github.com/angular/angular.js/issues/16606)) +- **interpolate:** do not create directives for constant media URL attributes + ([90a41d](https://github.com/angular/angular.js/commit/90a41d415c83abdbf28317f49df0fd0a7e07db86), + [#16734](https://github.com/angular/angular.js/issues/16734)) +- **$q:** allow third-party promise libraries + ([eefaa7](https://github.com/angular/angular.js/commit/eefaa76a90dbef08fdc7d734a205cc2de50d9f91), + [#16164](https://github.com/angular/angular.js/issues/16164), + [#16471](https://github.com/angular/angular.js/issues/16471)) +- **urlUtils:** make IPv6 URL's hostname wrapped in square brackets in IE/Edge + ([0e1bd7](https://github.com/angular/angular.js/commit/0e1bd7822e61822a48b8fd7ba5913a8702e6dabf), + [#16692](https://github.com/angular/angular.js/issues/16692), + [#16715](https://github.com/angular/angular.js/issues/16715)) +- **ngAnimateSwap:** make it compatible with `ngIf` on the same element + ([b27080](https://github.com/angular/angular.js/commit/b27080d52546409fb4e483f212f03616e2ca8037), + [#16616](https://github.com/angular/angular.js/issues/16616), + [#16729](https://github.com/angular/angular.js/issues/16729)) +- **ngMock:** make matchLatestDefinitionEnabled work + ([3cdffc](https://github.com/angular/angular.js/commit/3cdffcecbae71189b4db69b57fadda6608a23b61), + [#16702](https://github.com/angular/angular.js/issues/16702)) +- **ngStyle:** skip setting empty value when new style has the property + ([d6098e](https://github.com/angular/angular.js/commit/d6098eeb1c9510d599e9bd3cfdba7dd21e7a55a5), + [#16709](https://github.com/angular/angular.js/issues/16709)) + +## Performance Improvements +- **input:** prevent multiple validations on initialization + ([692622](https://github.com/angular/angular.js/commit/69262239632027b373258e75c670b89132ad9edb), + [#14691](https://github.com/angular/angular.js/issues/14691), + [#16760](https://github.com/angular/angular.js/issues/16760)) + + + + +# 1.7.5 anti-prettification (2018-10-04) + +## Bug Fixes +- **ngClass:** do not break on invalid values + ([f3a565](https://github.com/angular/angular.js/commit/f3a565872d802c94bb213944791b11b483d52f73), + [#16697](https://github.com/angular/angular.js/issues/16697), + [#16699](https://github.com/angular/angular.js/issues/16699)) + + + +# 1.7.4 interstellar-exploration (2018-09-07) + +## Bug Fixes +- **ngAria.ngClick:** prevent default event on space/enter only for non-interactive elements + ([61b335](https://github.com/angular/angular.js/commit/61b33543ff8e7f32464dec98a46bf0a35e9b03a4), + [#16664](https://github.com/angular/angular.js/issues/16664), + [#16680](https://github.com/angular/angular.js/issues/16680)) +- **ngAnimate:** remove the "prepare" classes with multiple structural animations + ([3105b2](https://github.com/angular/angular.js/commit/3105b2c26a71594c4e7904efc18f4b2e9da25b1b), + [#16681](https://github.com/angular/angular.js/issues/16681), + [#16677](https://github.com/angular/angular.js/issues/16677)) +- **$route:** correctly extract path params if the path contains a question mark or a hash + ([2ceeb7](https://github.com/angular/angular.js/commit/2ceeb739f35e01fcebcabac4beeeb7684ae9f86d)) +- **ngHref:** allow numbers and other objects in interpolation + ([30084c](https://github.com/angular/angular.js/commit/30084c13699c814ff6703d7aa2d3947a9b2f7067), + [#16652](https://github.com/angular/angular.js/issues/16652), + [#16626](https://github.com/angular/angular.js/issues/16626)) +- **select:** allow to select first option with value `undefined` + ([668a33](https://github.com/angular/angular.js/commit/668a33da3439f17e61dfa8f6d9b114ebde8c9d87), + [#16653](https://github.com/angular/angular.js/issues/16653), + [#16656](https://github.com/angular/angular.js/issues/16656)) + + + +# 1.7.3 eventful-proposal (2018-08-03) + +## Bug Fixes +- **$location:** + - fix infinite recursion/digest on URLs with special characters + ([e68697](https://github.com/angular/angular.js/commit/e68697e2e30695f509e6c2c1e43c2c02b7af41f0), + [#16592](https://github.com/angular/angular.js/issues/16592), + [#16611](https://github.com/angular/angular.js/issues/16611)) + - avoid unnecessary `$locationChange*` events due to empty hash + ([1144b1](https://github.com/angular/angular.js/commit/1144b1eccb886ea0e4a80bcb07d38a305c3263b4), + [#16632](https://github.com/angular/angular.js/issues/16632), + [#16636](https://github.com/angular/angular.js/issues/16636)) +- **ngMock.$httpBackend:** + - pass failed HTTP expectations to `$exceptionHandler` + ([4adbf8](https://github.com/angular/angular.js/commit/4adbf82a84a564a8d3f0982c17a64c6163200bcd), + [#16644](https://github.com/angular/angular.js/issues/16644)) + - correctly ignore query params in {expect,when}Route + ([be417f](https://github.com/angular/angular.js/commit/be417f28549e184fbc3c7f74251ac21fca965ae8), + [#14173](https://github.com/angular/angular.js/issues/14173), + [#16589](https://github.com/angular/angular.js/issues/16589)) +- **Angular:** add workaround for Safari / Webdriver problem + ([0a1db2](https://github.com/angular/angular.js/commit/0a1db2ad5f8da6902b1711a738ae4177ce9685fa), + [#16645](https://github.com/angular/angular.js/issues/16645)) +- **$animate:** avoid memory leak with `$animate.enabled(element, enabled)` + ([4bd424](https://github.com/angular/angular.js/commit/4bd424690612885ca06028e9b27de585edc3d3c3), + [#16649](https://github.com/angular/angular.js/issues/16649)) +- **$compile:** + - use correct parent element when requiring on html element + ([05ac70](https://github.com/angular/angular.js/commit/05ac702bc7edae5f89c363ea661774910735ea8b), + [#16535](https://github.com/angular/angular.js/issues/16535), + [#16647](https://github.com/angular/angular.js/issues/16647)) + - work around Firefox `DocumentFragment` bug + ([10973c](https://github.com/angular/angular.js/commit/10973c3366676ac8e5b2728b1e006cdef4ea197e), + [#16607](https://github.com/angular/angular.js/issues/16607), + [#16615](https://github.com/angular/angular.js/issues/16615)) +- **ngEventDirs:** + - pass error in handler to $exceptionHandler when event was triggered in a digest + ([688211](https://github.com/angular/angular.js/commit/6882113bc194fb10081db9bab3dd7d69dd59f311)) + - don't wrap the event handler in $apply if already in $digest + ([535ee3](https://github.com/angular/angular.js/commit/535ee32a0b4881c9fd526fb5e0ffc10919ba1800), + [#14673](https://github.com/angular/angular.js/issues/14673), + [#14674](https://github.com/angular/angular.js/issues/14674)) +- **angular.element:** do not break on `cleanData()` if `_data()` returns undefined + ([7cf4a2](https://github.com/angular/angular.js/commit/7cf4a2933cb017e45b0c97b0a836cbbd905ee31a), + [#16641](https://github.com/angular/angular.js/issues/16641), + [#16642](https://github.com/angular/angular.js/issues/16642)) +- **ngAria:** do not scroll when pressing spacebar on custom buttons + ([3a517c](https://github.com/angular/angular.js/commit/3a517c25f677294a7a9eca1660654a3edcc9e103), + [#14665](https://github.com/angular/angular.js/issues/14665), + [#16604](https://github.com/angular/angular.js/issues/16604)) + + +## New Features +- **$compile:** add support for arbitrary DOM property and event bindings + ([a5914c](https://github.com/angular/angular.js/commit/a5914c94a8fa5b1eceeab9e4e6849cbf467bc26d), + [#16428](https://github.com/angular/angular.js/issues/16428), + [#16235](https://github.com/angular/angular.js/issues/16235), + [#16614](https://github.com/angular/angular.js/issues/16614)) +- **ngMock:** add `$flushPendingTasks()` and `$verifyNoPendingTasks()` + ([6f7674](https://github.com/angular/angular.js/commit/6f7674a7d063d434205f75f5b861f167e8125999), + [#14336](https://github.com/angular/angular.js/issues/14336)) +- **core:** implement more granular pending task tracking + ([17b139](https://github.com/angular/angular.js/commit/17b139f107e5471a9351af638093a8e13a69e42a)) +- **$animate:** add option data to event callbacks + ([fc64e6](https://github.com/angular/angular.js/commit/fc64e6807642512b567deb52b497bd2bff570a1f), + [#12697](https://github.com/angular/angular.js/issues/12697), + [#13059](https://github.com/angular/angular.js/issues/13059)) +- **form.FormController:** add $getControls() + ([c9d1e6](https://github.com/angular/angular.js/commit/c9d1e690aa597283373b78e646676fa8f1ba1b4d), + [#16601](https://github.com/angular/angular.js/issues/16601), + [#14749](https://github.com/angular/angular.js/issues/14749), + [#14517](https://github.com/angular/angular.js/issues/14517), + [#13202](https://github.com/angular/angular.js/issues/13202)) +- **ngModelOptions:** add `timeStripZeroSeconds` and `timeSecondsFormat` + ([b68221](https://github.com/angular/angular.js/commit/b682213d72d65c996a6a31ea57b79d4c4f4e3c98), + [#10721](https://github.com/angular/angular.js/issues/10721), + [#16510](https://github.com/angular/angular.js/issues/16510), + [#16584](https://github.com/angular/angular.js/issues/16584)) + + +## Performance Improvements +- **ngAnimate:** avoid repeated calls to addClass/removeClass when animation has no duration + ([093635](https://github.com/angular/angular.js/commit/0936353e9a03f072bc3c4056888fd154a96530ef), + [#14165](https://github.com/angular/angular.js/issues/14165), + [#14166](https://github.com/angular/angular.js/issues/14166), + [#16613](https://github.com/angular/angular.js/issues/16613)) + + + +# 1.7.2 extreme-compatiplication (2018-06-12) + +In the previous release, we removed a private, undocumented API that was no longer used by +AngularJS. It turned out that several popular UI libraries (such as +[AngularJS Material](https://material.angularjs.org/), +[UI Bootstrap](https://angular-ui.github.io/bootstrap/), +[ngDialog](http://likeastore.github.io/ngDialog/) and probably others) relied on that API. + +In order to avoid unnecessary pain for developers, this release reverts the removal of the private +API and restores compatibility of the aforementioned libraries with the latest AngularJS. + +## Reverts +- **$compile:** remove `preAssignBindingsEnabled` leftovers + ([2da495](https://github.com/angular/angular.js/commit/2da49504065e9e2b71a7a5622e45118d8abbe87e), + [#16580](https://github.com/angular/angular.js/pull/16580), + [a81232](https://github.com/angular/angular.js/commit/a812327acda8bc890a4c4e809f0debb761c29625), + [#16595](https://github.com/angular/angular.js/pull/16595)) + + + +# 1.7.1 momentum-defiance (2018-06-08) + + +## Bug Fixes +- **$compile:** support transcluding multi-element directives + ([789db8](https://github.com/angular/angular.js/commit/789db83a8ae0e2db5db13289b2c29e56093d967a), + [#15554](https://github.com/angular/angular.js/issues/15554), + [#15555](https://github.com/angular/angular.js/issues/15555)) +- **ngModel:** do not throw if view value changes on destroyed scope + ([2b6c98](https://github.com/angular/angular.js/commit/2b6c9867369fd3ef1ddb687af1153478ab62ee1b), + [#16583](https://github.com/angular/angular.js/issues/16583), + [#16585](https://github.com/angular/angular.js/issues/16585)) + + +## New Features +- **$compile:** add one-way collection bindings + ([f9d1ca](https://github.com/angular/angular.js/commit/f9d1ca20c38f065f15769fbe23aee5314cb58bd4), + [#14039](https://github.com/angular/angular.js/issues/14039), + [#16553](https://github.com/angular/angular.js/issues/16553), + [#15874](https://github.com/angular/angular.js/issues/15874)) +- **ngRef:** add directive to publish controller, or element into scope + ([bf841d](https://github.com/angular/angular.js/commit/bf841d35120bf3c4655fde46af4105c85a0f1cdc), + [#16511](https://github.com/angular/angular.js/issues/16511)) +- **errorHandlingConfig:** add option to exclude error params from url + ([3d6c45](https://github.com/angular/angular.js/commit/3d6c45d76e30b1b3c4eb9672cf4a93e5251c06b3), + [#14744](https://github.com/angular/angular.js/issues/14744), + [#15707](https://github.com/angular/angular.js/issues/15707), + [#16283](https://github.com/angular/angular.js/issues/16283), + [#16299](https://github.com/angular/angular.js/issues/16299), + [#16591](https://github.com/angular/angular.js/issues/16591)) +- **ngAria:** add support for ignoring a specific element + ([7d9d38](https://github.com/angular/angular.js/commit/7d9d387195292cb5e04984602b752d31853cfea6), + [#14602](https://github.com/angular/angular.js/issues/14602), + [#14672](https://github.com/angular/angular.js/issues/14672), + [#14833](https://github.com/angular/angular.js/issues/14833)) +- **ngCookies:** support samesite option + ([10a229](https://github.com/angular/angular.js/commit/10a229ce1befdeaf6295d1635dc11391c252a91a), + [#16543](https://github.com/angular/angular.js/issues/16543), + [#16544](https://github.com/angular/angular.js/issues/16544)) +- **ngMessages:** add support for default message + ([a8c263](https://github.com/angular/angular.js/commit/a8c263c1947cc85ee60b4732f7e4bcdc7ba463e8), + [#12008](https://github.com/angular/angular.js/issues/12008), + [#12213](https://github.com/angular/angular.js/issues/12213), + [#16587](https://github.com/angular/angular.js/issues/16587)) +- **ngMock, ngMockE2E:** add option to match latest definition for `$httpBackend` request + ([773f39](https://github.com/angular/angular.js/commit/773f39c9345479f5f8b6321236ce6ad96f77aa92), + [#16251](https://github.com/angular/angular.js/issues/16251), + [#11637](https://github.com/angular/angular.js/issues/11637), + [#16560](https://github.com/angular/angular.js/issues/16560)) +- **$route:** add support for the `reloadOnUrl` configuration option + ([f4f571](https://github.com/angular/angular.js/commit/f4f571efdf86d6acbcd5c6b1de66b4b33a259125), + [#7925](https://github.com/angular/angular.js/issues/7925), + [#15002](https://github.com/angular/angular.js/issues/15002)) + + + +# 1.7.0 nonexistent-physiology (2018-05-11) + +**Here are the full changes for the release of 1.7.0 that are not already released in the 1.6.x branch, +which includes commits from 1.7.0-rc.0 and commits from 1.7.0 directly.** + +1.7.0 is the last scheduled release of AngularJS that includes breaking changes. 1.7.x patch +releases will continue to receive bug fixes and non-breaking features until AngularJS enters Long +Term Support mode (LTS) on July 1st 2018. + +## Bug Fixes +- **input:** + - listen on "change" instead of "click" for radio/checkbox ngModels + ([656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e), + [#4516](https://github.com/angular/angular.js/issues/4516), + [#14667](https://github.com/angular/angular.js/issues/14667), + [#14685](https://github.com/angular/angular.js/issues/14685)) +- **input\[number\]:** validate min/max against viewValue + ([aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec), + [#12761](https://github.com/angular/angular.js/issues/12761), + [#16325](https://github.com/angular/angular.js/issues/16325)) +- **input\[date\]:** correctly parse 2-digit years + ([627180](https://github.com/angular/angular.js/commit/627180fb71b92048d5b9ca2606b9eff1fd99387e), + [#16537](https://github.com/angular/angular.js/issues/16537), + [#16539](https://github.com/angular/angular.js/issues/16539)) +- **jqLite:** make removeData() not remove event handlers + ([b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a), + [#15869](https://github.com/angular/angular.js/issues/15869), + [#16512](https://github.com/angular/angular.js/issues/16512)) +- **$compile:** + - remove the preAssignBindingsEnabled flag + ([38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb), + [#15782](https://github.com/angular/angular.js/issues/15782)) + - add `base[href]` to the list of RESOURCE_URL context attributes + ([1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e), + [#15597](https://github.com/angular/angular.js/issues/15597)) +- **$interval:** throw when trying to cancel non-$interval promise + ([a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$timeout:** throw when trying to cancel non-$timeout promise + ([336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$cookies:** remove the deprecated $cookieStore factory + ([73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77), + [#16465](https://github.com/angular/angular.js/issues/16465)) +- **$resource:** fix interceptors and success/error callbacks + ([ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd), + [#6731](https://github.com/angular/angular.js/issues/6731), + [#9334](https://github.com/angular/angular.js/issues/9334), + [#6865](https://github.com/angular/angular.js/issues/6865), + [#16446](https://github.com/angular/angular.js/issues/16446)) +- **$templateRequest:** + - give tpload error the correct namespace + ([c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)) + - always return the template that is stored in the cache + ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e), + [#16225](https://github.com/angular/angular.js/issues/16225)) +- **$animate:** let cancel() reject the runner promise + ([16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83), + [#14204](https://github.com/angular/angular.js/issues/14204), + [#16373](https://github.com/angular/angular.js/issues/16373)) +- **ngTouch:** + - deprecate the module and its contents + ([67f54b](https://github.com/angular/angular.js/commit/67f54b660038de2b4346b3e76d66a8dc8ccb1f9b), + [#16427](https://github.com/angular/angular.js/issues/16427), + [#16431](https://github.com/angular/angular.js/issues/16431)) + - remove ngClick override, `$touchProvider`, and `$touch` + ([11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50), + [#15761](https://github.com/angular/angular.js/issues/15761), + [#15755](https://github.com/angular/angular.js/issues/15755)) +- **ngScenario:** completely remove the angular scenario runner + ([0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60), + [#9405](https://github.com/angular/angular.js/issues/9405)) +- **form:** set $submitted to true on child forms when parent is submitted + ([223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77), + [#10071](https://github.com/angular/angular.js/issues/10071)) +- **$rootScope:** + - provide correct value of one-time bindings in watchGroup + ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)) + - don't allow explicit digest calls to affect $evalAsync + ([02c046](https://github.com/angular/angular.js/commit/02c04690da16a9bef55694f5db0b8368dc0125c9), + [#15127](https://github.com/angular/angular.js/issues/15127), + [#15494](https://github.com/angular/angular.js/issues/15494)) +- **ngAria:** do not set aria attributes on input[type="hidden"] + ([6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b), + [#15113](https://github.com/angular/angular.js/issues/15113), + [#16367](https://github.com/angular/angular.js/issues/16367)) +- **ngModel, input:** improve handling of built-in named parsers + ([74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140), + [#14292](https://github.com/angular/angular.js/issues/14292), + [#10076](https://github.com/angular/angular.js/issues/10076), + [#16347](https://github.com/angular/angular.js/issues/16347)) +- **$httpParamSerializerJQLike:** + - call functions as jQuery does + ([a784fa](https://github.com/angular/angular.js/commit/a784fab605d825f1158c6292b3c42f8c4a502fdf), + [#16138](https://github.com/angular/angular.js/issues/16138), + [#16139](https://github.com/angular/angular.js/issues/16139)) + - follow jQuery for `null` and `undefined` + ([301fdd](https://github.com/angular/angular.js/commit/301fdda648680d89ccab607c413a7ddede7b0165)) +- **$parse:** + - do not pass scope/locals to interceptors of one-time bindings + ([87a586](https://github.com/angular/angular.js/commit/87a586eb9a23cfd0d0bb681cc778b4b8e5c8451d)) + - always pass the intercepted value to watchers + ([2ee503](https://github.com/angular/angular.js/commit/2ee5033967d5f87a516bad137686b0592e25d26b), + [#16021](https://github.com/angular/angular.js/issues/16021)) + - respect the interceptor.$stateful flag + ([de7403](https://github.com/angular/angular.js/commit/de74034ddf6f92505ccdb61be413a6df2c723f87)) +- **Angular:** remove `angular.lowercase` and `angular.uppercase` + ([1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de), + [#15445](https://github.com/angular/angular.js/issues/15445)) +- **$controller:** remove instantiating controllers defined on window + ([e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16), + [#15349](https://github.com/angular/angular.js/issues/15349), + [#15762](https://github.com/angular/angular.js/issues/15762)) + + +## New Features +- **angular.isArray:** support Array subclasses in `angular.isArray()` + ([e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948), + [#15533](https://github.com/angular/angular.js/issues/15533), + [#15541](https://github.com/angular/angular.js/issues/15541)) +- **$sce:** handle URL sanitization through the `$sce` service + ([1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)) +- **orderBy:** consider `null` and `undefined` greater than other values + ([1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8), + [#15294](https://github.com/angular/angular.js/issues/15294), + [#16376](https://github.com/angular/angular.js/issues/16376)) +- **$resource:** add support for `request` and `requestError` interceptors (#15674) + ([240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded), + [#5146](https://github.com/angular/angular.js/issues/5146)) +- **ngModelOptions:** add debounce catch-all + allow debouncing 'default' only + ([55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68), + [#15411](https://github.com/angular/angular.js/issues/15411), + [#16335](https://github.com/angular/angular.js/issues/16335)) +- **$compile:** lower the `xlink:href` security context for SVG's `a` and `image` elements + ([6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd), + [#15736](https://github.com/angular/angular.js/issues/15736)) + + +## Performance Improvements +- **$rootScope:** allow $watchCollection use of expression input watching + ([97b00c](https://github.com/angular/angular.js/commit/97b00ca497676aaff8a803762a9f8c7ff4aa24dd)) +- **ngStyle:** use $watchCollection + ([15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0), + [#15947](https://github.com/angular/angular.js/issues/15947)) +- **$compile:** do not use deepWatch in literal one-way bindings + ([fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727), + [#15301](https://github.com/angular/angular.js/issues/15301)) + + + + +## Breaking Changes + +### **jqLite** due to: + - **[b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**: make removeData() not remove event handlers + +Before this commit `removeData()` invoked on an element removed its event +handlers as well. If you want to trigger a full cleanup of an element, change: + +```js +elem.removeData(); +``` + +to: + +```js +angular.element.cleanData(elem); +``` + +In most cases, though, cleaning up after an element is supposed to be done +only when it's removed from the DOM as well; in such cases the following: + +```js +elem.remove(); +``` + +will remove event handlers as well. + +### **$cookies** due to: + - **[73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**: remove the deprecated $cookieStore factory + +The $cookieStore has been removed. Migrate to the $cookies service. Note that +for object values you need to use the `putObject` & `getObject` methods as +`get`/`put` will not correctly save/retrieve them. + +Before: +```js +$cookieStore.put('name', {key: 'value'}); +$cookieStore.get('name'); // {key: 'value'} +$cookieStore.remove('name'); +``` + +After: +```js +$cookies.putObject('name', {key: 'value'}); +$cookies.getObject('name'); // {key: 'value'} +$cookies.remove('name'); +``` + +### **$resource** due to: + - **[ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**: fix interceptors and success/error callbacks + +If you are not using `success` or `error` callbacks with `$resource`, +your app should not be affected by this change. + +If you are using `success` or `error` callbacks (with or without +response interceptors), one (subtle) difference is that throwing an +error inside the callbacks will not propagate to the returned +`$promise`. Therefore, you should try to use the promises whenever +possible. E.g.: + +```js +// Avoid +User.query(function onSuccess(users) { throw new Error(); }). + $promise. + catch(function onError() { /* Will not be called. */ }); + +// Prefer +User.query(). + $promise. + then(function onSuccess(users) { throw new Error(); }). + catch(function onError() { /* Will be called. */ }); +``` + +Finally, if you are using `success` or `error` callbacks with response +interceptors, the callbacks will now always run _after_ the interceptors +(and wait for them to resolve in case they return a promise). +Previously, the `error` callback was called before the `responseError` +interceptor and the `success` callback was synchronously called after +the `response` interceptor. E.g.: + +```js +var User = $resource('/api/users/:id', {id: '@id'}, { + get: { + method: 'get', + interceptor: { + response: function(response) { + console.log('responseInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseInterceptor-2'); + return response.resource; + }); + }, + responseError: function(response) { + console.log('responseErrorInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseErrorInterceptor-2'); + return $q.reject('Ooops!'); + }); + } + } + } +}); +var onSuccess = function(value) { console.log('successCallback', value); }; +var onError = function(error) { console.log('errorCallback', error); }; + +// Assuming the following call is successful... +User.get({id: 1}, onSuccess, onError); + // Old behavior: + // responseInterceptor-1 + // successCallback, {/* Promise object */} + // responseInterceptor-2 + // New behavior: + // responseInterceptor-1 + // responseInterceptor-2 + // successCallback, {/* User object */} + +// Assuming the following call returns an error... +User.get({id: 2}, onSuccess, onError); + // Old behavior: + // errorCallback, {/* Response object */} + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // New behavior: + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // errorCallback, Ooops! +``` + + - **[240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**: add support for `request` and `requestError` interceptors (#15674) + +Previously, calling a `$resource` method would synchronously call +`$http`. Now, it will be called asynchronously (regardless if a +`request`/`requestError` interceptor has been defined. + +This is not expected to affect applications at runtime, since the +overall operation is asynchronous already, but may affect assertions in +tests. For example, if you want to assert that `$http` has been called +with specific arguments as a result of a `$resource` call, you now need +to run a `$digest` first, to ensure the (possibly empty) request +interceptor promise has been resolved. + +Before: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +After: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + $rootScope.$digest(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +### **$templateRequest**: + - due to **[c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**: give tpload error the correct namespace + +Previously the `tpload` error was namespaced to `$compile`. If you have +code that matches errors of the form `[$compile:tpload]` it will no +longer run. You should change the code to match +`[$templateRequest:tpload]`. + + - due to **([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**: always return the template that is stored in the cache + +The service now returns the result of `$templateCache.put()` when making a server request to the +template. Previously it would return the content of the response directly. +This now means if you are decorating `$templateCache.put()` to manipulate the template, you will +now get this manipulated result also on the first `$templateRequest` rather than only on subsequent +calls (when the template is retrived from the cache). +In practice this should not affect any apps, as it is unlikely that they rely on the template being +different in the first and subsequent calls. + +### **$animate** due to: + - **[16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**: let cancel() reject the runner promise + +$animate.cancel(runner) now rejects the underlying +promise and calls the catch() handler on the runner +returned by $animate functions (enter, leave, move, +addClass, removeClass, setClass, animate). +Previously it would resolve the promise as if the animation +had ended successfully. + +Example: + +```js +var runner = $animate.addClass('red'); +runner.then(function() { console.log('success')}); +runner.catch(function() { console.log('cancelled')}); + +runner.cancel(); +``` + +Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'. +To migrate, add a catch() handler to your animation runners. + +### **angular.isArray** due to: + - **[e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**: support Array subclasses in `angular.isArray()` + +Previously, `angular.isArray()` was an alias for `Array.isArray()`. +Therefore, objects that prototypally inherit from `Array` where not +considered arrays. Now such objects are considered arrays too. + +This change affects several other methods that use `angular.isArray()` +under the hood, such as `angular.copy()`, `angular.equals()`, +`angular.forEach()`, and `angular.merge()`. + +This in turn affects how dirty checking treats objects that prototypally +inherit from `Array` (e.g. MobX observable arrays). AngularJS will now +be able to handle these objects better when copying or watching. + +### **$sce** : + - due to **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service + +If you use `attrs.$set` for URL attributes (a[href] and img[src]) there will no +longer be any automated sanitization of the value. This is in line with other +programmatic operations, such as writing to the innerHTML of an element. + +If you are programmatically writing URL values to attributes from untrusted +input then you must sanitize it yourself. You could write your own sanitizer or copy +the private `$$sanitizeUri` service. + +Note that values that have been passed through the `$interpolate` service within the +`URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize +these values again. + + - due to **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service + +binding `trustAs()` and the short versions (`trustAsResourceUrl()` et al.) to +`ngSrc`, `ngSrcset`, and `ngHref` will now raise an infinite digest error: + +```js + $scope.imgThumbFn = function(id) { + return $sce.trustAsResourceUrl(someService.someUrl(id)); + }; +``` + +```html + +``` +This is because the `$interpolate` service is now responsible for sanitizing +the attribute value, and its watcher receives a new object from `trustAs()` +on every digest. +To migrate, compute the trusted value only when the input value changes: + +```js + $scope.$watch('imgId', function(id) { + $scope.imgThumb = $sce.trustAsResourceUrl(someService.someUrl(id)); + }); +``` + +```html + +``` + +### **orderBy** due to: + - **[1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**: consider `null` and `undefined` greater than other values + +When using `orderBy` to sort arrays containing `null` values, the `null` values +will be considered "greater than" all other values, except for `undefined`. +Previously, they were sorted as strings. This will result in different (but more +intuitive) sorting order. + +Before: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', null, 'o', 'z', undefined +``` + +After: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', 'o', 'z', null, undefined +``` + +### **ngScenario** due to: + - **[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**: completely remove the angular scenario runner + +The angular scenario runner end-to-end test framework has been +removed from the project and will no longer be available on npm +or bower starting with 1.7.0. +It was deprecated and removed from the documentation in 2014. +Applications that still use it should migrate to +[Protractor](http://www.protractortest.org). +Technically, it should also be possible to continue using an +older version of the scenario runner, as the underlying APIs have +not changed. However, we do not guarantee future compatibility. + +### **form** due to: + - **[223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**: set $submitted to true on child forms when parent is submitted + +Forms will now set $submitted on child forms when they are submitted. +For example: +``` +
+ + + + +
+``` + +Submitting this form will set $submitted on "parentform" and "childform". +Previously, it was only set on "parentform". + +This change was introduced because mixing form and ngForm does not create +logically separate forms, but rather something like input groups. +Therefore, child forms should inherit the submission state from their parent form. + +### **ngAria** due to: + - **[6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**: do not set aria attributes on input[type="hidden"] + +ngAria no longer sets aria-* attributes on input[type="hidden"] with ngModel. +This can affect apps that test for the presence of aria attributes on hidden inputs. +To migrate, remove these assertions. +In actual apps, this should not have a user-facing effect, as the previous behavior +was incorrect, and the new behavior is correct for accessibility. + +### **ngModel, input** due to: + - **[74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**: improve handling of built-in named parsers + +*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month", +"time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and +the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no +longer set `ngModelController.$error.number` and the `ng-invalid-number` class. + +Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and +`ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers +and custom parsers easier. + +### **ngModelOptions** due to: + - **[55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**: add debounce catch-all + allow debouncing 'default' only + +the 'default' key in 'debounce' now only debounces the default event, i.e. the event +that is added as an update trigger by the different input directives automatically. + +Previously, it also applied to other update triggers defined in 'updateOn' that +did not have a corresponding key in the 'debounce'. + +This behavior is now supported via a special wildcard / catch-all key: '*'. + +See the following example: + +Pre-1.7: +'mouseup' is also debounced by 500 milliseconds because 'default' is applied: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500, 'blur': 0 } +} +``` + +1.7: +The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { '*': 500, 'blur': 0 } +} +``` + +In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500 } +} +``` + +### **input\[number\]** due to: + - **[aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**: validate min/max against viewValue + +`input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against +the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`. + +This affects apps that use `$parsers` or `$formatters` to transform the input / model value. + +If you rely on the $modelValue validation, you can overwrite the `min`/`max` validator from a custom directive, as seen in the following example directive definition object: + +``` +{ + restrict: 'A', + require: 'ngModel', + link: function(scope, element, attrs, ctrl) { + var maxValidator = ctrl.$validators.max; + + ctrl.$validators.max = function(modelValue, viewValue) { + return maxValidator(modelValue, modelValue); + }; + } +} +``` + +### **input** due to: + - **[656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**: listen on "change" instead of "click" for radio/checkbox ngModels + +`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event. +Most apps should not be affected, as "change" is automatically fired by browsers after "click" +happens. + +Two scenarios might need migration: + +- Custom click events: + +Before this change, custom click event listeners on radio / checkbox would be called after the +input element and `ngModel` had been updated, unless they were specifically registered before +the built-in click handlers. +After this change, they are called before the input is updated, and can call event.preventDefault() +to prevent the input from updating. + +If an app uses a click event listener that expects ngModel to be updated when it is called, it now +needs to register a change event listener instead. + +- Triggering click events: + +Conventional trigger functions: + +The change event might not be fired when the input element is not attached to the document. This +can happen in **tests** that compile input elements and +trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method, +the change event will not be fired when the input isn't attached to the document. + +Before: + +```js + it('should update the model', inject(function($compile, $rootScope) { + var inputElm = $compile('')($rootScope); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +With this patch, `$rootScope.checkbox` might not be true, because the click event +hasn't triggered the change event. To make the test, work append the inputElm to the app's +`$rootElement`, and the `$rootElement` to the `$document`. + +After: + +```js + it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) { + var inputElm = $compile('')($rootScope); + + $rootElement.append(inputElm); + $document.append($rootElement); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +`triggerHandler()`: + +If you are using this jQuery / jqLite function on the input elements, you don't have to attach +the elements to the document, but instead change the triggered event to "change". This is because +`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite. + +### **ngStyle** due to: + - **[15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**: use $watchCollection + +Previously the use of deep watch by ng-style would trigger styles to be +re-applied when nested state changed. Now only changes to direct +properties of the watched object will trigger changes. + +### **$compile** due to: + - **[38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**: remove the preAssignBindingsEnabled flag + +Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported. +The flag controlled whether bindings were available inside the controller +constructor or only in the `$onInit` hook. The bindings are now no longer +available in the constructor. + +To migrate your code: + +1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you +don't have to do anything to migrate. + +2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you +can remove that statement - since AngularJS 1.6.0 this is the default so your +app should still work even in AngularJS 1.6 after such removal. Afterwards, +migrating to AngularJS 1.7.0 shouldn't require any further action. + +3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need +to first migrate your code so that the flag can be flipped to `false`. The +instructions on how to do that are available in the "Migrating from 1.5 to 1.6" +guide: +https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6 +Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)` +statement. + + - **[6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**: lower the `xlink:href` security context for SVG's `a` and `image` elements + +In the unlikely case that an app relied on RESOURCE_URL whitelisting for the +purpose of binding to the `xlink:href` property of SVG's `` or `` +elements and if the values do not pass the regular URL sanitization, they will +break. + +To fix this you need to ensure that the values used for binding to the affected +`xlink:href` contexts are considered safe URLs, e.g. by whitelisting them in +`$compileProvider`'s `aHrefSanitizationWhitelist` (for `` elements) or +`imgSrcSanitizationWhitelist` (for `` elements). + + - **[fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**: do not use deepWatch in literal one-way bindings + +Previously when a literal value was passed into a directive/component via +one-way binding it would be watched with a deep watcher. + +For example, for ``, a new instance of the array +would be passed into the directive/component (and trigger $onChanges) not +only if `a` changed but also if any sub property of `a` changed such as +`a.b` or `a.b.c.d.e` etc. + +This also means a new but equal value for `a` would NOT trigger such a +change. + +Now literal values use an input-based watch similar to other directive/component +one-way bindings. In this context inputs are the non-constant parts of the +literal. In the example above the input would be `a`. Changes are only +triggered when the inputs to the literal change. + + - **[1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**: add `base[href]` to the list of RESOURCE_URL context attributes + +Previously, `` would not require `baseUrl` to +be trusted as a RESOURCE_URL. Now, `baseUrl` will be sent to `$sce`'s +RESOURCE_URL checks. By default, it will break unless `baseUrl` is of the same +origin as the application document. + +Refer to the +[`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce) +for more info on how to trust a value in a RESOURCE_URL context. + +Also, concatenation in trusted contexts is not allowed, which means that the +following won't work: ``. + +Either construct complex values in a controller (recommended): + +```js +this.baseUrl = '/something/' + this.partialPath; +``` +```html + +``` + +Or use string concatenation in the interpolation expression (not recommended +except for the simplest of cases): + +```html + +``` + +### **ngTouch** due to: + - **[11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**: remove ngClick override, `$touchProvider`, and `$touch` + +The `ngClick` directive from the ngTouch module has been removed, and with it the +corresponding `$touchProvider` and `$touch` service. + +If you have included ngTouch v1.5.0 or higher in your application, and have not +changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch` +service, then there are no migration steps for your code. Otherwise you must remove references to +the provider and service. + +The `ngClick` override directive had been deprecated and by default disabled since v1.5.0, +because of buggy behavior in edge cases, and a general trend to avoid special touch based +overrides of click events. In modern browsers, it should not be necessary to use a touch override +library: + +- Chrome, Firefox, Edge, and Safari remove the 300ms delay when + `` is set. +- Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the + `touch-action` css property is set to `manipulation`. + +You can find out more in these articles: +https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away +https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8 +https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/ + +### **Angular** due to: + - **[1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**: remove `angular.lowercase` and `angular.uppercase` + +The helper functions `angular.lowercase` `and angular.uppercase` have +been removed. + +These functions have been deprecated since 1.5.0. They are internally +used, but should not be exposed as they contain special locale handling +(for Turkish) to maintain internal consistency regardless of user-set locale. + +Developers should generally use the built-ins `toLowerCase` and `toUpperCase` +or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases. + +Further, we generally discourage using the angular.x helpers in application code. + +### **$controller** due to: + - **[e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**: remove instantiating controllers defined on window + +The option to instantiate controllers from constructors on the global `window` object +has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()` +method that could enable this behavior, has been removed. + +This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope +is bad. To migrate, remove the call to $controllerProvider.allowGlobals() in the config, and +register your controller via the Module API or the $controllerProvider, e.g. + +``` +angular.module('myModule', []).controller('myController', function() {...}); + +angular.module('myModule', []).config(function($controllerProvider) { + $controllerProvider.register('myController', function() {...}); +}); + +``` + +### **$rootScope** due to: + - **[c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)**: provide correct value of one-time bindings in watchGroup + +Previously when using `$watchGroup` the entries in `newValues` and +`oldValues` represented the *most recent change of each entry*. + +Now the entries in `oldValues` will always equal the `newValues` of the previous +call of the listener. This means comparing the entries in `newValues` and +`oldValues` can be used to determine which individual expressions changed. + +For example `$scope.$watchGroup(['a', 'b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [1, undefined] | + + +Now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [2, undefined] | + +Note the last call now shows `a === 2` in the `oldValues` array. + +This also makes the `oldValue` of one-time watchers more clear. Previously +the `oldValue` of a one-time watcher would remain `undefined` forever. For +example `$scope.$watchGroup(['a', '::b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [undefined, undefined] | +| `a=b=3` | [3, 2] | [1, undefined] | + +Where now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [1, undefined] | +| `a=b=3` | [3, 2] | [1, 2] | + +### **$interval** due to: + - **[a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**: throw when trying to cancel non-$interval promise + +`$interval.cancel()` will throw an error if called with a promise that +was not generated by `$interval()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // No error; interval NOT canceled. +``` + +After: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $interval(doSomething, 1000, 5); +var newPromise = promise.then(doSomethingElse); +$interval.cancel(promise); // Interval canceled. +``` + +### **$timeout** due to: + - **[336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**: throw when trying to cancel non-$timeout promise + +`$timeout.cancel()` will throw an error if called with a promise that +was not generated by `$timeout()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // No error; timeout NOT canceled. +``` + +After: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $timeout(doSomething, 1000); +var newPromise = promise.then(doSomethingElse); +$timeout.cancel(promise); // Timeout canceled. +``` + + +# 1.7.0-rc.0 maximum-overdrive (2018-04-19) + +## Bug Fixes +- **input:** + - listen on "change" instead of "click" for radio/checkbox ngModels + ([656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e), + [#4516](https://github.com/angular/angular.js/issues/4516), + [#14667](https://github.com/angular/angular.js/issues/14667), + [#14685](https://github.com/angular/angular.js/issues/14685)) +- **input\[number\]:** validate min/max against viewValue + ([aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec), + [#12761](https://github.com/angular/angular.js/issues/12761), + [#16325](https://github.com/angular/angular.js/issues/16325)) +- **jqLite:** make removeData() not remove event handlers + ([b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a), + [#15869](https://github.com/angular/angular.js/issues/15869), + [#16512](https://github.com/angular/angular.js/issues/16512)) +- **$compile:** + - remove the preAssignBindingsEnabled flag + ([38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb), + [#15782](https://github.com/angular/angular.js/issues/15782)) + - add `base[href]` to the list of RESOURCE_URL context attributes + ([1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e), + [#15597](https://github.com/angular/angular.js/issues/15597)) +- **$interval:** throw when trying to cancel non-$interval promise + ([a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$timeout:** throw when trying to cancel non-$timeout promise + ([336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$cookies:** remove the deprecated $cookieStore factory + ([73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77), + [#16465](https://github.com/angular/angular.js/issues/16465)) +- **$resource:** fix interceptors and success/error callbacks + ([ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd), + [#6731](https://github.com/angular/angular.js/issues/6731), + [#9334](https://github.com/angular/angular.js/issues/9334), + [#6865](https://github.com/angular/angular.js/issues/6865), + [#16446](https://github.com/angular/angular.js/issues/16446)) +- **$templateRequest:** + - give tpload error the correct namespace + ([c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)) + - always return the template that is stored in the cache + ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e), + [#16225](https://github.com/angular/angular.js/issues/16225)) +- **$animate:** let cancel() reject the runner promise + ([16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83), + [#14204](https://github.com/angular/angular.js/issues/14204), + [#16373](https://github.com/angular/angular.js/issues/16373)) +- **ngTouch:** + - deprecate the module and its contents + ([67f54b](https://github.com/angular/angular.js/commit/67f54b660038de2b4346b3e76d66a8dc8ccb1f9b), + [#16427](https://github.com/angular/angular.js/issues/16427), + [#16431](https://github.com/angular/angular.js/issues/16431)) + - remove ngClick override, `$touchProvider`, and `$touch` + ([11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50), + [#15761](https://github.com/angular/angular.js/issues/15761), + [#15755](https://github.com/angular/angular.js/issues/15755)) +- **ngScenario:** completely remove the angular scenario runner + ([0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60), + [#9405](https://github.com/angular/angular.js/issues/9405)) +- **form:** set $submitted to true on child forms when parent is submitted + ([223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77), + [#10071](https://github.com/angular/angular.js/issues/10071)) +- **$rootScope:** + - provide correct value of one-time bindings in watchGroup + ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)) +- **ngAria:** do not set aria attributes on input[type="hidden"] + ([6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b), + [#15113](https://github.com/angular/angular.js/issues/15113), + [#16367](https://github.com/angular/angular.js/issues/16367)) +- **ngModel, input:** improve handling of built-in named parsers + ([74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140), + [#14292](https://github.com/angular/angular.js/issues/14292), + [#10076](https://github.com/angular/angular.js/issues/10076), + [#16347](https://github.com/angular/angular.js/issues/16347)) +- **$httpParamSerializerJQLike:** + - call functions as jQuery does + ([a784fa](https://github.com/angular/angular.js/commit/a784fab605d825f1158c6292b3c42f8c4a502fdf), + [#16138](https://github.com/angular/angular.js/issues/16138), + [#16139](https://github.com/angular/angular.js/issues/16139)) + - follow jQuery for `null` and `undefined` + ([301fdd](https://github.com/angular/angular.js/commit/301fdda648680d89ccab607c413a7ddede7b0165)) +- **$parse:** + - do not pass scope/locals to interceptors of one-time bindings + ([87a586](https://github.com/angular/angular.js/commit/87a586eb9a23cfd0d0bb681cc778b4b8e5c8451d)) + - always pass the intercepted value to watchers + ([2ee503](https://github.com/angular/angular.js/commit/2ee5033967d5f87a516bad137686b0592e25d26b), + [#16021](https://github.com/angular/angular.js/issues/16021)) + - respect the interceptor.$stateful flag + ([de7403](https://github.com/angular/angular.js/commit/de74034ddf6f92505ccdb61be413a6df2c723f87)) +- **Angular:** remove `angular.lowercase` and `angular.uppercase` + ([1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de), + [#15445](https://github.com/angular/angular.js/issues/15445)) +- **$controller:** remove instantiating controllers defined on window + ([e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16), + [#15349](https://github.com/angular/angular.js/issues/15349), + [#15762](https://github.com/angular/angular.js/issues/15762)) + + +## New Features +- **angular.isArray:** support Array subclasses in `angular.isArray()` + ([e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948), + [#15533](https://github.com/angular/angular.js/issues/15533), + [#15541](https://github.com/angular/angular.js/issues/15541)) +- **$sce:** handle URL sanitization through the `$sce` service + ([1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)) +- **orderBy:** consider `null` and `undefined` greater than other values + ([1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8), + [#15294](https://github.com/angular/angular.js/issues/15294), + [#16376](https://github.com/angular/angular.js/issues/16376)) +- **$resource:** add support for `request` and `requestError` interceptors (#15674) + ([240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded), + [#5146](https://github.com/angular/angular.js/issues/5146)) +- **ngModelOptions:** add debounce catch-all + allow debouncing 'default' only + ([55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68), + [#15411](https://github.com/angular/angular.js/issues/15411), + [#16335](https://github.com/angular/angular.js/issues/16335)) +- **$compile:** lower the `xlink:href` security context for SVG's `a` and `image` elements + ([6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd), + [#15736](https://github.com/angular/angular.js/issues/15736)) + + +## Performance Improvements +- **$rootScope:** allow $watchCollection use of expression input watching + ([97b00c](https://github.com/angular/angular.js/commit/97b00ca497676aaff8a803762a9f8c7ff4aa24dd)) +- **ngStyle:** use $watchCollection + ([15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0), + [#15947](https://github.com/angular/angular.js/issues/15947)) +- **$compile:** do not use deepWatch in literal one-way bindings + ([fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727), + [#15301](https://github.com/angular/angular.js/issues/15301)) + + + + +## Breaking Changes + +### **jqLite** due to: + - **[b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**: make removeData() not remove event handlers + +Before this commit `removeData()` invoked on an element removed its event +handlers as well. If you want to trigger a full cleanup of an element, change: + +```js +elem.removeData(); +``` + +to: + +```js +angular.element.cleanData(elem); +``` + +In most cases, though, cleaning up after an element is supposed to be done +only when it's removed from the DOM as well; in such cases the following: + +```js +elem.remove(); +``` + +will remove event handlers as well. + +### **$cookies** due to: + - **[73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**: remove the deprecated $cookieStore factory + +The $cookieStore has been removed. Migrate to the $cookies service. Note that +for object values you need to use the `putObject` & `getObject` methods as +`get`/`put` will not correctly save/retrieve them. + +Before: +```js +$cookieStore.put('name', {key: 'value'}); +$cookieStore.get('name'); // {key: 'value'} +$cookieStore.remove('name'); +``` + +After: +```js +$cookies.putObject('name', {key: 'value'}); +$cookies.getObject('name'); // {key: 'value'} +$cookies.remove('name'); +``` + +### **$resource** due to: + - **[ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**: fix interceptors and success/error callbacks + +If you are not using `success` or `error` callbacks with `$resource`, +your app should not be affected by this change. + +If you are using `success` or `error` callbacks (with or without +response interceptors), one (subtle) difference is that throwing an +error inside the callbacks will not propagate to the returned +`$promise`. Therefore, you should try to use the promises whenever +possible. E.g.: + +```js +// Avoid +User.query(function onSuccess(users) { throw new Error(); }). + $promise. + catch(function onError() { /* Will not be called. */ }); + +// Prefer +User.query(). + $promise. + then(function onSuccess(users) { throw new Error(); }). + catch(function onError() { /* Will be called. */ }); +``` + +Finally, if you are using `success` or `error` callbacks with response +interceptors, the callbacks will now always run _after_ the interceptors +(and wait for them to resolve in case they return a promise). +Previously, the `error` callback was called before the `responseError` +interceptor and the `success` callback was synchronously called after +the `response` interceptor. E.g.: + +```js +var User = $resource('/api/users/:id', {id: '@id'}, { + get: { + method: 'get', + interceptor: { + response: function(response) { + console.log('responseInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseInterceptor-2'); + return response.resource; + }); + }, + responseError: function(response) { + console.log('responseErrorInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseErrorInterceptor-2'); + return $q.reject('Ooops!'); + }); + } + } + } +}); +var onSuccess = function(value) { console.log('successCallback', value); }; +var onError = function(error) { console.log('errorCallback', error); }; + +// Assuming the following call is successful... +User.get({id: 1}, onSuccess, onError); + // Old behavior: + // responseInterceptor-1 + // successCallback, {/* Promise object */} + // responseInterceptor-2 + // New behavior: + // responseInterceptor-1 + // responseInterceptor-2 + // successCallback, {/* User object */} + +// Assuming the following call returns an error... +User.get({id: 2}, onSuccess, onError); + // Old behavior: + // errorCallback, {/* Response object */} + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // New behavior: + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // errorCallback, Ooops! +``` + + - **[240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**: add support for `request` and `requestError` interceptors (#15674) + +Previously, calling a `$resource` method would synchronously call +`$http`. Now, it will be called asynchronously (regardless if a +`request`/`requestError` interceptor has been defined. + +This is not expected to affect applications at runtime, since the +overall operation is asynchronous already, but may affect assertions in +tests. For example, if you want to assert that `$http` has been called +with specific arguments as a result of a `$resource` call, you now need +to run a `$digest` first, to ensure the (possibly empty) request +interceptor promise has been resolved. + +Before: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +After: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + $rootScope.$digest(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +### **$templateRequest**: + - due to **[c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**: give tpload error the correct namespace + +Previously the `tpload` error was namespaced to `$compile`. If you have +code that matches errors of the form `[$compile:tpload]` it will no +longer run. You should change the code to match +`[$templateRequest:tpload]`. + + - due to **([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**: always return the template that is stored in the cache + +The service now returns the result of `$templateCache.put()` when making a server request to the +template. Previously it would return the content of the response directly. +This now means if you are decorating `$templateCache.put()` to manipulate the template, you will +now get this manipulated result also on the first `$templateRequest` rather than only on subsequent +calls (when the template is retrived from the cache). +In practice this should not affect any apps, as it is unlikely that they rely on the template being +different in the first and subsequent calls. + +### **$animate** due to: + - **[16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**: let cancel() reject the runner promise + +$animate.cancel(runner) now rejects the underlying +promise and calls the catch() handler on the runner +returned by $animate functions (enter, leave, move, +addClass, removeClass, setClass, animate). +Previously it would resolve the promise as if the animation +had ended successfully. + +Example: + +```js +var runner = $animate.addClass('red'); +runner.then(function() { console.log('success')}); +runner.catch(function() { console.log('cancelled')}); + +runner.cancel(); +``` + +Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'. +To migrate, add a catch() handler to your animation runners. + +### **angular.isArray** due to: + - **[e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**: support Array subclasses in `angular.isArray()` + +Previously, `angular.isArray()` was an alias for `Array.isArray()`. +Therefore, objects that prototypally inherit from `Array` where not +considered arrays. Now such objects are considered arrays too. + +This change affects several other methods that use `angular.isArray()` +under the hood, such as `angular.copy()`, `angular.equals()`, +`angular.forEach()`, and `angular.merge()`. + +This in turn affects how dirty checking treats objects that prototypally +inherit from `Array` (e.g. MobX observable arrays). AngularJS will now +be able to handle these objects better when copying or watching. + +### **$sce** due to: + - **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service + +If you use `attrs.$set` for URL attributes (a[href] and img[src]) there will no +longer be any automated sanitization of the value. This is in line with other +programmatic operations, such as writing to the innerHTML of an element. + +If you are programmatically writing URL values to attributes from untrusted +input then you must sanitize it yourself. You could write your own sanitizer or copy +the private `$$sanitizeUri` service. + +Note that values that have been passed through the `$interpolate` service within the +`URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize +these values again. + +### **orderBy** due to: + - **[1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**: consider `null` and `undefined` greater than other values + +When using `orderBy` to sort arrays containing `null` values, the `null` values +will be considered "greater than" all other values, except for `undefined`. +Previously, they were sorted as strings. This will result in different (but more +intuitive) sorting order. + +Before: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', null, 'o', 'z', undefined +``` + +After: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', 'o', 'z', null, undefined +``` + +### **ngScenario** due to: + - **[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**: completely remove the angular scenario runner + +The angular scenario runner end-to-end test framework has been +removed from the project and will no longer be available on npm +or bower starting with 1.7.0. +It was deprecated and removed from the documentation in 2014. +Applications that still use it should migrate to +[Protractor](http://www.protractortest.org). +Technically, it should also be possible to continue using an +older version of the scenario runner, as the underlying APIs have +not changed. However, we do not guarantee future compatibility. + +### **form** due to: + - **[223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**: set $submitted to true on child forms when parent is submitted + +Forms will now set $submitted on child forms when they are submitted. +For example: +``` +
+ + + + +
+``` + +Submitting this form will set $submitted on "parentform" and "childform". +Previously, it was only set on "parentform". + +This change was introduced because mixing form and ngForm does not create +logically separate forms, but rather something like input groups. +Therefore, child forms should inherit the submission state from their parent form. + +### **ngAria** due to: + - **[6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**: do not set aria attributes on input[type="hidden"] + +ngAria no longer sets aria-* attributes on input[type="hidden"] with ngModel. +This can affect apps that test for the presence of aria attributes on hidden inputs. +To migrate, remove these assertions. +In actual apps, this should not have a user-facing effect, as the previous behavior +was incorrect, and the new behavior is correct for accessibility. + +### **ngModel, input** due to: + - **[74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**: improve handling of built-in named parsers + +*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month", +"time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and +the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no +longer set `ngModelController.$error.number` and the `ng-invalid-number` class. + +Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and +`ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers +and custom parsers easier. + +### **ngModelOptions** due to: + - **[55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**: add debounce catch-all + allow debouncing 'default' only + +the 'default' key in 'debounce' now only debounces the default event, i.e. the event +that is added as an update trigger by the different input directives automatically. + +Previously, it also applied to other update triggers defined in 'updateOn' that +did not have a corresponding key in the 'debounce'. + +This behavior is now supported via a special wildcard / catch-all key: '*'. + +See the following example: + +Pre-1.7: +'mouseup' is also debounced by 500 milliseconds because 'default' is applied: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500, 'blur': 0 } +} +``` + +1.7: +The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { '*': 500, 'blur': 0 } +} +``` + +In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500 } +} +``` + +### **input\[number\]** due to: + - **[aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**: validate min/max against viewValue + +`input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against +the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`. + +This affects apps that use `$parsers` or `$formatters` to transform the input / model value. + +If you rely on the $modelValue validation, you can overwrite the `min`/`max` validator from a custom directive, as seen in the following example directive definition object: + +``` +{ + restrict: 'A', + require: 'ngModel', + link: function(scope, element, attrs, ctrl) { + var maxValidator = ctrl.$validators.max; + + ctrl.$validators.max = function(modelValue, viewValue) { + return maxValidator(modelValue, modelValue); + }; + } +} +``` + +### **input** due to: + - **[656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**: listen on "change" instead of "click" for radio/checkbox ngModels + +`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event. +Most apps should not be affected, as "change" is automatically fired by browsers after "click" +happens. + +Two scenarios might need migration: + +- Custom click events: + +Before this change, custom click event listeners on radio / checkbox would be called after the +input element and `ngModel` had been updated, unless they were specifically registered before +the built-in click handlers. +After this change, they are called before the input is updated, and can call event.preventDefault() +to prevent the input from updating. + +If an app uses a click event listener that expects ngModel to be updated when it is called, it now +needs to register a change event listener instead. + +- Triggering click events: + +Conventional trigger functions: + +The change event might not be fired when the input element is not attached to the document. This +can happen in **tests** that compile input elements and +trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method, +the change event will not be fired when the input isn't attached to the document. + +Before: + +```js + it('should update the model', inject(function($compile, $rootScope) { + var inputElm = $compile('')($rootScope); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +With this patch, `$rootScope.checkbox` might not be true, because the click event +hasn't triggered the change event. To make the test, work append the inputElm to the app's +`$rootElement`, and the `$rootElement` to the `$document`. + +After: + +```js + it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) { + var inputElm = $compile('')($rootScope); + + $rootElement.append(inputElm); + $document.append($rootElement); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +`triggerHandler()`: + +If you are using this jQuery / jqLite function on the input elements, you don't have to attach +the elements to the document, but instead change the triggered event to "change". This is because +`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite. + +### **ngStyle** due to: + - **[15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**: use $watchCollection + +Previously the use of deep watch by ng-style would trigger styles to be +re-applied when nested state changed. Now only changes to direct +properties of the watched object will trigger changes. + +### **$compile** due to: + - **[38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**: remove the preAssignBindingsEnabled flag + +Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported. +The flag controlled whether bindings were available inside the controller +constructor or only in the `$onInit` hook. The bindings are now no longer +available in the constructor. + +To migrate your code: + +1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you +don't have to do anything to migrate. + +2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you +can remove that statement - since AngularJS 1.6.0 this is the default so your +app should still work even in AngularJS 1.6 after such removal. Afterwards, +migrating to AngularJS 1.7.0 shouldn't require any further action. + +3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need +to first migrate your code so that the flag can be flipped to `false`. The +instructions on how to do that are available in the "Migrating from 1.5 to 1.6" +guide: +https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6 +Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)` +statement. + + - **[6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**: lower the `xlink:href` security context for SVG's `a` and `image` elements + +In the unlikely case that an app relied on RESOURCE_URL whitelisting for the +purpose of binding to the `xlink:href` property of SVG's `` or `` +elements and if the values do not pass the regular URL sanitization, they will +break. + +To fix this you need to ensure that the values used for binding to the affected +`xlink:href` contexts are considered safe URLs, e.g. by whitelisting them in +`$compileProvider`'s `aHrefSanitizationWhitelist` (for `` elements) or +`imgSrcSanitizationWhitelist` (for `` elements). + + - **[fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**: do not use deepWatch in literal one-way bindings + +Previously when a literal value was passed into a directive/component via +one-way binding it would be watched with a deep watcher. + +For example, for ``, a new instance of the array +would be passed into the directive/component (and trigger $onChanges) not +only if `a` changed but also if any sub property of `a` changed such as +`a.b` or `a.b.c.d.e` etc. + +This also means a new but equal value for `a` would NOT trigger such a +change. + +Now literal values use an input-based watch similar to other directive/component +one-way bindings. In this context inputs are the non-constant parts of the +literal. In the example above the input would be `a`. Changes are only +triggered when the inputs to the literal change. + + - **[1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**: add `base[href]` to the list of RESOURCE_URL context attributes + +Previously, `` would not require `baseUrl` to +be trusted as a RESOURCE_URL. Now, `baseUrl` will be sent to `$sce`'s +RESOURCE_URL checks. By default, it will break unless `baseUrl` is of the same +origin as the application document. + +Refer to the +[`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce) +for more info on how to trust a value in a RESOURCE_URL context. + +Also, concatenation in trusted contexts is not allowed, which means that the +following won't work: ``. + +Either construct complex values in a controller (recommended): + +```js +this.baseUrl = '/something/' + this.partialPath; +``` +```html + +``` + +Or use string concatenation in the interpolation expression (not recommended +except for the simplest of cases): + +```html + +``` + +### **ngTouch** due to: + - **[11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**: remove ngClick override, `$touchProvider`, and `$touch` + +The `ngClick` directive from the ngTouch module has been removed, and with it the +corresponding `$touchProvider` and `$touch` service. + +If you have included ngTouch v1.5.0 or higher in your application, and have not +changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch` +service, then there are no migration steps for your code. Otherwise you must remove references to +the provider and service. + +The `ngClick` override directive had been deprecated and by default disabled since v1.5.0, +because of buggy behavior in edge cases, and a general trend to avoid special touch based +overrides of click events. In modern browsers, it should not be necessary to use a touch override +library: + +- Chrome, Firefox, Edge, and Safari remove the 300ms delay when + `` is set. +- Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the + `touch-action` css property is set to `manipulation`. + +You can find out more in these articles: +https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away +https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8 +https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/ + +### **Angular** due to: + - **[1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**: remove `angular.lowercase` and `angular.uppercase` + +The helper functions `angular.lowercase` `and angular.uppercase` have +been removed. + +These functions have been deprecated since 1.5.0. They are internally +used, but should not be exposed as they contain special locale handling +(for Turkish) to maintain internal consistency regardless of user-set locale. + +Developers should generally use the built-ins `toLowerCase` and `toUpperCase` +or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases. + +Further, we generally discourage using the angular.x helpers in application code. + +### **$controller** due to: + - **[e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**: remove instantiating controllers defined on window + +The option to instantiate controllers from constructors on the global `window` object +has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()` +method that could enable this behavior, has been removed. + +This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope +is bad. To migrate, remove the call to $controllerProvider.allowGlobals() in the config, and +register your controller via the Module API or the $controllerProvider, e.g. + +``` +angular.module('myModule', []).controller('myController', function() {...}); + +angular.module('myModule', []).config(function($controllerProvider) { + $controllerProvider.register('myController', function() {...}); +}); + +``` + +### **$rootScope** due to: + - **[c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)**: provide correct value of one-time bindings in watchGroup + +Previously when using `$watchGroup` the entries in `newValues` and +`oldValues` represented the *most recent change of each entry*. + +Now the entries in `oldValues` will always equal the `newValues` of the previous +call of the listener. This means comparing the entries in `newValues` and +`oldValues` can be used to determine which individual expressions changed. + +For example `$scope.$watchGroup(['a', 'b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [1, undefined] | + + +Now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [2, undefined] | + +Note the last call now shows `a === 2` in the `oldValues` array. + +This also makes the `oldValue` of one-time watchers more clear. Previously +the `oldValue` of a one-time watcher would remain `undefined` forever. For +example `$scope.$watchGroup(['a', '::b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [undefined, undefined] | +| `a=b=3` | [3, 2] | [1, undefined] | + +Where now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [1, undefined] | +| `a=b=3` | [3, 2] | [1, 2] | + +### **$interval** due to: + - **[a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**: throw when trying to cancel non-$interval promise + +`$interval.cancel()` will throw an error if called with a promise that +was not generated by `$interval()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // No error; interval NOT canceled. +``` + +After: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $interval(doSomething, 1000, 5); +var newPromise = promise.then(doSomethingElse); +$interval.cancel(promise); // Interval canceled. +``` + +### **$timeout** due to: + - **[336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**: throw when trying to cancel non-$timeout promise + +`$timeout.cancel()` will throw an error if called with a promise that +was not generated by `$timeout()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // No error; timeout NOT canceled. +``` + +After: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $timeout(doSomething, 1000); +var newPromise = promise.then(doSomethingElse); +$timeout.cancel(promise); // Timeout canceled. +``` + + + +# 1.6.10 crystalline-persuasion (2018-04-17) + +## Bug Fixes +- **$compile:** + - correctly handle `null`/`undefined` href `attrs.$set()` + ([f04e04](https://github.com/angular/angular.js/commit/f04e04e0e63e0d30c29718abd5cae634901793b2), + [#16520](https://github.com/angular/angular.js/issues/16520)) + - throw error in `$onChanges` immediately + ([b7d1e0fbd](https://github.com/angular/angular.js/commit/983e27b628fd1eab653e2b3966d90a270f27cc93), + [#15578](https://github.com/angular/angular.js/issues/15578), + [#16492](https://github.com/angular/angular.js/issues/16492)) +- **input:** + - allow overriding timezone for date input types + ([4355de](https://github.com/angular/angular.js/commit/4355dee21d26667bb7f6f21bf75c081351315033), + [#16181](https://github.com/angular/angular.js/issues/16181), + [#13382](https://github.com/angular/angular.js/issues/13382), + [#16336](https://github.com/angular/angular.js/issues/16336)) + - take timezone into account when validating minimum and maximum in date types + ([2f0ac6](https://github.com/angular/angular.js/commit/2f0ac696cb09aec3e291bb8c9c8a1092cbe3a061), + [#16342](https://github.com/angular/angular.js/issues/16342), + [#16390](https://github.com/angular/angular.js/issues/16390)) + - fix composition mode in IE for Korean input + ([9a1b7c](https://github.com/angular/angular.js/commit/9a1b7c9fa135d1dae3f9b4ccf48f081675796e92), + [#6656](https://github.com/angular/angular.js/issues/6656), + [#16273](https://github.com/angular/angular.js/issues/16273)) +- **jqLite:** use XHTML-compliant HTML as input for jqLite + ([a0c55a](https://github.com/angular/angular.js/commit/a0c55af9858075ab268a88dd7a4464788a46f4b7), + [#6917](https://github.com/angular/angular.js/issues/6917), + [#16518](https://github.com/angular/angular.js/issues/16518)) +- **minErr:** update url to https + ([52e466](https://github.com/angular/angular.js/commit/52e46683bfcc0ce0dc9a3d2ee42b389508423799)) +- **$http:** set correct xhrStatus in response when using 'timeout' + ([1faf7e](https://github.com/angular/angular.js/commit/1faf7ec30d55bba107b18efbcf0ef07732c55b91)) +- **browserTrigger:** support CompositionEvent + ([c33fd1](https://github.com/angular/angular.js/commit/c33fd1325417fdc6d7d6abc90cd935130653b149)) + + +## New Features +- **$http:** support sending XSRF token to whitelisted origins + ([bc7757](https://github.com/angular/angular.js/commit/bc775759c88b2221c2bb71d2335bc233c93f43b0), + [#7862](https://github.com/angular/angular.js/issues/7862)) +- **minErr:** strip error url from error parameters + ([980b69](https://github.com/angular/angular.js/commit/980b69dcae73dd8a3d0b9d91b63fa7711cd0ba36)) +- **$sanitize:** support enhancing elements/attributes white-lists + ([ee8e05](https://github.com/angular/angular.js/commit/ee8e05cfafe086188fc318ed4115fb56ba335112), + [#5900](https://github.com/angular/angular.js/issues/5900), + [#16326](https://github.com/angular/angular.js/issues/16326)) +- **$rootScope:** allow suspending and resuming watchers on scope + ([efb822c58](https://github.com/angular/angular.js/commit/41d5c90f170cc054b0f8f88220c22ef1ef6cc0a6), + [#16308](https://github.com/angular/angular.js/issues/5301)) + + +# 1.6.9 fiery-basilisk (2018-02-02) + + +## Bug Fixes +- **input:** add `drop` event support for IE + ([5dc076](https://github.com/angular/angular.js/commit/5dc07667de00c5e85fd69c5b7b7fe4fb5fd65a77)) +- **ngMessages:** prevent memory leak from messages that are never attached + ([9d058d](https://github.com/angular/angular.js/commit/9d058de04bb78694b83179e9b97bc40214eca01a), + [#16389](https://github.com/angular/angular.js/issues/16389), + [#16404](https://github.com/angular/angular.js/issues/16404), + [#16406](https://github.com/angular/angular.js/issues/16406)) +- **ngTransclude:** remove terminal: true + ([1d826e](https://github.com/angular/angular.js/commit/1d826e2f1e941d14c3c56d7a0249f5796ba11f85), + [#16411](https://github.com/angular/angular.js/issues/16411), + [#16412](https://github.com/angular/angular.js/issues/16412)) +- **$sanitize:** sanitize `xml:base` attributes + ([b9ef65](https://github.com/angular/angular.js/commit/b9ef6585e10477fbbf912a971fe0b390bca692a6)) + + +## New Features +- **currencyFilter:** trim whitespace around an empty currency symbol + ([367390](https://github.com/angular/angular.js/commit/3673909896efb6ff47546caf7fc61549f193e043), + [#15018](https://github.com/angular/angular.js/issues/15018), + [#15085](https://github.com/angular/angular.js/issues/15085), + [#15105](https://github.com/angular/angular.js/issues/15105)) + + + +# 1.6.8 beneficial-tincture (2017-12-18) + + +## Bug Fixes +- **$location:** + - always decode special chars in `$location.url(https://github.com/value)` + ([2bdf71](https://github.com/angular/angular.js/commit/2bdf7126878c87474bb7588ce093d0a3c57b0026)) + - decode non-component special chars in Hashbang URLS + ([57b626](https://github.com/angular/angular.js/commit/57b626a673b7530399d3377dfe770165bec35f8a)) +- **ngModelController:** allow $overrideModelOptions to set updateOn + ([55516d](https://github.com/angular/angular.js/commit/55516da2dfc7c5798dce24e9fa930c5ac90c900c), + [#16351](https://github.com/angular/angular.js/issues/16351), + [#16364](https://github.com/angular/angular.js/issues/16364)) + + +## New Features +- **$parse:** add a hidden interface to retrieve an expression's AST + ([f33d95](https://github.com/angular/angular.js/commit/f33d95cfcff6fd0270f92a142df8794cca2013ad), + [#16253](https://github.com/angular/angular.js/issues/16253), + [#16260](https://github.com/angular/angular.js/issues/16260)) + + +# 1.6.7 imperial-backstroke (2017-11-24) + + +## Bug Fixes +- **$compile:** sanitize special chars in directive name + ([c4003f](https://github.com/angular/angular.js/commit/c4003fd03489f876b646f06838f4edb576bacf6f), + [#16314](https://github.com/angular/angular.js/issues/16314), + [#16278](https://github.com/angular/angular.js/issues/16278)) +- **$location:** do not decode forward slashes in the path in HTML5 mode + ([e06ebf](https://github.com/angular/angular.js/commit/e06ebfdbb558544602fe9da4d7d98045a965f468), + [#16312](https://github.com/angular/angular.js/issues/16312)) +- **sanitizeUri:** sanitize URIs that contain IDEOGRAPHIC SPACE chars + ([ddeb1d](https://github.com/angular/angular.js/commit/ddeb1df15a23de93eb95dbe202e83e93673e1c4e), + [#16288](https://github.com/angular/angular.js/issues/16288)) +- **$rootScope:** fix potential memory leak when removing scope listeners + ([358a69](https://github.com/angular/angular.js/commit/358a69fa8b89b251ee44e523458d6c7f40b92b2d), + [#16135](https://github.com/angular/angular.js/issues/16135), + [#16161](https://github.com/angular/angular.js/issues/16161)) +- **http:** do not allow encoded callback params in jsonp requests + ([569e90](https://github.com/angular/angular.js/commit/569e906a5818271416ad0b749be2f58dc34938bd)) +- **ngMock:** pass unexpected request failures in `$httpBackend` to the error handler + ([1555a4](https://github.com/angular/angular.js/commit/1555a4911ad5360c145c0ddc8ec6c4bf9a381c13), + [#16150](https://github.com/angular/angular.js/issues/16150), + [#15855](https://github.com/angular/angular.js/issues/15855)) +- **ngAnimate:** don't close transitions when child transitions close + ([1391e9](https://github.com/angular/angular.js/commit/1391e99c7f73795180b792af21ad4402f96e225d), + [#16210](https://github.com/angular/angular.js/issues/16210)) +- **ngMock.browserTrigger:** add 'bubbles' to Transition/Animation Event + ([7a5f06](https://github.com/angular/angular.js/commit/7a5f06d55d123a39bb7b030667fb1ab672939598)) + + +## New Features +- **$sanitize, $compileProvider, linky:** add support for the "sftp" protocol in links + ([a675ea](https://github.com/angular/angular.js/commit/a675ea034366fbb0fcf0d73fed65216aa99bce11), + [#16102](https://github.com/angular/angular.js/issues/16102)) +- **ngModel.NgModelController:** expose $processModelValue to run model -> view pipeline + ([145194](https://github.com/angular/angular.js/commit/14519488ce9218aa891d34e89fc3271fd4ed0f04), + [#3407](https://github.com/angular/angular.js/issues/3407), + [#10764](https://github.com/angular/angular.js/issues/10764), + [#16237](https://github.com/angular/angular.js/issues/16237)) +- **$injector:** ability to load new modules after bootstrapping + ([6e78fe](https://github.com/angular/angular.js/commit/6e78fee73258bb0ae36414f9db2e8734273e481b)) + + +## Performance Improvements +- **jqLite:** + - avoid setting class attribute when not changed + ([9c95f6](https://github.com/angular/angular.js/commit/9c95f6d5e00ee7e054aabb3e363f5bfb3b7b4103)) + - avoid repeated add/removeAttribute in jqLiteRemoveClass + ([cab9eb](https://github.com/angular/angular.js/commit/cab9ebfd5a02e897f802bf6321b8471e4843c5d3), + [#16078](https://github.com/angular/angular.js/issues/16078), + [#16131](https://github.com/angular/angular.js/issues/16131)) + + + +# 1.6.6 interdimensional-cable (2017-08-18) + + +## Bug Fixes +- **$httpParamSerializer:** ignore functions + ([b51ded](https://github.com/angular/angular.js/commit/b51ded67366865f36c5781dd5d9b801488ec95ea), + [#16133](https://github.com/angular/angular.js/issues/16133)) +- **$resource:** do not throw when calling old `$cancelRequest()` + ([009ebe](https://github.com/angular/angular.js/commit/009ebec64c81d11b280c635167050e8906e191c6), + [#16037](https://github.com/angular/angular.js/issues/16037)) +- **$parse:** + - do not shallow-watch computed property keys + ([750465](https://github.com/angular/angular.js/commit/7504656a26202de591e4ac9674333254304edf8a)) + - support constants in computed keys + ([9d6c3f](https://github.com/angular/angular.js/commit/9d6c3f3ec233279885e37a250d25860d5c15f716)) +- **$http:** do not throw error if `Content-Type` is not `application/json` but response is JSON-like + ([2e1163](https://github.com/angular/angular.js/commit/2e1163ef5cb56d1933e8ecd7b74020b9df9c6693), + [#16027](https://github.com/angular/angular.js/issues/16027), + [#16075](https://github.com/angular/angular.js/issues/16075)) + + +## New Features +- **$compile:** add `strictComponentBindingsEnabled()` method + ([3ec181](https://github.com/angular/angular.js/commit/3ec1819b913c8edf0649e06217dbd5920f29f126), + [#16129](https://github.com/angular/angular.js/issues/16129)) +- **$resource:** add resource to response for error interceptors + ([9256db](https://github.com/angular/angular.js/commit/9256dbc4201343ce5cd63a9eadf98da4793f45af), + [#16109](https://github.com/angular/angular.js/issues/16109)) +- **$http:** allow differentiation between XHR completion, error, abort, timeout + ([5e2bc5](https://github.com/angular/angular.js/commit/5e2bc5bbf347a9dfadc08b1514b8be06fd550913), + [#15924](https://github.com/angular/angular.js/issues/15924), + [#15847](https://github.com/angular/angular.js/issues/15847)) + + + +# 1.6.5 toffee-salinization (2017-07-03) + + +## Bug Fixes +- **core:** + - correctly detect Error instances from different contexts + ([6daca0](https://github.com/angular/angular.js/commit/6daca023e42098f7098b9bf153c8e53a17af84f1), + [#15868](https://github.com/angular/angular.js/issues/15868), + [#15872](https://github.com/angular/angular.js/issues/15872)) + - deprecate `angular.merge` + ([dc41f4](https://github.com/angular/angular.js/commit/dc41f465baae9bc91418a61f446596157c530b6e), + [#12653](https://github.com/angular/angular.js/issues/12653), + [#14941](https://github.com/angular/angular.js/issues/14941), + [#15180](https://github.com/angular/angular.js/issues/15180), + [#15992](https://github.com/angular/angular.js/issues/15992), + [#16036](https://github.com/angular/angular.js/issues/16036)) +- **ngOptions:** + - re-render after empty option has been removed + ([510d0f](https://github.com/angular/angular.js/commit/510d0f946fa1a443ad43fa31bc9337676ef31332)) + - allow empty option to be removed and re-added + ([71b4da](https://github.com/angular/angular.js/commit/71b4daa4e10b6912891927ee2a7930c604b538f8)) + - select unknown option if unmatched model does not match empty option + ([17d34b](https://github.com/angular/angular.js/commit/17d34b7a983a0ef63f6cf404490385c696fb0da1)) +- **orderBy:** guarantee stable sort + ([e50ed4](https://github.com/angular/angular.js/commit/e50ed4da9e8177168f67da68bdf02f07da4e7bcf), + [#14881](https://github.com/angular/angular.js/issues/14881), + [#15914](https://github.com/angular/angular.js/issues/15914)) +- **$parse:** + - do not shallow-watch inputs to one-time intercepted expressions + ([6e3b5a](https://github.com/angular/angular.js/commit/6e3b5a57cd921823f3eca7200a79ac5c2ef0567a)) + - standardize one-time literal vs non-literal and interceptors + ([f003d9](https://github.com/angular/angular.js/commit/f003d93a3dd052dccddef41125d9c51034ac3605)) + - do not shallow-watch inputs when wrapped in an interceptor fn + ([aac562](https://github.com/angular/angular.js/commit/aac5623247a86681cbe0e1c8179617b816394c1d), + [#15905](https://github.com/angular/angular.js/issues/15905)) + - always re-evaluate filters within literals when an input is an object + ([ec9768](https://github.com/angular/angular.js/commit/ec97686f2f4a5481cc806462313a664fc7a1c893), + [#15964](https://github.com/angular/angular.js/issues/15964), + [#15990](https://github.com/angular/angular.js/issues/15990)) +- **$sanitize:** use appropriate inert document strategy for Firefox and Safari + ([8f31f1](https://github.com/angular/angular.js/commit/8f31f1ff43b673a24f84422d5c13d6312b2c4d94)) +- **$timeout/$interval:** do not trigger a digest on cancel + ([a222d0](https://github.com/angular/angular.js/commit/a222d0b452622624dc498ef0b9d3c43647fd4fbc), + [#16057](https://github.com/angular/angular.js/issues/16057), + [#16064](https://github.com/angular/angular.js/issues/16064))
+ This change might affect the use of `$timeout.flush()` in unit tests. See the commit message for + more info. +- **ngMock/$interval:** add support for zero-delay intervals in tests + ([a1e3f8](https://github.com/angular/angular.js/commit/a1e3f8728e0a80396f980e48f8dc68dde6721b2b), + [#15952](https://github.com/angular/angular.js/issues/15952), + [#15953](https://github.com/angular/angular.js/issues/15953)) +- **angular-loader:** do not depend on "closure" globals that may not be available + ([a3226d](https://github.com/angular/angular.js/commit/a3226d01fadaf145713518dc5b8022b581c34e81), + [#15880](https://github.com/angular/angular.js/issues/15880), + [#15881](https://github.com/angular/angular.js/issues/15881)) + + +## New Features +- **select:** expose info about selection state in controller + ([0b962d](https://github.com/angular/angular.js/commit/0b962d4881e98327a91c37f7317da557aa991663), + [#13172](https://github.com/angular/angular.js/issues/13172), + [#10127](https://github.com/angular/angular.js/issues/10127)) +- **$animate:** add support for `customFilter` + ([ab114a](https://github.com/angular/angular.js/commit/ab114af8508bdbdb1fa5fd1e070d08818d882e28), + [#14891](https://github.com/angular/angular.js/issues/14891)) +- **$compile:** overload `.component()` to accept object map of components + ([210112](https://github.com/angular/angular.js/commit/2101126ce72308d8fc468ca2411bb9972e614f79), + [#14579](https://github.com/angular/angular.js/issues/14579), + [#16062](https://github.com/angular/angular.js/issues/16062)) +- **$log:** log all parameters in IE 9, not just the first two. + ([3671a4](https://github.com/angular/angular.js/commit/3671a43be43d05b00c90dfb3a3f746c013139581)) +- **ngMock:** describe unflushed http requests + ([d9128e](https://github.com/angular/angular.js/commit/d9128e7b2371ab2bb5169ba854b21c78baa784d2), + [#10596](https://github.com/angular/angular.js/issues/10596), + [#15928](https://github.com/angular/angular.js/issues/15928)) + + +## Performance Improvements +- **ngOptions:** prevent initial options repainting + ([ff52b1](https://github.com/angular/angular.js/commit/ff52b188a759f2cc7ee6ee78a8c646c2354a47eb), + [#15801](https://github.com/angular/angular.js/issues/15801), + [#15812](https://github.com/angular/angular.js/issues/15812), + [#16071](https://github.com/angular/angular.js/issues/16071)) +- **$animate:** + - avoid unnecessary computations if animations are globally disabled + ([ce5ffb](https://github.com/angular/angular.js/commit/ce5ffbf667464bd58eae4c4af0917eb2685f1f6a), + [#14914](https://github.com/angular/angular.js/issues/14914)) + - do not retrieve `className` unless `classNameFilter` is used + ([275978](https://github.com/angular/angular.js/commit/27597887379a1904cd86832602e286894b449a75)) + + + + +# 1.6.4 phenomenal-footnote (2017-03-31) + + +## Bug Fixes +- **$parse:** + - standardize one-time literal vs non-literal and interceptors + ([60394a](https://github.com/angular/angular.js/commit/60394a9d91dad8932fa900af7c8529837f1d4557), + [#15858](https://github.com/angular/angular.js/issues/15858)) + - fix infinite digest errors when watching objects with .valueOf in literals + ([f5ddb1](https://github.com/angular/angular.js/commit/f5ddb10b56676c2ad912ce453acb87f0a7a94e01), + [#15867](https://github.com/angular/angular.js/issues/15867)) +- **ngModel:** prevent internal scope reference from being copied + ([e1f8a6](https://github.com/angular/angular.js/commit/e1f8a6e82bb8a70079ef3db9a891b1c08b5bae31), + [#15833](https://github.com/angular/angular.js/issues/15833)) +- **jqLite:** make jqLite invoke jqLite.cleanData as a method + ([9cde98](https://github.com/angular/angular.js/commit/9cde98cbc770f8d33fc074ba563b7ab6e2baaf8b), + [#15846](https://github.com/angular/angular.js/issues/15846)) +- **$http:** throw more informative error on invalid JSON response + ([df8887](https://github.com/angular/angular.js/commit/df88873bb79213057057adb47151b626a7ec0e5d), + [#15695](https://github.com/angular/angular.js/issues/15695), + [#15724](https://github.com/angular/angular.js/issues/15724)) +- **dateFilter:** correctly handle newlines in `format` string + ([982271](https://github.com/angular/angular.js/commit/9822711ad2a401c2449239edc13d18b301714757), + [#15794](https://github.com/angular/angular.js/issues/15794), + [#15792](https://github.com/angular/angular.js/issues/15792)) + + +## New Features +- **$resource:** add `hasBody` action configuration option + ([a9f987](https://github.com/angular/angular.js/commit/a9f987a0c9653246ea471a89197907d94c0cea2a), + [#10128](https://github.com/angular/angular.js/issues/10128), + [#12181](https://github.com/angular/angular.js/issues/12181)) + + + +# 1.6.3 scriptalicious-bootstrapping (2017-03-08) + + +## Bug Fixes +- **AngularJS:** + - do not auto-bootstrap if the `src` exists but is empty + ([3536e8](https://github.com/angular/angular.js/commit/3536e83d8a085b02bd6dcec8324800b7e6c734e4)) + - do not auto bootstrap if the currentScript has been clobbered + ([95f964](https://github.com/angular/angular.js/commit/95f964b827b6f5b5aab10af54f7831316c7a9935)) + - do not auto-bootstrap if the script source is bad and inside SVG + ([c8f78a](https://github.com/angular/angular.js/commit/c8f78a8ca9debc33a6deaf951f344b8d372bf210)) +- **$log:** don't parse error stacks manually outside of IE/Edge + ([64e5af](https://github.com/angular/angular.js/commit/64e5afc4786fdfd850c6bdb488a5aa2b8b077f74), + [#15590](https://github.com/angular/angular.js/issues/15590), + [#15767](https://github.com/angular/angular.js/issues/15767)) +- **$sanitize:** prevent clobbered elements from freezing the browser + ([3bb1dd](https://github.com/angular/angular.js/commit/3bb1dd5d7f7dcde6fea5a3148f8f10e92f451e9d), + [#15699](https://github.com/angular/angular.js/issues/15699)) +- **$animate:** + - reset `classNameFilter` to `null` when a disallowed RegExp is used + ([a584fb](https://github.com/angular/angular.js/commit/a584fb6e1569fc1dd85e23b251a7c126edc2dd5b), + [#14913](https://github.com/angular/angular.js/issues/14913)) + - improve detection on `ng-animate` in `classNameFilter` RegExp + ([1f1331](https://github.com/angular/angular.js/commit/1f13313f403381581e1c31c57ebfe7a96546c6e4), + [#14806](https://github.com/angular/angular.js/issues/14806)) +- **filterFilter:** don't throw if `key.charAt` is not a function + ([f27d19](https://github.com/angular/angular.js/commit/f27d19ed606bf05ba41698159ebbc5fbc195033e), + [#15644](https://github.com/angular/angular.js/issues/15644), + [#15660](https://github.com/angular/angular.js/issues/15660)) +- **select:** + - add attribute "selected" for `select[multiple]` + ([851367](https://github.com/angular/angular.js/commit/8513674911300b27d518383a905fde9b3f25f7ae)) + - keep original selection when using shift to add options in IE/Edge + ([97b74a](https://github.com/angular/angular.js/commit/97b74ad6fbcbc4b63e37e9eb44962d6f8de83e8b), + [#15675](https://github.com/angular/angular.js/issues/15675), + [#15676](https://github.com/angular/angular.js/issues/15676)) +- **$jsonpCallbacks:** allow `$window` to be mocked in unit tests + ([5ca0de](https://github.com/angular/angular.js/commit/5ca0de64873c32ab2f540a3226e73c4175a15c50), + [#15685](https://github.com/angular/angular.js/issues/15685), + [#15686](https://github.com/angular/angular.js/issues/15686)) + + +## New Features +- **info:** add `angularVersion` info to each module + ([1e582e](https://github.com/angular/angular.js/commit/1e582e4fa486f340150bba95927f1b26d9142de2)) +- **$injector:** add new `modules` property + ([742123](https://github.com/angular/angular.js/commit/7421235f247e5b7113345401bc5727cfbf81ddc2)) +- **Module:** add `info()` method + ([09ba69](https://github.com/angular/angular.js/commit/09ba69078de6ba52c70571b82b6205929f6facc5), + [#15225](https://github.com/angular/angular.js/issues/15225)) +- **errorHandlingConfig:** make the depth for object stringification in errors configurable + ([4a5eaf](https://github.com/angular/angular.js/commit/4a5eaf7bec85ceca8b934ebaff4d1834a1a09f57), + [#15402](https://github.com/angular/angular.js/issues/15402), + [#15433](https://github.com/angular/angular.js/issues/15433)) + + + +# 1.6.2 llamacorn-lovehug (2017-02-07) + + +## Bug Fixes +- **$compile:** + - do not swallow thrown errors in testsg + ([0377c6](https://github.com/angular/angular.js/commit/0377c6f0e890cb4ed3eb020b96720b4b34f75df3), + [#15629](https://github.com/angular/angular.js/issues/15629), + [#15631](https://github.com/angular/angular.js/issues/15631)) + - allow the usage of "$" in isolate scope property alias + ([7f2af3](https://github.com/angular/angular.js/commit/7f2af3f923e7a3f85c8862d0ed57d21c72eae904), + [#15594](https://github.com/angular/angular.js/issues/15594)) +- **$location:** correctly handle external URL change during `$digest` + ([b60761](https://github.com/angular/angular.js/commit/b607618342d6c4fab364966fe05f152be6bd4d5f), + [#11075](https://github.com/angular/angular.js/issues/11075), + [#12571](https://github.com/angular/angular.js/issues/12571), + [#15556](https://github.com/angular/angular.js/issues/15556), + [#15561](https://github.com/angular/angular.js/issues/15561)) +- **$browser:** detect external changes in `history.state` + ([fa50fb](https://github.com/angular/angular.js/commit/fa50fbaf57b3437be7a410ecaba7008dbe0ef239)) +- **$resource:** + - do not swallow errors in `success` callback + ([27146e](https://github.com/angular/angular.js/commit/27146e8a7fad54c1342179b6d291b1b5c2ebe816), + [#15624](https://github.com/angular/angular.js/issues/15624), + [#15628](https://github.com/angular/angular.js/issues/15628)) + - correctly unescape `/\.` even if `\.` comes from a param value + ([419a48](https://github.com/angular/angular.js/commit/419a4813e354496bdf0df44e3f8afaa198df1ab1), + [#15627](https://github.com/angular/angular.js/issues/15627)) + - delete `$cancelRequest()` in `toJSON()` + ([086c5d](https://github.com/angular/angular.js/commit/086c5d0354db8cb3d106b9ff966fb48d6fb46ef8), + [#15244](https://github.com/angular/angular.js/issues/15244)) +- **$animate:** correctly animate transcluded clones with `templateUrl` + ([f01212](https://github.com/angular/angular.js/commit/f01212ab5287ac7a154da7d75037ed444e81eb34), + [#15510](https://github.com/angular/angular.js/issues/15510), + [#15514](https://github.com/angular/angular.js/issues/15514)) +- **$route:** make asynchronous tasks count as pending requests + ([eb968c](https://github.com/angular/angular.js/commit/eb968c4a6884838db05369a04459066424c5bba8), + [#14159](https://github.com/angular/angular.js/issues/14159)) +- **$parse:** make sure ES6 object computed properties are watched + ([5e418b](https://github.com/angular/angular.js/commit/5e418b1145a1045da598c7863e785d647ea83850), + [#15678](https://github.com/angular/angular.js/issues/15678)) +- **$sniffer:** allow `history` for NW.js apps + ([4a593d](https://github.com/angular/angular.js/commit/4a593db79ba1e21a6aa600a82cf6d757cad94d01), + [#15474](https://github.com/angular/angular.js/issues/15474), + [#15633](https://github.com/angular/angular.js/issues/15633)) +- **input:** fix `step` validation for `input[type=number/range]` + ([c95a67](https://github.com/angular/angular.js/commit/c95a6737fbd277e40c064bd9f68f383bf119505c), + [#15504](https://github.com/angular/angular.js/issues/15504), + [#15506](https://github.com/angular/angular.js/issues/15506)) +- **select:** keep `ngModel` when selected option is recreated by `ngRepeat` + ([131af8](https://github.com/angular/angular.js/commit/131af8272d269a541d04cb522c264a91e0ec8b6a), + [#15630](https://github.com/angular/angular.js/issues/15630), + [#15632](https://github.com/angular/angular.js/issues/15632)) +- **ngValue:** correctly update the `value` property when `value` is undefined + ([05aab6](https://github.com/angular/angular.js/commit/05aab660ce74f526f2110d3b5faf9a5b4f4e664b) + [#15603](https://github.com/angular/angular.js/issues/15603), + [#15605](https://github.com/angular/angular.js/issues/15605)) +- **angularInit:** allow auto-bootstrapping from inline script + ([bb464d](https://github.com/angular/angular.js/commit/bb464d16b434b9e2de2fecf80c192d4741cba879), + [#15567](https://github.com/angular/angular.js/issues/15567), + [#15571](https://github.com/angular/angular.js/issues/15571)) +- **ngMockE2E:** ensure that mocked `$httpBackend` uses correct `$browser` + ([bd63b2](https://github.com/angular/angular.js/commit/bd63b2235cd410251cb83eebd9a47d3102830b6b), + [#15593](https://github.com/angular/angular.js/issues/15593)) + + +## New Features +- **ngModel:** add `$overrideModelOptions` support + ([2546c2](https://github.com/angular/angular.js/commit/2546c29f811b68eea4d68be7fa1c8f7bb562dc11), + [#15415](https://github.com/angular/angular.js/issues/15415)) +- **$parse:** allow watching array/object literals with non-primitive values + ([25f008](https://github.com/angular/angular.js/commit/25f008f541d68b09efd7b428b648c6d4899e6972), + [#15301](https://github.com/angular/angular.js/issues/15301)) + + + + +# 1.5.11 princely-quest (2017-01-13) + + +## Bug Fixes +- **$compile:** allow the usage of "$" in isolate scope property alias + ([e75fbc](https://github.com/angular/angular.js/commit/e75fbc494e6a0da6a9231b40bb0382431b62be07), + [#15586](https://github.com/angular/angular.js/issues/15586), + [#15594](https://github.com/angular/angular.js/issues/15594)) +- **angularInit:** allow auto-bootstrapping from inline script + ([41aa91](https://github.com/angular/angular.js/commit/41aa9125b9aaf771addb250642f524a4e6f9d8d3), + [#15567](https://github.com/angular/angular.js/issues/15567), + [#15571](https://github.com/angular/angular.js/issues/15571)) +- **$resource:** delete `$cancelRequest()` in `toJSON()` + ([4f3858](https://github.com/angular/angular.js/commit/4f3858e7c371f87534397f45b9d002add33b00cc), + [#15244](https://github.com/angular/angular.js/issues/15244)) +- **$$cookieReader:** correctly handle forbidden access to `document.cookie` + ([6933cf](https://github.com/angular/angular.js/commit/6933cf64fe51f54b10d1639f2b95bab3c1178df9), + [#15523](https://github.com/angular/angular.js/issues/15523), + [#15532](https://github.com/angular/angular.js/issues/15532)) + + + + +# 1.6.1 promise-rectification (2016-12-23) + + +## Bug Fixes +- **$q:** Add traceback to unhandled promise rejections + ([174cb4](https://github.com/angular/angular.js/commit/174cb4a8c81e25581da5b452c2bb43b0fa377a9b), + [#14631](https://github.com/angular/angular.js/issues/14631)) +- **$$cookieReader:** correctly handle forbidden access to `document.cookie` + ([33f769](https://github.com/angular/angular.js/commit/33f769b0a1214055c16fb59adad4897bf53d62bf), + [#15523](https://github.com/angular/angular.js/issues/15523)) +- **ngOptions:** do not unset the `selected` property unless necessary + ([bc4844](https://github.com/angular/angular.js/commit/bc4844d3b297d80aecef89aa1b32615024decedc), + [#15477](https://github.com/angular/angular.js/issues/15477)) +- **ngModelOptions:** work correctly when on the template of `replace` directives + ([5f8ed6](https://github.com/angular/angular.js/commit/5f8ed63f2ab02ffb9c21bf9c29d27c851d162e26), + [#15492](https://github.com/angular/angular.js/issues/15492)) +- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously + ([d52864](https://github.com/angular/angular.js/commit/d528644fe3e9ffd43999e7fc67806059f9e1083e)) +- **jqLite:** silently ignore `after()` if element has no parent + ([3d68b9](https://github.com/angular/angular.js/commit/3d68b9502848ff6714ef89bfb95b8e70ae34eff6), + [#15331](https://github.com/angular/angular.js/issues/15331), + [#15475](https://github.com/angular/angular.js/issues/15475)) +- **$rootScope:** when adding/removing watchers during $digest + ([163aca](https://github.com/angular/angular.js/commit/163aca336d7586a45255787af41b14b2a12361dd), + [#15422](https://github.com/angular/angular.js/issues/15422)) + + +## Performance Improvements +- **ngClass:** avoid unnecessary `.data()` accesses, deep-watching and copies + ([1d3b65](https://github.com/angular/angular.js/commit/1d3b65adc2c22ff662159ef910089cf10d1edb7b), + [#14404](https://github.com/angular/angular.js/issues/14404)) + + + + +# 1.5.10 asynchronous-synchronization (2016-12-15) + + +## Bug Fixes +- **$compile:** + - don't throw tplrt error when there is whitespace around a top-level comment + ([12752f](https://github.com/angular/angular.js/commit/12752f66ac425ab38a5ee574a4bfbf3516adc42c), + [#15108](https://github.com/angular/angular.js/issues/15108)) + - clean up `@`-binding observers when re-assigning bindings + ([f3cb6e](https://github.com/angular/angular.js/commit/f3cb6e309aa1f676e5951ac745fa886d3581c2f4), + [#15268](https://github.com/angular/angular.js/issues/15268)) + - set attribute value even if `ngAttr*` contains no interpolation + ([229799](https://github.com/angular/angular.js/commit/22979904fb754c59e9f6ee5d8763e3b8de0e18c2), + [#15133](https://github.com/angular/angular.js/issues/15133)) + - `bindToController` should work without `controllerAs` + ([944989](https://github.com/angular/angular.js/commit/9449893763a4fd95ee8ff78b53c6966a874ec9ae), + [#15088](https://github.com/angular/angular.js/issues/15088)) + - do not overwrite values set in `$onInit()` for `<`-bound literals + ([07e1ba](https://github.com/angular/angular.js/commit/07e1ba365fb5e8a049be732bd7b62f71e0aa1672), + [#15118](https://github.com/angular/angular.js/issues/15118)) + - avoid calling `$onChanges()` twice for `NaN` initial values + ([0cf5be](https://github.com/angular/angular.js/commit/0cf5be52642f7e9d81a708b3005042eac6492572)) +- **$location:** prevent infinite digest with IDN urls in Edge + ([4bf892](https://github.com/angular/angular.js/commit/4bf89218130d434771089fdfe643490b8d2ee259), + [#15217](https://github.com/angular/angular.js/issues/15217)) +- **$rootScope:** correctly handle adding/removing watchers during `$digest` + ([a9708d](https://github.com/angular/angular.js/commit/a9708de84b50f06eacda33834d5bbdfc97c97f37), + [#15422](https://github.com/angular/angular.js/issues/15422)) +- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**` + ([78eecb](https://github.com/angular/angular.js/commit/78eecb43dbb0500358d333aea8955bd0646a7790)) +- **jqLite:** silently ignore `after()` if element has no parent + ([77ed85](https://github.com/angular/angular.js/commit/77ed85bcd3be057a5a79231565ac7accc6d644c6), + [#15331](https://github.com/angular/angular.js/issues/15331)) +- **input[radio]:** use non-strict comparison for checkedness + ([593a50](https://github.com/angular/angular.js/commit/593a5034841b3b7661d3bcbdd06b7a9d0876fd34)) +- **select, ngOptions:** + - let `ngValue` take precedence over option text with multiple interpolations + ([5b7ec8](https://github.com/angular/angular.js/commit/5b7ec8c84e88ee08aacaf9404853eda0016093f5), + [#15413](https://github.com/angular/angular.js/issues/15413)) + - don't add comment nodes as empty options + ([1d29c9](https://github.com/angular/angular.js/commit/1d29c91c3429de96e4103533752700d1266741be), + [#15454](https://github.com/angular/angular.js/issues/15454)) +- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously + ([e3d020](https://github.com/angular/angular.js/commit/e3d02070ab8a02c818dcc5114db6fba9d3f385d6)) +- **$sanitize:** reduce stack height in IE <= 11 + ([862dc2](https://github.com/angular/angular.js/commit/862dc2532f8126a4a71fd3d957884ba6f11f591c), + [#14928](https://github.com/angular/angular.js/issues/14928)) +- **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()` + ([75c83f](https://github.com/angular/angular.js/commit/75c83ff3195931859a099f7a95bf81d32abf2eb3)) + + +## New Features +- **bootstrap:** do not bootstrap from unknown schemes with a different origin + ([bdeb33](https://github.com/angular/angular.js/commit/bdeb3392a8719131ab2b993f2a881c43a2860f92), + [#15428](https://github.com/angular/angular.js/issues/15428)) +- **$anchorScroll:** convert numeric hash targets to string + ([a52640](https://github.com/angular/angular.js/commit/a5264090b66ad0cf9a93de84bb7b307868c0edef), + [#14680](https://github.com/angular/angular.js/issues/14680)) +- **$compile:** + - add `preAssignBindingsEnabled` option + ([f86576](https://github.com/angular/angular.js/commit/f86576def44005f180a66e3aa12d6cc73c1ac72c)) + - throw error when directive name or factory function is invalid + ([5c9399](https://github.com/angular/angular.js/commit/5c9399d18ae5cd79e6cf6fc4377d66df00f6fcc7), + [#15056](https://github.com/angular/angular.js/issues/15056)) +- **$controller:** throw when requested controller is not registered + ([9ae793](https://github.com/angular/angular.js/commit/9ae793d8a69afe84370b601e07fc375fc18a576a), + [#14980](https://github.com/angular/angular.js/issues/14980)) +- **$location:** add support for selectively rewriting links based on attribute + ([a4a222](https://github.com/angular/angular.js/commit/a4a22266f127d3b9a6818e6f4754f048e253f693)) +- **$resource:** pass `status`/`statusText` to success callbacks + ([a8da25](https://github.com/angular/angular.js/commit/a8da25c74d2c1f6265f0fafd95bf72c981d9d678), + [#8341](https://github.com/angular/angular.js/issues/8841), + [#8841](https://github.com/angular/angular.js/issues/8841)) +- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator` + ([0e1651](https://github.com/angular/angular.js/commit/0e1651bfd28ba73ebd0e4943d85af48c4506e02c), + [#3410](https://github.com/angular/angular.js/issues/3410), + [#3516](https://github.com/angular/angular.js/issues/3516)) + + +## Performance Improvements +- **all:** don't trigger digests after enter/leave of structural directives + ([c57779](https://github.com/angular/angular.js/commit/c57779d8725493c5853dceda0105dafd5c0e3a7c), + [#15322](https://github.com/angular/angular.js/issues/15322)) +- **$compile:** validate `directive.restrict` property on directive init + ([31d464](https://github.com/angular/angular.js/commit/31d464feef38b1cc950da6c8dccd0f194ebfc68b)) +- **ngOptions:** avoid calls to `element.value` + ([e269ad](https://github.com/angular/angular.js/commit/e269ad1244bc50fee9218f7c18fab3e9ab063aab)) +- **jqLite:** move bind/unbind definitions out of the loop + ([7717b9](https://github.com/angular/angular.js/commit/7717b96e950a5916a5f12fd611c73d3b06a8d717)) + + # 1.6.0 rainbow-tsunami (2016-12-08) @@ -8,14 +2701,23 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** - **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions` ([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa), [#10922](https://github.com/angular/angular.js/issues/10922)) -- **$compile:** set `preAssignBindingsEnabled` to false by default - ([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858), - [#15352](https://github.com/angular/angular.js/issues/15352)) +- **$compile:** + - add `preAssignBindingsEnabled` option + ([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef)) + - set `preAssignBindingsEnabled` to false by default + ([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858), + [#15352](https://github.com/angular/angular.js/issues/15352)) + - throw error when directive name or factory function is invalid + ([53a3bf](https://github.com/angular/angular.js/commit/53a3bf6634600c3aeff092eacc35edf399b27aec) + [#15056](https://github.com/angular/angular.js/issues/15056)) - **jqLite:** - implement `jqLite(f)` as an alias to `jqLite(document).ready(f)` ([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e)) - don't throw for elements with missing `getAttribute` ([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417)) + - don't get/set properties when getting/setting boolean attributes + ([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304), + [#14126](https://github.com/angular/angular.js/issues/14126)) - don't remove a boolean attribute for `.attr(attrName, '')` ([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)) - remove the attribute for `.attr(attribute, null)` @@ -38,6 +2740,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** - JSONP requests now require a trusted resource URL ([6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4), [#11352](https://github.com/angular/angular.js/issues/11352)) +- **$anchorScroll:** convert numeric hash targets to string + ([9062ba](https://github.com/angular/angular.js/commit/9062bae05c002934fe7bfd76043dcc3de9acfde6) + [#14680](https://github.com/angular/angular.js/issues/14680)) - **select:** support values of any type added with `ngValue` ([f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622), [#9842](https://github.com/angular/angular.js/issues/9842)) @@ -51,6 +2756,10 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** [#10597](https://github.com/angular/angular.js/issues/10597)) - allow `ngTrim` to work for `input[type=radio]` ([47724b](https://github.com/angular/angular.js/commit/47724baffe050269385b3481e9a9cf4ab3944b4b)) +- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator` + ([0b221](https://github.com/angular/angular.js/commit/0b22173000596bf4b78f6a90083b994d46164d79) + [#3410](https://github.com/angular/angular.js/issues/3410) + [#3516](https://github.com/angular/angular.js/issues/3516)) - **$interpolate:** use custom `toString()` function if present ([a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe), [#7317](https://github.com/angular/angular.js/issues/7317), @@ -66,18 +2775,30 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** ([c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0), [#13653](https://github.com/angular/angular.js/issues/13653), [#7992](https://github.com/angular/angular.js/issues/7992)) -- **$location:** default hashPrefix to `'!'` - ([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52), - [#13812](https://github.com/angular/angular.js/issues/13812)) +- **$resource:** pass `status`/`statusText` to success callbacks + ([e3a378](https://github.com/angular/angular.js/commit/e3a378e7a329f60f6b48517f83a4f4c9efecb056) + [#8341](https://github.com/angular/angular.js/issues/8841) + [#8841](https://github.com/angular/angular.js/issues/8841)) +- **$location:** + - default hashPrefix to `'!'` + ([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52) + [#13812](https://github.com/angular/angular.js/issues/13812)) + - add support for selectively rewriting links based on attribute + ([3d686a](https://github.com/angular/angular.js/commit/3d686a988dc4373da094cff6905e5b0d8da6afa4)) +- **$controller:** throw when requested controller is not registered + ([eacfe4](https://github.com/angular/angular.js/commit/eacfe4148eb97e550117ed7fd3c37b58537a9f64) + [#14980](https://github.com/angular/angular.js/issues/14980)) ## Security Related - Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/09/angular-16-expression-sandbox-removal.html). -- **bootstrap:** explicitly whitelist URL schemes for bootstrap. (#15427) - ([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53)) +- **bootstrap:** + - explicitly whitelist URL schemes for bootstrap. + ([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53)) - do not bootstrap from unknown schemes with a different origin - ([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1)) + ([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1) + [#15428](https://github.com/angular/angular.js/issues/15428)) - **$compile:** - secure `link[href]` as a `RESOURCE_URL`s in `$sce` ([04cad4](https://github.com/angular/angular.js/commit/04cad41d26ebaf44b5ee0c29a152d61f235f3efa), @@ -87,14 +2808,18 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** ## Bug Fixes -- **$sce:** fix `adjustMatcher` to replace multiple '*' and '**' (#7897) +- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**` ([991a2b](https://github.com/angular/angular.js/commit/991a2b30e00aed1d312e29555e356a795f9e3d62)) - **ngModelOptions:** handle debounce of `updateOn` triggers that are not in debounce list ([789790](https://github.com/angular/angular.js/commit/789790feee4d6c5b1f5d5b18ecb0ccf6edd36fb3)) - **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()` ([7d9a79](https://github.com/angular/angular.js/commit/7d9a791c6a8c80d29d6c84afa287c81f2a307439)) -- **$location:** throw if the path starts with double (back)slashes - ([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004)) +- **$location:** + - prevent infinite digest with IDN URLs in Edge + ([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7) + [#15217](https://github.com/angular/angular.js/issues/15217)) + - throw if the path starts with double (back)slashes + ([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004)) - **core:** do not auto-bootstrap when loaded from an extension. ([0ff10e](https://github.com/angular/angular.js/commit/0ff10e1b56c6b7c4ac465e35c96a5886e294bac5)) - **input[radio]:** use strict comparison when evaluating checked-ness @@ -124,6 +2849,20 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** - don't throw tplrt error when there is a whitespace around a top-level comment ([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284), [#15108](https://github.com/angular/angular.js/issues/15108)) + - clean up `@`-binding observers when re-assigning bindings + ([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0) + [#15268](https://github.com/angular/angular.js/issues/15268)) + - set attribute value even if `ngAttr*` contains no interpolation + ([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842) + [#15133](https://github.com/angular/angular.js/issues/15133)) + - `bindToController` should work without `controllerAs` + ([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e) + [#15088](https://github.com/angular/angular.js/issues/15088)) + - do not overwrite values set in `$onInit()` for `<`-bound literals + ([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9) + [#15118](https://github.com/angular/angular.js/issues/15118)) + - avoid calling `$onChanges()` twice for `NaN` initial values + ([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445)) - disallow linking the same element more than once ([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f)) - correctly merge consecutive text nodes on IE11 @@ -136,14 +2875,14 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** [#5513](https://github.com/angular/angular.js/issues/5513), [#5597](https://github.com/angular/angular.js/issues/5597)) - move check for interpolation of on-event attributes to compile time - ([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4), - [#13267](https://github.com/angular/angular.js/issues/13267)) + ([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4), + [#13267](https://github.com/angular/angular.js/issues/13267)) - **select, ngOptions, ngValue:** - don't add comment nodes as empty options ([245b27](https://github.com/angular/angular.js/commit/245b27101aad129061585252b73652054319ca82), [#15454](https://github.com/angular/angular.js/issues/15454)) - do not throw when removing the element (e.g. via `ngIf`) - ([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f)) + ([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f)) - add/remove selected attribute for selected/unselected options ([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e)) - don't register options when select has no ngModel @@ -158,7 +2897,7 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** ([e6afca](https://github.com/angular/angular.js/commit/e6afca00c9061a3e13b570796ca3ab428c1723a1), [#14031](https://github.com/angular/angular.js/issues/14031)) - **$resource:** - - **$resource:** allow params in `hostname` (except for IPv6 addresses) + - allow params in `hostname` (except for IPv6 addresses) ([752b1e](https://github.com/angular/angular.js/commit/752b1e69b7a8e9c0b908f1980e9c738888f3647c), [#14542](https://github.com/angular/angular.js/issues/14542)) - fulfill promise with the correct value on error @@ -202,6 +2941,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** - **loader:** `module.decorator` order of operations is now irrelevant ([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e), [#12382](https://github.com/angular/angular.js/issues/12382)) +- **$sanitize:** reduce stack height in IE <= 11 + ([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76) + [#14928](https://github.com/angular/angular.js/issues/14928)) - **ngAnimate:** make svg elements work with `classNameFilter` ([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3)) @@ -221,8 +2963,11 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** ([d71dc2](https://github.com/angular/angular.js/commit/d71dc2f5afec230711351e9f160873a41eb60597)) - **injector:** cache the results of the native class detection check ([5ceb5d](https://github.com/angular/angular.js/commit/5ceb5dbfa6d9b6d15232a1f5c767b2f431325948)) -- **$compile:** use strict comparison for `controller === '@'` - ([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f)) +- **$compile:** + - use strict comparison for `controller === '@'` + ([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f)) + - validate `directive.restrict` property on directive init + ([11f273](https://github.com/angular/angular.js/commit/11f2731f72e932615e8ce15e6a73f4ac808cc7e7)) - **$parse:** - Inline constants ([bd7d5f](https://github.com/angular/angular.js/commit/bd7d5f6345439aa2d1da708ffee20b4c565131d4)) @@ -236,10 +2981,10 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.** - **feat($compile): set preAssignBindingsEnabled to false by default ([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858))**: -Previously, `$compileProvider.preAssignBindingsEnabled` was -set to true by default. This means bindings were pre-assigned in component -constructors. In Angular 1.5+ the place to put the initialization logic -relying on bindings being present is the controller `$onInit` method. +Previously, `$compileProvider.preAssignBindingsEnabled` was set to true by default. This means +bindings were pre-assigned on component/directive controller instances (which made them available +inside the constructors). In AngularJS 1.5+ the place to put the initialization logic relying on +bindings being present is the controller's `$onInit` method. To migrate follow the example below: @@ -452,6 +3197,48 @@ var bgColor = elem.css('background-color'); var bgColor = elem.css('backgroundColor'); ``` +- **[7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**: don't get/set properties when getting/setting boolean attributes + +Previously, all boolean attributes were reflected into the corresponding property when calling a +setter and from the corresponding property when calling a getter, even on elements that don't treat +those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to +know when to reflect the property. Note that this browser-level conversion differs between browsers; +if you need to dynamically change the state of an element, you should modify the property, not the +attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed +description about a related change in jQuery 1.9. + +This change aligns jqLite with jQuery 3. To migrate the code follow the example below: + +Before: + +CSS: + +```css +input[checked="checked"] { ... } +``` + +JS: + +```js +elem1.attr('checked', 'checked'); +elem2.attr('checked', false); +``` + +After: + +CSS: + +```css +input:checked { ... } +``` + +JS: + +```js +elem1.prop('checked', true); +elem2.prop('checked', false); +``` + - **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**: don't remove a boolean attribute for `.attr(attrName, '')` @@ -778,7 +3565,7 @@ You configure this list in a module configuration block: ```js appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) { - $sceDelegateProvider.resourceUrlWhiteList([ + $sceDelegateProvider.resourceUrlWhitelist([ // Allow same origin resource loads. 'self', // Allow JSONP calls that match this pattern @@ -875,7 +3662,7 @@ previous behaviour simply add a comment: **Note:** Everything described below affects **IE11 only**. Previously, consecutive text nodes would not get merged if they had no parent. They will now, which -might have unexpectd side effects in the following cases: +might have unexpected side effects in the following cases: 1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly: @@ -1029,7 +3816,7 @@ In cases where `ngView` was loaded asynchronously, `$route` (and its dependencie might also have been instantiated asynchronously. After this change, `$route` (and its dependencies) will - by default - be instantiated early on. -Although this is not expected to have unwanted side-effects in normal application bebavior, it may +Although this is not expected to have unwanted side-effects in normal application behavior, it may affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a request will be made for the default route's template. If not properly "trained", `$httpBackend` will complain about this unexpected request. @@ -1396,7 +4183,7 @@ validation), you can overwrite the built-in `step` validator with a custom direc # 1.5.9 timeturning-lockdown (2016-11-24) This is an interim release primarily to publish some security fixes, in particular a modification to -ensure that Angular 1 can pass the linter checks for Mozilla add-ons. +ensure that AngularJS can pass the linter checks for Mozilla add-ons. ## Security Fixes - **bootstrap:** @@ -1484,7 +4271,7 @@ ensure that Angular 1 can pass the linter checks for Mozilla add-ons. Previously, `$compileProvider.preAssignBindingsEnabled` was set to true by default. This means bindings were pre-assigned in component -constructors. In Angular 1.5+ the place to put the initialization logic +constructors. In AngularJS 1.5+ the place to put the initialization logic relying on bindings being present is the controller `$onInit` method. To migrate follow the example below: @@ -1595,6 +4382,7 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0 - **ngModel:** treat synchronous validators as boolean always ([7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b) [#14734](https://github.com/angular/angular.js/issues/14734)) - **$q:** treat thrown errors as regular rejections ([e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9) [#3174](https://github.com/angular/angular.js/issues/3174) [#15213](https://github.com/angular/angular.js/issues/15213)) - **ngTransclude:** use fallback content if only whitespace is provided ([32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301) [#15077](https://github.com/angular/angular.js/issues/15077)) +- **$location:** prevent infinite digest with IDN URLs in Edge ([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7) [#15217](https://github.com/angular/angular.js/issues/15217)) - **$compile:** - don't throw tplrt error when there is a whitespace around a top-level comment ([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284) [#15108](https://github.com/angular/angular.js/issues/15108)) - disallow linking the same element more than once ([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f)) @@ -1604,6 +4392,20 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0 - don't add leading white-space in attributes for a specific merge case ([305ba1](https://github.com/angular/angular.js/commit/305ba1a3fb3529cb3fdf04c12ac03fbb4f634456)) - don't trim white-space in attributes ([97bbf8](https://github.com/angular/angular.js/commit/97bbf86a1979d099802f0d631c17c54b87563b40) [#5513](https://github.com/angular/angular.js/issues/5513) [#5597](https://github.com/angular/angular.js/issues/5597)) - move check for interpolation of on-event attributes to compile time ([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4) [#13267](https://github.com/angular/angular.js/issues/13267)) + - clean up `@`-binding observers when re-assigning bindings + ([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0) + [#15268](https://github.com/angular/angular.js/issues/15268)) + - set attribute value even if `ngAttr*` contains no interpolation + ([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842) + [#15133](https://github.com/angular/angular.js/issues/15133)) + - `bindToController` should work without `controllerAs` + ([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e) + [#15088](https://github.com/angular/angular.js/issues/15088)) + - do not overwrite values set in `$onInit()` for `<`-bound literals + ([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9) + [#15118](https://github.com/angular/angular.js/issues/15118)) + - avoid calling `$onChanges()` twice for `NaN` initial values + ([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445)) - **select:** - add/remove selected attribute for selected/unselected options ([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e)) - don't register options when select has no ngModel ([e8c2e1](https://github.com/angular/angular.js/commit/e8c2e119758e58e18fe43932d09a8ff9f506aa9d)) @@ -1627,6 +4429,9 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0 - **ngMock/$httpBackend:** fail if a url is provided but is `undefined` ([7551b8](https://github.com/angular/angular.js/commit/7551b8975a91ee286cc2cf4af5e78f924533575e) [#8442](https://github.com/angular/angular.js/issues/8442) [#10934](https://github.com/angular/angular.js/issues/10934)) - **$route:** don't process route change controllers and templates for `redirectTo` routes ([7f4b35](https://github.com/angular/angular.js/commit/7f4b356c2bebb87f0c26b57a20415b004b20bcd1) [#3332](https://github.com/angular/angular.js/issues/3332)) - **loader:** `module.decorator` order of operations is now irrelevant ([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e) [#12382](https://github.com/angular/angular.js/issues/12382)) +- **$sanitize:** reduce stack height in IE <= 11 + ([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76) + [#14928](https://github.com/angular/angular.js/issues/14928)) - **ngAnimate:** make svg elements work with `classNameFilter` ([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3)) @@ -1634,24 +4439,50 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0 - **jqLite:** - implement `jqLite(f)` as an alias to `jqLite(document).ready(f)` ([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e)) - don't throw for elements with missing `getAttribute` ([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417)) + - don't get/set properties when getting/setting boolean attributes ([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304), [#14126](https://github.com/angular/angular.js/issues/14126)) - don't remove a boolean attribute for `.attr(attrName, '')` ([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)) - remove the attribute for `.attr(attribute, null)` ([4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)) - return `[]` for `.val()` on ` @@ -6327,7 +9217,7 @@ In Angular 1.3.x, setting `scope.x = 200` would select the `option` with the val ``` -In Angular 1.4.x, the 'unknown option' will be selected. +In AngularJS 1.4.x, the 'unknown option' will be selected. To remedy this, you can simply initialize the model as a string: `scope.x = '200'`, or if you want to keep the model as a `Number`, you can do the conversion via `$formatters` and `$parsers` on `ngModel`: @@ -10993,7 +13883,7 @@ There are no breaking changes in this release (promise!) [#4437](https://github.com/angular/angular.js/issues/4437), [#4874](https://github.com/angular/angular.js/issues/4874)) - **minErr:** remove references to internal APIs ([94764ee0](https://github.com/angular/angular.js/commit/94764ee08910726db1db7a1101c3001500306dea)) -- **ngIf:** don't create multiple elements when changing from a truthy value to another thruthy value +- **ngIf:** don't create multiple elements when changing from a truthy value to another truthy value ([4612705e](https://github.com/angular/angular.js/commit/4612705ec297bc6ba714cb7a98f1be6aff77c4b8), [#4852](https://github.com/angular/angular.js/issues/4852)) - **urlUtils:** @@ -12079,7 +14969,7 @@ Contains only these fixes cherry-picked from [v1.2.0rc1](#1.2.0rc1). - due to [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56), Interpolations inside DOM event handlers are disallowed. - DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer. + DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer. To migrate the code follow the example below: @@ -14710,7 +17600,7 @@ with the `$route` service ### Docs - rewrite of several major portions of angular.service.*, angular.Array.*, angular.Object.* docs -- added support for [sitemap]((http://docs.angularjs.org/sitemap.xml) to make the docs indexable by +- added support for [sitemap](http://docs.angularjs.org/sitemap.xml) to make the docs indexable by search crawlers - transition of Developer Guide docs from the wiki into docs.angularjs.org - lots of improvements related to formatting of the content of docs.anguarjs.org @@ -14901,7 +17791,7 @@ with the `$route` service ### Breaking changes - we now support ISO 8601 extended format datetime strings (YYYY-MM-DDTHH:mm:ss.SSSZ) as defined - in EcmaScript 5 throughout angular. This means that the following apis switched from + in EcmaScript 5 throughout AngularJS. This means that the following apis switched from YYYY-MM-DDTHH:mm:ssZ to YYYY-MM-DDTHH:mm:ss.SSSZ (note the added millis) when representing dates: - angular.Date.toString - angular.String.fromDate diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..baa757d028ae --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +# Contributor Code of Conduct + +The AngularJS project follows the Code of Conduct defined in [the angular/code-of-conduct repository](https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md). Please read it. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83def1e87ae7..fadd5d01937d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,85 +3,101 @@ We'd love for you to contribute to our source code and to make AngularJS even better than it is today! Here are the guidelines we'd like you to follow: - - [Code of Conduct](#coc) - - [Question or Problem?](#question) - - [Issues and Bugs](#issue) - - [Feature Requests](#feature) - - [Submission Guidelines](#submit) - - [Coding Rules](#rules) - - [Commit Message Guidelines](#commit) - - [Signing the CLA](#cla) - - [Further Info](#info) +* [Code of Conduct](#coc) +* [Questions and Problems](#question) +* [Issues and Bugs](#issue) +* [Feature Requests](#feature) +* [Improving Documentation](#docs) +* [Issue Submission Guidelines](#submit) +* [Pull Request Submission Guidelines](#submit-pr) +* [Signing the CLA](#cla) ## Code of Conduct -Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][coc]. +Help us keep AngularJS open and inclusive. Please read and follow our [Code of Conduct][coc]. -## Got a Question or Problem? +## Questions, Bugs, Features -If you have questions about how to use AngularJS, please direct these to the [Google Group][groups] -discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC][irc] and -[Gitter][gitter]. +### Got a Question or Problem? -## Found an Issue? +Do not open issues for general support questions as we want to keep GitHub issues for bug reports +and feature requests. You've got much better chances of getting your question answered on dedicated +support platforms, the best being [Stack Overflow][stackoverflow]. -If you find a bug in the source code or a mistake in the documentation, you can help us by -submitting an issue to our [GitHub Repository][github]. Even better you can submit a Pull Request -with a fix. +Stack Overflow is a much better place to ask questions since: -**Localization Issues:** Angular.js uses the [Google Closure I18N library] to generate -its own I18N files (the ngLocale module). This means that any changes to these files would be lost -the next time that we import the library. +- there are thousands of people willing to help on Stack Overflow +- questions and answers stay available for public viewing so your question / answer might help + someone else +- Stack Overflow's voting system assures that the best answers are prominently visible. + +To save your and our time, we will systematically close all issues that are requests for general +support and redirect people to the section you are reading right now. + +Other channels for support are: +- the [Google Group][groups] discussion list +- the [AngularJS IRC][irc] +- the [AngularJS Gitter][gitter] + +### Found an Issue or Bug? + +If you find a bug in the source code, you can help us by submitting an issue to our +[GitHub Repository][github]. Even better, you can submit a Pull Request with a fix. + +**Please see the [Submission Guidelines](#submit) below.** + +**Special Note for Localization Issues:** AngularJS uses the [Google Closure I18N library] to +generate its own I18N files (the ngLocale module). This means that any changes to these files +would be lost the next time that we import the library. Since the Closure library i18n data is itself auto-generated from the data of the [Common Locale Data Repository (CLDR)] project, errors in the data should be reported there. See also the [Closure guide to i18n changes]. -**Please see the [Submission Guidelines](#submit) below.** +### Missing a Feature? -## Want a Feature? +You can request a new feature by submitting an issue to our [GitHub Repository][github-issues]. -You can request a new feature by submitting an issue to our [GitHub Repository][github]. If you -would like to implement a new feature then consider what kind of change it is: +If you would like to implement a new feature then consider what kind of change it is: -* **Major Changes** that you wish to contribute to the project should be discussed first on our - [dev mailing list][angular-dev] or [IRC][irc] so that we can better coordinate our efforts, - prevent duplication of work, and help you to craft the change so that it is successfully accepted - into the project. -* **Small Changes** can be crafted and submitted to the [GitHub Repository][github] as a Pull - Request. +* **Major Changes** that you wish to contribute to the project should be discussed first in an + [GitHub issue][github-issues] that clearly outlines the changes and benefits of the feature. +* **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github] + as a Pull Request. See the section about [Pull Request Submission Guidelines](#submit-pr), and + for detailed information the [core development documentation][developers]. +### Want a Doc Fix? -## Want a Doc Fix? +Should you have a suggestion for the documentation, you can open an issue and outline the problem +or improvement you have - however, creating the doc fix yourself is much better! If you want to help improve the docs, it's a good idea to let others know what you're working on to minimize duplication of effort. Create a new issue (or comment on a related existing one) to let others know what you're working on. +If you're making a small change (typo, phrasing) don't worry about filing an issue first. Use the +friendly blue "Improve this doc" button at the top right of the doc page to fork the repository +in-place and make a quick change on the fly. The commit message is preformatted to the right type +and scope, so you only have to add the description. + For large fixes, please build and test the documentation before submitting the PR to be sure you haven't accidentally introduced any layout or formatting issues. You should also make sure that your -commit message starts with "docs" and follows the **[Commit Message Guidelines](#commit)** outlined -below. +commit message follows the **[Commit Message Guidelines][developers.commits]**. -If you're just making a small change, don't worry about filing an issue first. Use the friendly blue -"Improve this doc" button at the top right of the doc page to fork the repository in-place and make -a quick change on the fly. When naming the commit, it is advised to follow the commit message -guidelines below, by starting the commit message with **docs** and referencing the filename. Since -this is not obvious and some changes are made on the fly, this is not strictly necessary and we will -understand if this isn't done the first few times. - -## Submission Guidelines - -### Submitting an Issue +## Issue Submission Guidelines Before you submit your issue search the archive, maybe your question was already answered. If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize the effort we can spend fixing issues and adding new features, by not reporting duplicate issues. -Providing the following information will increase the chances of your issue being dealt with -quickly: + +The "[new issue][github-new-issue]" form contains a number of prompts that you should fill out to +make it easier to understand and categorize the issue. + +In general, providing the following information will increase the chances of your issue being dealt +with quickly: * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps * **Motivation for or Use Case** - explain why this is a bug for you -* **Angular Version(s)** - is it a regression? +* **AngularJS Version(s)** - is it a regression? * **Browsers and Operating System** - is this a problem with all browsers or only specific ones? * **Reproduce the Error** - provide a live example (using [Plunker][plunker] or [JSFiddle][jsfiddle]) or an unambiguous set of steps. @@ -89,41 +105,43 @@ quickly: * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be causing the problem (line of code or commit) -Here is a great example of a well defined issue: https://github.com/angular/angular.js/issues/5069 +Here is a great example of a well defined issue: https://github.com/angular/angular.js/issues/5069. **If you get help, help others. Good karma rulez!** -### Submitting a Pull Request +## Pull Request Submission Guidelines Before you submit your pull request consider the following guidelines: * Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request that relates to your submission. You don't want to duplicate effort. -* Please sign our [Contributor License Agreement (CLA)](#cla) before sending pull - requests. We cannot accept code without this. +* Create the [development environment][developers.setup] * Make your changes in a new git branch: ```shell git checkout -b my-fix-branch master ``` -* Create your patch, **including appropriate test cases**. -* Follow our [Coding Rules](#rules). -* Run the full Angular test suite, as described in the [developer documentation][dev-doc], - and ensure that all tests pass. +* Create your patch commit, **including appropriate test cases**. +* Follow our [Coding Rules][developers.rules]. +* If the changes affect public APIs, change or add relevant [documentation][developers.documentation]. +* Run the AngularJS [unit][developers.tests-unit] and [E2E test][developers.tests-e2e] suites, and ensure that all tests + pass. It is generally sufficient to run the tests only on Chrome, as our continuous integration test will + run the tests on additional browsers. +* Run `yarn grunt eslint` to check that you have followed the automatically enforced coding rules * Commit your changes using a descriptive commit message that follows our - [commit message conventions](#commit) and passes our commit message presubmit hook - (`validate-commit-msg.js`). Adherence to the [commit message conventions](#commit) is required, - because release notes are automatically generated from these messages. + [commit message conventions][developers.commits]. Adherence to the + [commit message conventions][developers.commits] is required, because release notes are + automatically generated from these messages. ```shell git commit -a ``` Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. -* Build your changes locally to ensure all the tests pass: +* Before creating the Pull Request, package and run all tests a last time: ```shell - grunt test + yarn grunt test ``` * Push your branch to GitHub: @@ -132,24 +150,30 @@ Before you submit your pull request consider the following guidelines: git push origin my-fix-branch ``` -In GitHub, send a pull request to `angular:master`. -If we suggest changes, then: +* In GitHub, send a pull request to `angular.js:master`. This will trigger the check of the +[Contributor License Agreement](#cla) and the continuous integration tests. + +* If you find that the continuous integration tests have failed, look into the logs to find out +if your changes caused test failures, the commit message was malformed etc. If you find that the +tests failed or times out for unrelated reasons, you can ping a team member so that the build can be +restarted. + +* If we suggest changes, then: -* Make the required updates. -* Re-run the Angular test suite to ensure tests are still passing. -* Commit your changes to your branch (e.g. `my-fix-branch`). -* Push the changes to your GitHub repository (this will update your Pull Request). + * Make the required updates. + * Re-run the AngularJS test suite to ensure tests are still passing. + * Commit your changes to your branch (e.g. `my-fix-branch`). + * Push the changes to your GitHub repository (this will update your Pull Request). -If the PR gets too outdated we may ask you to rebase and force push to update the PR: + You can also amend the initial commits and force push them to the branch. -```shell -git rebase master -i -git push origin my-fix-branch -f -``` + ```shell + git rebase master -i + git push origin my-fix-branch -f + ``` -_WARNING: Squashing or reverting commits and force-pushing thereafter may remove GitHub comments -on code that were previously made by you or others in your commits. Avoid any form of rebasing -unless necessary._ + This is generally easier to follow, but separate commits are useful if the Pull Request contains + iterations that might be interesting to see side-by-side. That's it! Thank you for your contribution! @@ -182,135 +206,41 @@ from the main (upstream) repository: git pull --ff upstream master ``` -## Coding Rules - -To ensure consistency throughout the source code, keep these rules in mind as you are working: - -* All features or bug fixes **must be tested** by one or more [specs][unit-testing]. -* All public API methods **must be documented** with ngdoc, an extended version of jsdoc (we added - support for markdown and templating via @ngdoc tag). To see how we document our APIs, please check - out the existing ngdocs and see [this wiki page][ngDocs]. -* With the exceptions listed below, we follow the rules contained in - [Google's JavaScript Style Guide][js-style-guide]: - * **Do not use namespaces**: Instead, wrap the entire angular code base in an anonymous closure and - export our API explicitly rather than implicitly. - * Wrap all code at **100 characters**. - * Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypal - inheritance only when absolutely necessary. - * We **love functions and closures** and, whenever possible, prefer them over objects. - * To write concise code that can be better minified, we **use aliases internally** that map to the - external API. See our existing code to see what we mean. - * We **don't go crazy with type annotations** for private internal APIs unless it's an internal API - that is used throughout AngularJS. The best guidance is to do what makes the most sense. - -## Git Commit Guidelines - -We have very precise rules over how our git commit messages can be formatted. This leads to **more -readable messages** that are easy to follow when looking through the **project history**. But also, -we use the git commit messages to **generate the AngularJS change log**. - -The commit message formatting can be added using a typical git workflow or through the use of a CLI -wizard ([Commitizen](https://github.com/commitizen/cz-cli)). To use the wizard, run `yarn run commit` -in your terminal after staging your changes in git. - -### Commit Message Format -Each commit message consists of a **header**, a **body** and a **footer**. The header has a special -format that includes a **type**, a **scope** and a **subject**: +## Signing the Contributor License Agreement (CLA) -``` -(): - - - -
-``` - -The **header** is mandatory and the **scope** of the header is optional. - -Any line of the commit message cannot be longer 100 characters! This allows the message to be easier -to read on GitHub as well as in various git tools. - -### Revert -If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. -In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted. - -### Type -Must be one of the following: - -* **feat**: A new feature -* **fix**: A bug fix -* **docs**: Documentation only changes -* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing - semi-colons, etc) -* **refactor**: A code change that neither fixes a bug nor adds a feature -* **perf**: A code change that improves performance -* **test**: Adding missing or correcting existing tests -* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation - generation - -### Scope -The scope could be anything specifying place of the commit change. For example `$location`, -`$browser`, `$compile`, `$rootScope`, `ngHref`, `ngClick`, `ngView`, etc... - -You can use `*` when the change affects more than a single scope. - -### Subject -The subject contains succinct description of the change: - -* use the imperative, present tense: "change" not "changed" nor "changes" -* don't capitalize first letter -* no dot (.) at the end - -### Body -Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". -The body should include the motivation for the change and contrast this with previous behavior. - -### Footer -The footer should contain any information about **Breaking Changes** and is also the place to -[reference GitHub issues that this commit closes][closing-issues]. - -**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. -The rest of the commit message is then used for this. - -A detailed explanation can be found in this [document][commit-message-format]. - -## Signing the CLA - -Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code -changes to be accepted, the CLA must be signed. It's a quick process, we promise! +Upon submmitting a Pull Request, a friendly bot will ask you to sign our CLA if you haven't done +so before. Unfortunately, this is necessary for documentation changes, too. +It's a quick process, we promise! * For individuals we have a [simple click-through form][individual-cla]. * For corporations we'll need you to [print, sign and one of scan+email, fax or mail the form][corporate-cla]. -## Further Information -You can find out more detailed information about contributing in the -[AngularJS documentation][contributing]. - -[Google Closure I18N library]: https://github.com/google/closure-library/tree/master/closure/goog/i18n -[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev -[closing-issues]: https://help.github.com/articles/closing-issues-via-commit-messages/ +[Closure guide to i18n changes]: https://github.com/google/closure-library/wiki/Internationalization-%28i18n%29-changes-in-Closure-Library [coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md -[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit# -[contribute]: http://docs.angularjs.org/misc/contribute -[contributing]: http://docs.angularjs.org/misc/contribute +[Common Locale Data Repository (CLDR)]: http://cldr.unicode.org [corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html -[dev-doc]: https://docs.angularjs.org/guide +[developers]: DEVELOPERS.md +[developers.commits]: DEVELOPERS.md#commits +[developers.documentation]: DEVELOPERS.md#documentation +[developers.rules]: DEVELOPERS.md#rules +[developers.setup]: DEVELOPERS.md#setup +[developers.tests-e2e]: DEVELOPERS.md#e2e-tests +[developers.tests-unit]: DEVELOPERS.md#unit-tests +[github-issues]: https://github.com/angular/angular.js/issues +[github-new-issue]: https://github.com/angular/angular.js/issues/new [github]: https://github.com/angular/angular.js [gitter]: https://gitter.im/angular/angular.js +[Google Closure I18N library]: https://github.com/google/closure-library/tree/master/closure/goog/i18n [groups]: https://groups.google.com/forum/?fromgroups#!forum/angular [individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html [irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4 -[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml [jsfiddle]: http://jsfiddle.net/ -[list]: https://groups.google.com/forum/?fromgroups#!forum/angular -[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation +[karma-browserstack]: https://github.com/karma-runner/karma-browserstack-launcher +[karma-saucelabs]: https://github.com/karma-runner/karma-sauce-launcher [plunker]: http://plnkr.co/edit [stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs -[unit-testing]: https://docs.angularjs.org/guide/unit-testing -[Common Locale Data Repository (CLDR)]: http://cldr.unicode.org -[Closure guide to i18n changes]: https://github.com/google/closure-library/wiki/Internationalization-%28i18n%29-changes-in-Closure-Library [![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/CONTRIBUTING.md?pixel)](https://github.com/igrigorik/ga-beacon) diff --git a/DEVELOPERS.md b/DEVELOPERS.md new file mode 100644 index 000000000000..cc91b6183dfb --- /dev/null +++ b/DEVELOPERS.md @@ -0,0 +1,488 @@ +# Developing AngularJS + +* [Development Setup](#setup) +* [Running Tests](#tests) +* [Coding Rules](#rules) +* [Commit Message Guidelines](#commits) +* [Writing Documentation](#documentation) + +## Development Setup + +This document describes how to set up your development environment to build and test AngularJS, and +explains the basic mechanics of using `git`, `node`, `yarn` and `grunt`. + +### Installing Dependencies + +Before you can build AngularJS, you must install and configure the following dependencies on your +machine: + +* [Git](http://git-scm.com/): The [Github Guide to + Installing Git][git-setup] is a good source of information. + +* [Node.js v8.x (LTS)](http://nodejs.org): We use Node to generate the documentation, run a + development web server, run tests, and generate distributable files. Depending on your system, + you can install Node either from source or as a pre-packaged bundle. + + We recommend using [nvm](https://github.com/creationix/nvm) (or + [nvm-windows](https://github.com/coreybutler/nvm-windows)) + to manage and install Node.js, which makes it easy to change the version of Node.js per project. + +* [Yarn](https://yarnpkg.com): We use Yarn to install our Node.js module dependencies + (rather than using npm). See the detailed [installation instructions][yarn-install]. + +* [Java](http://www.java.com): We minify JavaScript using + [Closure Tools](https://developers.google.com/closure/), which require Java (version 7 or higher) + to be installed and included in your + [PATH](http://docs.oracle.com/javase/tutorial/essential/environment/paths.html) variable. + +* [Grunt](http://gruntjs.com): We use Grunt as our build system. We're using it as a local dependency, + but you can also add the grunt command-line tool globally (with `yarn global add grunt-cli`), which allows + you to leave out the `yarn` prefix for all our grunt commands. + +### Forking AngularJS on Github + +To contribute code to AngularJS, you must have a GitHub account so you can push code to your own +fork of AngularJS and open Pull Requests in the [GitHub Repository][github]. + +To create a Github account, follow the instructions [here](https://github.com/signup/free). +Afterwards, go ahead and [fork](http://help.github.com/forking) the +[main AngularJS repository][github]. + + +### Building AngularJS + +To build AngularJS, you clone the source code repository and use Grunt to generate the non-minified +and minified AngularJS files: + +```shell +# Clone your Github repository: +git clone https://github.com//angular.js.git + +# Go to the AngularJS directory: +cd angular.js + +# Add the main AngularJS repository as an upstream remote to your repository: +git remote add upstream "https://github.com/angular/angular.js.git" + +# Install JavaScript dependencies: +yarn install + +# Build AngularJS: +yarn grunt package +``` + +**Note:** If you're using Windows, you must use an elevated command prompt (right click, run as +Administrator). This is because `yarn grunt package` creates some symbolic links. + +The build output is in the `build` directory. It consists of the following files and +directories: + +* `angular-.zip` — The complete zip file, containing all of the release build +artifacts. + +* `angular.js` / `angular.min.js` — The regular and minified core AngularJS script file. + +* `angular-*.js` / `angular-*.min.js` — All other AngularJS module script files. + +* `docs/` — A directory that contains a standalone version of the docs + (same as served in `docs.angularjs.org`). + +### Running a Local Development Web Server + +To debug code, run end-to-end tests, and serve the docs, it is often useful to have a local +HTTP server. For this purpose, we have made available a local web server based on Node.js. + +1. To start the web server, run: + ```shell + yarn grunt webserver + ``` + +2. To access the local server, enter the following URL into your web browser: + ```text + http://localhost:8000/ + ``` + By default, it serves the contents of the AngularJS project directory. + +3. To access the locally served docs, visit this URL: + ```text + http://localhost:8000/build/docs/ + ``` + +## Running Tests + +### Running the Unit Test Suite + +We write unit and integration tests with Jasmine and execute them with Karma. To run all of the +tests once on Chrome run: + +```shell +yarn grunt test:unit +``` + +To run the tests on other browsers use the command line flag: + +```shell +yarn grunt test:unit --browsers=Chrome,Firefox +``` + +**Note:** there should be _no spaces between browsers_. `Chrome, Firefox` is INVALID. + +If you have a Saucelabs or Browserstack account, you can also run the unit tests on these services +via our pre-defined customLaunchers. See the [karma config file](/karma-shared.conf.js) for all pre-configured browsers. + +For example, to run the whole unit test suite on selected browsers: + +```shell +# Browserstack +yarn grunt test:unit --browsers=BS_Chrome,BS_Firefox,BS_Safari,BS_IE_9,BS_IE_10,BS_IE_11,BS_EDGE,BS_iOS_10 +# Saucelabs +yarn grunt test:unit --browsers=SL_Chrome,SL_Firefox,SL_Safari,SL_IE_9,SL_IE_10,SL_IE_11,SL_EDGE,SL_iOS_10 +``` + +Running these commands requires you to set up [Karma Browserstack][karma-browserstack] or +[Karma-Saucelabs][karma-saucelabs], respectively. + +During development, however, it's more productive to continuously run unit tests every time the +source or test files change. To execute tests in this mode run: + +1. To start the Karma server, capture Chrome browser and run unit tests, run: + + ```shell + yarn grunt autotest + ``` + +2. To capture more browsers, open this URL in the desired browser (URL might be different if you + have multiple instance of Karma running, read Karma's console output for the correct URL): + + ```text + http://localhost:9876/ + ``` + +3. To re-run tests just change any source or test file. + + +To learn more about all of the preconfigured Grunt tasks run: + +```shell +yarn grunt --help +``` + + +### Running the End-to-end Test Suite + +AngularJS's end to end tests are run with Protractor. Simply run: + +```shell +yarn grunt test:e2e +``` + +This will start the webserver and run the tests on Chrome. + +## Coding Rules + +To ensure consistency throughout the source code, keep these rules in mind as you are working: + +* All features or bug fixes **must be tested** by one or more [specs][unit-testing]. +* All public API methods **must be documented** with ngdoc, an extended version of jsdoc (we added + support for markdown and templating via @ngdoc tag). To see how we document our APIs, please check + out the existing source code and see the section about [writing documentation](#documentation) +* With the exceptions listed below, we follow the rules contained in + [Google's JavaScript Style Guide][js-style-guide]: + * **Do not use namespaces**: Instead, wrap the entire AngularJS code base in an anonymous + closure and export our API explicitly rather than implicitly. + * Wrap all code at **100 characters**. + * Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypal + inheritance only when absolutely necessary. + * We **love functions and closures** and, whenever possible, prefer them over objects. + * To write concise code that can be better minified, we **use aliases internally** that map to + the external API. See our existing code to see what we mean. + * We **don't go crazy with type annotations** for private internal APIs unless it's an internal + API that is used throughout AngularJS. The best guidance is to do what makes the most sense. + +### Specific topics + +#### Provider configuration + +When adding configuration (options) to [providers][docs.provider], we follow a special pattern. + +- for each option, add a `method` that ... + - works as a getter and returns the current value when called without argument + - works as a setter and returns itself for chaining when called with argument + - for boolean options, uses the naming scheme `
diff --git a/benchmarks/repeat-animate-bp/app-classfilter.js b/benchmarks/repeat-animate-bp/app-classfilter.js new file mode 100644 index 000000000000..6c2708da145f --- /dev/null +++ b/benchmarks/repeat-animate-bp/app-classfilter.js @@ -0,0 +1,9 @@ +'use strict'; + +angular.module('repeatAnimateBenchmark', ['ngAnimate']) + .config(function($animateProvider) { + $animateProvider.classNameFilter(/animate-/); + }) + .run(function($rootScope) { + $rootScope.fileType = 'classfilter'; + }); diff --git a/benchmarks/repeat-animate-bp/app-noanimate.js b/benchmarks/repeat-animate-bp/app-noanimate.js new file mode 100644 index 000000000000..cc99bfcc8cd7 --- /dev/null +++ b/benchmarks/repeat-animate-bp/app-noanimate.js @@ -0,0 +1,6 @@ +'use strict'; + +angular.module('repeatAnimateBenchmark', []) + .run(function($rootScope) { + $rootScope.fileType = 'noanimate'; + }); diff --git a/benchmarks/repeat-animate-bp/app.js b/benchmarks/repeat-animate-bp/app.js new file mode 100644 index 000000000000..e7ac91d7c5fd --- /dev/null +++ b/benchmarks/repeat-animate-bp/app.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('repeatAnimateBenchmark', ['ngAnimate']) + .run(function($rootScope) { + $rootScope.fileType = 'default'; + }); + diff --git a/benchmarks/repeat-animate-bp/bp.conf.js b/benchmarks/repeat-animate-bp/bp.conf.js new file mode 100644 index 000000000000..e0f060ef9630 --- /dev/null +++ b/benchmarks/repeat-animate-bp/bp.conf.js @@ -0,0 +1,24 @@ +/* eslint-env node */ + +'use strict'; + +module.exports = function(config) { + config.set({ + scripts: [ + { + id: 'angular', + src: '/build/angular.js' + }, + { + id: 'angular-animate', + src: '/build/angular-animate.js' + }, + { + id: 'app', + src: 'app.js' + }, + { + src: 'common.js' + }] + }); +}; diff --git a/benchmarks/repeat-animate-bp/common.js b/benchmarks/repeat-animate-bp/common.js new file mode 100644 index 000000000000..faa4f77fe760 --- /dev/null +++ b/benchmarks/repeat-animate-bp/common.js @@ -0,0 +1,120 @@ +'use strict'; + +(function() { + var app = angular.module('repeatAnimateBenchmark'); + + app.config(function($compileProvider, $animateProvider) { + if ($compileProvider.debugInfoEnabled) { + $compileProvider.debugInfoEnabled(false); + } + + }); + + app.run(function($animate) { + if ($animate.enabled) { + $animate.enabled(true); + } + }); + + app.controller('DataController', function($scope, $rootScope, $animate) { + var totalRows = 500; + var totalColumns = 20; + + var data = $scope.data = []; + + function fillData() { + if ($animate.enabled) { + $animate.enabled($scope.benchmarkType !== 'globallyDisabled'); + } + + for (var i = 0; i < totalRows; i++) { + data[i] = []; + for (var j = 0; j < totalColumns; j++) { + data[i][j] = { + i: i + }; + } + } + } + + benchmarkSteps.push({ + name: 'enter', + fn: function() { + $scope.$apply(function() { + fillData(); + }); + } + }); + + benchmarkSteps.push({ + name: 'leave', + fn: function() { + $scope.$apply(function() { + data = $scope.data = []; + }); + } + }); + }); + + app.directive('disableAnimations', function($animate) { + return { + link: { + pre: function(s, e) { + $animate.enabled(e, false); + } + } + }; + }); + + app.directive('noop', function($animate) { + return { + link: { + pre: angular.noop + } + }; + }); + + app.directive('baseline', function($document) { + return { + restrict: 'E', + link: function($scope, $element) { + var document = $document[0]; + + var i, j, row, cell, comment; + var template = document.createElement('span'); + template.setAttribute('ng-repeat', 'foo in foos'); + template.classList.add('ng-scope'); + template.appendChild(document.createElement('span')); + template.appendChild(document.createTextNode(':')); + + function createList() { + for (i = 0; i < $scope.data.length; i++) { + row = document.createElement('div'); + $element[0].appendChild(row); + for (j = 0; j < $scope.data[i].length; j++) { + cell = template.cloneNode(true); + row.appendChild(cell); + cell.childNodes[0].textContent = i; + cell.ng339 = 'xxx'; + comment = document.createComment('ngRepeat end: bar in foo'); + row.appendChild(comment); + } + + comment = document.createComment('ngRepeat end: foo in foos'); + $element[0].appendChild(comment); + } + } + + $scope.$watch('data.length', function(newVal) { + if (newVal === 0) { + while ($element[0].firstChild) { + $element[0].removeChild($element[0].firstChild); + } + } else { + createList(); + } + }); + } + }; + }); +})(); diff --git a/benchmarks/repeat-animate-bp/main.html b/benchmarks/repeat-animate-bp/main.html new file mode 100644 index 000000000000..3c21074828a4 --- /dev/null +++ b/benchmarks/repeat-animate-bp/main.html @@ -0,0 +1,70 @@ +
+
+
+

+ Tests rendering of an ngRepeat with 500 elements.
+ Animations can be enabled / disabled in different ways.
+ Two tests require reloading the app with different module / app configurations. +

+ +
+
+
(requires app.js)
+
(requires app.js or app-classfilter.js)
+
(requires app.js)
+
(requires app-noanimate.js)
+
(requires app-classfilter.js)
+ + + + +
+
+
+ + {{column.i}} + +
+
+
+
+
+
+ + {{column.i}} + +
+
+
+
+
+
+ + {{column.i}} + +
+
+
+
+
+
+ + {{column.i}} + +
+
+
+
+
+
+ + {{column.i}} + +
+
+
+
+ +
+
+
diff --git a/bower.json b/bower.json deleted file mode 100644 index 95db7a55a742..000000000000 --- a/bower.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "angularjs", - "license": "MIT", - "devDependencies": { - "jquery": "3.1.0", - "jquery-2.2": "jquery#2.2.4", - "jquery-2.1": "jquery#2.1.4", - "closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip", - "ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.4/assets/ng-closure-runner.zip" - } -} diff --git a/css/angular-scenario.css b/css/angular-scenario.css index b8d25c1c08d0..56032ed777fe 100644 --- a/css/angular-scenario.css +++ b/css/angular-scenario.css @@ -13,7 +13,8 @@ body { text-align: center; } -#json, #xml { +#json, +#xml { display: none; } diff --git a/css/angular.css b/css/angular.css index a2921a61c9e1..8b3915383e3e 100644 --- a/css/angular.css +++ b/css/angular.css @@ -1,7 +1,11 @@ @charset "UTF-8"; -[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], -.ng-cloak, .x-ng-cloak, +[ng\:cloak], +[ng-cloak], +[data-ng-cloak], +[x-ng-cloak], +.ng-cloak, +.x-ng-cloak, .ng-hide:not(.ng-hide-animate) { display: none !important; } diff --git a/docs/app/assets/css/angular-topnav.css b/docs/app/assets/css/angular-topnav.css new file mode 100644 index 000000000000..826e424295aa --- /dev/null +++ b/docs/app/assets/css/angular-topnav.css @@ -0,0 +1 @@ +.visible-phone{display:none}.visible-desktop{display:block}.navbar{display:block}.navbar .container{padding:0 16px;width:auto}.navbar .brand{float:left;margin:8px 80px 0 8px;padding:0}.navbar .brand a{display:block;height:30px;margin:6px 0 5px 0;overflow:hidden;padding:0;width:117px}.navbar .nav{float:right}.navbar .nav .dropdown-toggle{color:rgba(255,255,255,0.87);font-size:16px;font-weight:300;line-height:56px;padding:0 24px;text-transform:uppercase;transition:all .3s}.navbar .nav .dropdown-toggle:hover,.navbar .nav .dropdown-toggle:active,.navbar .nav .dropdown-toggle:focus{background:#37474F;color:#fff}.navbar .nav .dropdown-menu{background:#37474F;border:none;border-radius:0;box-shadow:0 0 16px rgba(0,0,0,0.12),0 16px 16px rgba(0,0,0,0.24);color:#fff;left:auto;margin:0;padding:0;right:0}.navbar .nav .dropdown-menu:after,.navbar .nav .dropdown-menu:before{display:none}.navbar .nav .dropdown-menu li{border-bottom:1px solid rgba(38,50,56,0.56);box-sizing:border-box;line-height:48px}.navbar .nav .dropdown-menu li:last-child{border:none}.navbar .nav .dropdown-menu a{background:#37474F;color:#fff;font-weight:300;line-height:48px;padding:0 16px;transition:all .2s}.navbar .nav .dropdown-menu a:hover,.navbar .nav .dropdown-menu a:focus{background:#455A64}.navbar .navbar-search{left:200px;margin:0;position:absolute;right:440px;top:8px;width:auto}.navbar .navbar-search i{color:#546E7A;font-size:16px;left:12px;position:absolute;top:11px}.navbar .navbar-search .search-query{background:#37474F;border:none;border-radius:2px;box-shadow:none;box-sizing:border-box;color:#546E7A;font-size:14px;height:40px;width:100%;padding:0 16px 0 32px;text-shadow:none;transition:all .3s}.navbar .navbar-search .search-query:-webkit-autofill,.navbar .navbar-search .search-query:-webkit-autofill:hover,.navbar .navbar-search .search-query:-webkit-autofill:focus{background-color:#fff;transition:background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#455A64}.navbar .navbar-search .search-query:hover,.navbar .navbar-search .search-query:active,.navbar .navbar-search .search-query:focus{background:#fff;box-shadow:inset 0 2px 4px rgba(0,0,0,0.24);color:#2196F3}.navbar .navbar-search .search-query::-webkit-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query::-moz-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-ms-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-moz-placeholder{color:#546E7A}#navbar-main .navbar-inner{background:#263238;height:56px}#navbar-notice{z-index:1029;top:56px}#navbar-notice .navbar-inner{background:#ECEFF1;box-shadow:0 0 3px rgba(0,0,0,0.12),0 3px 3px rgba(0,0,0,0.24);height:auto}.site-notice{padding:4px 0;text-align:center;font-size:13px;margin:0}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.visible-phone{display:block}.visible-desktop{display:none}}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.homepage .container{padding:16px;width:auto}.homepage .span1{width:auto}.homepage .span2{width:auto}.homepage .span3{width:auto}.homepage .span4{width:auto}.homepage .span5{width:auto}.homepage .span6{width:auto}.homepage .span7{width:auto}.homepage .span8{width:auto}.homepage .span9{width:auto}.homepage .span10{width:auto}.homepage .navbar .container{padding:0 8px}.homepage #navbar-main .navbar-inner{height:40px}.homepage #navbar-main .brand{margin:6px 0 0 0}.homepage #navbar-main .brand a{margin:0}.homepage #navbar-main .nav{margin:0}.homepage #navbar-main .nav .dropdown-toggle{font-size:12px;line-height:40px;padding:0 8px}.homepage #navbar-main .dropdown-menu a{padding:0 8px}.homepage #navbar-main .navbar-search{background:#263238;border-bottom:1px solid #263238;left:0;right:0;top:100%}.homepage #navbar-main .navbar-search i{left:12px;top:7px}.homepage #navbar-main .navbar-search .search-query{border-radius:0;height:32px}.homepage #navbar-notice{top:40px}.homepage #navbar-notice .site-notice{font-size:11px}.homepage .hero{padding:80px 32px 32px 32px}.homepage .hero h2{background-size:230px 60px;height:60px;width:230px}} diff --git a/docs/app/assets/css/docs.css b/docs/app/assets/css/docs.css index c6d77c00ed96..eb28ad39d044 100644 --- a/docs/app/assets/css/docs.css +++ b/docs/app/assets/css/docs.css @@ -1,3 +1,37 @@ +@font-face { + font-family: 'Open Sans'; + src: url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.eot?v=1.1.0"); + src: url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.eot?#iefix&v=1.1.0") format("embedded-opentype"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.woff?v=1.1.0") format("woff"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.ttf?v=1.1.0") format("truetype"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Regular/OpenSans-Regular.svg?v=1.1.0#OpenSansBold") format("svg"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Open Sans'; + src: url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.eot?v=1.1.0"); + src: url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.eot?#iefix&v=1.1.0") format("embedded-opentype"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.woff?v=1.1.0") format("woff"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.ttf?v=1.1.0") format("truetype"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Semibold/OpenSans-Semibold.svg?v=1.1.0#OpenSansBold") format("svg"); + font-weight: 600; + font-style: normal; +} + + +@font-face { + font-family: 'Open Sans'; + src: url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.eot?v=1.1.0"); + src: url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.eot?#iefix&v=1.1.0") format("embedded-opentype"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.woff?v=1.1.0") format("woff"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.ttf?v=1.1.0") format("truetype"), + url("https://github.com/components/open-sans-fontface-1.4.0/fonts/Bold/OpenSans-Bold.svg?v=1.1.0#OpenSansBold") format("svg"); + font-weight: bold; + font-style: normal; +} + html, body { position: relative; height: 100%; @@ -53,13 +87,13 @@ h1,h2,h3,h4,h5,h6 { } .header .brand { - padding-top: 6px; padding-bottom: 0px; } .header .brand img { - margin-top: 5px; - height: 30px; + margin-top: 0; + height: auto; + vertical-align: top; } .docs-search { @@ -82,6 +116,11 @@ h1,h2,h3,h4,h5,h6 { margin-right: 10px; } +.navbar .navbar-search i { + top: 13px; + font-size: 12px; +} + .docs-search > .search-query:focus { outline: 0; } @@ -297,6 +336,7 @@ iframe.example { } .search-results-container { + position: relative; padding-bottom: 1em; border-top: 1px solid #111; background: #181818; @@ -435,15 +475,17 @@ iframe.example { background: #f1f1f1; } -.sup-header { +#navbar-sub { padding-top: 10px; padding-bottom: 5px; - background: rgba(245,245,245,0.88); + background: rgba(245,245,245,1); box-shadow: 0 0 2px #999; + z-index: 1028; + top: 57px; } .main-body-grid { - margin-top: 120px; + margin-top: 144px; position: relative; } @@ -454,7 +496,7 @@ iframe.example { .main-body-grid > .grid-left { position: fixed; - top: 120px; + top: 144px; bottom: 0; overflow: auto; } @@ -671,14 +713,14 @@ ul.events > li { margin-right: 5px; } -@media only screen and (min-width: 769px) { +@media only screen and (min-width: 768px) { [ng-include="partialPath"].ng-hide { display: block !important; visibility: hidden; } } -@media only screen and (min-width: 769px) and (max-width: 991px) { +@media only screen and (min-width: 768px) and (max-width: 991px) { .main-body-grid { margin-top: 160px; } @@ -687,7 +729,7 @@ ul.events > li { } } -@media only screen and (max-width : 768px) { +@media only screen and (max-width: 767px) { .picker, .picker select { width: auto; display: block; @@ -827,3 +869,123 @@ ul.events > li { iframe[name="example-anchoringExample"] { height: 400px; } + +/* + angular-topnav.css and bootstrap overrides + */ + +.navbar .navbar-inner .container { + padding: 0 16px; + width: auto; + height: auto; +} + +.navbar .nav > li { + float: left; +} + +.navbar-nav .open .dropdown-menu { + position: absolute; + float: left; +} + +.navbar-nav .open .dropdown-menu > li > a { + line-height: 48px; +} + +#navbar-main .navbar-inner, #navbar-notice .navbar-inner { + box-shadow: none; +} + +#navbar-sub .container { + max-width: 970px; +} + +.nav .open > a, .nav .open > a:hover, .nav .open > a:focus { + background-color: inherit; +} + +toc-container { + display: block; + margin: 15px 10px; +} + +toc-container b { + text-transform: uppercase; +} + +toc-container .btn { + padding: 3px 6px; + font-size: 13px; + margin-left: 5px; +} + +toc-container > div > toc-tree ul { + list-style: none; + padding-left: 15px; + padding-bottom: 2px; +} + +toc-container > div > toc-tree > ul { + padding-left: 0; +} + +toc-container > div > toc-tree > ul > li > toc-tree > ul > li toc-tree > ul li { + font-size: 13px; +} + +.dev-status span { + padding: 2px 8px; + border-radius: 5px; +} +.security span { background-color: orange; } +.stable span { background-color: green; color: white; } +.current span { background-color: blue; color: white; } + +@media handheld and (max-width:800px), screen and (max-device-width:800px), screen and (max-width:800px) { + .navbar { + min-height: auto; + } + + .search-results-container { + top: 32px; + overflow: auto; + max-height: 85vh; + padding-bottom: 0; + position: static; + } + + .search-close { + right: 1px; + margin-left: 0; + top: 41px; + padding: 5px 10px; + border-top-right-radius: 0; + border-top-left-radius: 0; + box-shadow: none; + width: auto; + bottom: auto; + left: auto; + } + + .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 0 8px; + } + + .homepage #navbar-notice { + top: 72px; + } + + #navbar-notice .navbar-inner { + box-shadow: 0 0 3px rgba(0, 0, 0, .12), 0 3px 3px rgba(0, 0, 0, .24) + } + + #navbar-sub { + position: relative; + top: 0; + margin-top: 80px; + padding-bottom: 0; + margin-bottom: 0; + } + +} diff --git a/docs/app/assets/img/AngularJS-small.png b/docs/app/assets/img/AngularJS-small.png index ab5e20f883c1..eb08948b6e5b 100644 Binary files a/docs/app/assets/img/AngularJS-small.png and b/docs/app/assets/img/AngularJS-small.png differ diff --git a/docs/app/assets/img/bullet.png b/docs/app/assets/img/bullet.png index 3575a8e60f48..33280db2e501 100644 Binary files a/docs/app/assets/img/bullet.png and b/docs/app/assets/img/bullet.png differ diff --git a/docs/app/assets/js/search-worker.js b/docs/app/assets/js/search-worker.js index 0376c556d02e..1d7b075442cb 100644 --- a/docs/app/assets/js/search-worker.js +++ b/docs/app/assets/js/search-worker.js @@ -4,7 +4,7 @@ /* global importScripts, lunr */ // Load up the lunr library -importScripts('../components/lunr.js-0.5.12/lunr.min.js'); +importScripts('../components/lunr-0.7.2/lunr.min.js'); // Create the lunr index - the docs should be an array of object, each object containing // the path and search terms for a page diff --git a/docs/app/assets/robots.txt b/docs/app/assets/robots.txt new file mode 100644 index 000000000000..898272b08202 --- /dev/null +++ b/docs/app/assets/robots.txt @@ -0,0 +1,4 @@ +User-agent: * + +# The map files are not required by the app +Disallow: /*.map$ \ No newline at end of file diff --git a/docs/app/e2e/.eslintrc.json b/docs/app/e2e/.eslintrc.json index 6a949b92ffc9..60c814cc9339 100644 --- a/docs/app/e2e/.eslintrc.json +++ b/docs/app/e2e/.eslintrc.json @@ -9,6 +9,7 @@ }, "globals": { + "angular": false, /* testabilityPatch / matchers */ "inject": false, "module": false, diff --git a/docs/app/e2e/api-docs/api-pages.scenario.js b/docs/app/e2e/api-docs/api-pages.scenario.js index f6f296fd759a..60e56c8c94bd 100644 --- a/docs/app/e2e/api-docs/api-pages.scenario.js +++ b/docs/app/e2e/api-docs/api-pages.scenario.js @@ -1,51 +1,48 @@ 'use strict'; -describe('doc.angularjs.org', function() { +describe('API pages', function() { - describe('API pages', function() { + it('should display links to code on GitHub', function() { + browser.get('build/docs/index.html#!/api/ng/service/$http'); + expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/); - it('should display links to code on GitHub', function() { - browser.get('build/docs/index.html#!/api/ng/service/$http'); - expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/); - - browser.get('build/docs/index.html#!/api/ng/service/$http'); - expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/); - }); + browser.get('build/docs/index.html#!/api/ng/service/$http'); + expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/); + }); - it('should change the page content when clicking a link to a service', function() { - browser.get('build/docs/index.html'); + it('should change the page content when clicking a link to a service', function() { + browser.get('build/docs/index.html'); - var ngBindLink = element(by.css('.definition-table td a[href="https://github.com/api/ng/directive/ngClick"]')); - ngBindLink.click(); + var ngBindLink = element(by.css('.definition-table td a[href="https://github.com/api/ng/directive/ngClick"]')); + ngBindLink.click(); - var pageBody = element(by.css('h1')); - expect(pageBody.getText()).toEqual('ngClick'); - }); + var mainHeader = element(by.css('.main-body h1 ')); + expect(mainHeader.getText()).toEqual('ngClick'); + }); - it('should show the functioning input directive example', function() { - browser.get('build/docs/index.html#!/api/ng/directive/input'); + it('should show the functioning input directive example', function() { + browser.get('build/docs/index.html#!/api/ng/directive/input'); - // Ensure that the page is loaded before trying to switch frames. - browser.waitForAngular(); + // Ensure that the page is loaded before trying to switch frames. + browser.waitForAngular(); - browser.switchTo().frame('example-input-directive'); + browser.switchTo().frame('example-input-directive'); - var nameInput = element(by.model('user.name')); - nameInput.sendKeys('!!!'); + var nameInput = element(by.model('user.name')); + nameInput.sendKeys('!!!'); - var code = element.all(by.css('tt')).first(); - expect(code.getText()).toContain('guest!!!'); - }); + var code = element.all(by.css('tt')).first(); + expect(code.getText()).toContain('guest!!!'); + }); - it('should trim indentation from code blocks', function() { - browser.get('build/docs/index.html#!/api/ng/type/$rootScope.Scope'); + it('should trim indentation from code blocks', function() { + browser.get('build/docs/index.html#!/api/ng/type/$rootScope.Scope'); - var codeBlocks = element.all(by.css('pre > code.lang-js')); - codeBlocks.each(function(codeBlock) { - var firstSpan = codeBlock.all(by.css('span')).first(); - expect(firstSpan.getText()).not.toMatch(/^\W+$/); - }); + var codeBlocks = element.all(by.css('pre > code.lang-js')); + codeBlocks.each(function(codeBlock) { + var firstSpan = codeBlock.all(by.css('span')).first(); + expect(firstSpan.getText()).not.toMatch(/^\W+$/); }); }); }); diff --git a/docs/app/e2e/api-docs/directive-pages.scenario.js b/docs/app/e2e/api-docs/directive-pages.scenario.js new file mode 100644 index 000000000000..cec2d484545d --- /dev/null +++ b/docs/app/e2e/api-docs/directive-pages.scenario.js @@ -0,0 +1,58 @@ +'use strict'; + +describe('directives', function() { + + describe('parameter section', function() { + + it('should show the directive name only if it is a param (attribute) with a value', function() { + browser.get('build/docs/index.html#!/api/ng/directive/ngInclude'); + expect(getParamNames().getText()).toContain('ngInclude | src'); + + browser.get('build/docs/index.html#!/api/ngRoute/directive/ngView'); + expect(getParamNames().getText()).not.toContain('ngView'); + }); + }); + + describe('usage section', function() { + + it('should show the directive name if it is a param (attribute) with a value', function() { + browser.get('build/docs/index.html#!/api/ng/directive/ngInclude'); + + expect(getUsageAs('element', 'ng-include').isPresent()).toBe(true); + expect(getUsageAs('attribute', 'ng-include').isPresent()).toBe(true); + expect(getUsageAs('CSS class', 'ng-include').isPresent()).toBe(true); + }); + + it('should show the directive name if it is a void param (attribute)', function() { + browser.get('build/docs/index.html#!/api/ngRoute/directive/ngView'); + + expect(getUsageAs('element', 'ng-view').isPresent()).toBe(true); + expect(getUsageAs('attribute', 'ng-view').isPresent()).toBe(true); + expect(getUsageAs('CSS class', 'ng-view').isPresent()).toBe(true); + }); + }); +}); + +function getParamNames() { + var argsSection = element(by.className('input-arguments')); + + var paramNames = argsSection.all(by.css('tr td:nth-child(1)')); + + return paramNames; +} + +// Based on the type of directive usage, the directive name will show up in the code block +// with a specific class +var typeClassMap = { + element: 'tag', + attribute: 'atn', + 'CSS class': 'atv' +}; + +function getUsageAs(type, directiveName) { + var usage = element(by.className('usage')); + + var as = usage.element(by.cssContainingText('li', 'as ' + type)); + + return as.element(by.cssContainingText('span.' + typeClassMap[type], directiveName)); +} diff --git a/docs/app/e2e/app.scenario.js b/docs/app/e2e/app.scenario.js index 4bacb00de97a..a78667962ab9 100644 --- a/docs/app/e2e/app.scenario.js +++ b/docs/app/e2e/app.scenario.js @@ -21,6 +21,9 @@ describe('docs.angularjs.org', function() { console.log('browser console errors: ' + require('util').inspect(filteredLog)); } }); + + browser.ignoreSynchronization = false; + browser.clearMockModules(); }); @@ -44,30 +47,50 @@ describe('docs.angularjs.org', function() { var ngBindLink = element(by.css('.definition-table td a[href="https://github.com/api/ng/directive/ngClick"]')); ngBindLink.click(); - var pageBody = element(by.css('h1')); - expect(pageBody.getText()).toEqual('ngClick'); + var mainHeader = element(by.css('.main-body h1 ')); + expect(mainHeader.getText()).toEqual('ngClick'); }); + it('should include the files for the embedded examples from the same domain', function() { + browser.get('build/docs/index-production.html#!api/ng/directive/ngClick'); + + var origin = browser.executeScript('return document.location.origin;'); + + var exampleIFrame = element(by.name('example-ng-click')); + + // This is technically an implementation detail, but if this changes, then there's a good + // chance the deployment process changed + expect(exampleIFrame.getAttribute('src')).toContain('examples/example-ng-click/index.html'); + + browser.switchTo().frame('example-ng-click'); + + var scriptEl = element(by.tagName('script')); + + // Ensure the included file is from the same domain + expect(scriptEl.getAttribute('src')).toContain(origin); + }); + it('should be resilient to trailing slashes', function() { browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/'); - var pageBody = element(by.css('h1')); - expect(pageBody.getText()).toEqual('angular.noop'); + + var mainHeader = element(by.css('.main-body h1 ')); + expect(mainHeader.getText()).toEqual('angular.noop'); }); it('should be resilient to trailing "index"', function() { browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index'); - var pageBody = element(by.css('h1')); - expect(pageBody.getText()).toEqual('angular.noop'); + var mainHeader = element(by.css('.main-body h1 ')); + expect(mainHeader.getText()).toEqual('angular.noop'); }); it('should be resilient to trailing "index/"', function() { browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index/'); - var pageBody = element(by.css('h1')); - expect(pageBody.getText()).toEqual('angular.noop'); + var mainHeader = element(by.css('.main-body h1 ')); + expect(mainHeader.getText()).toEqual('angular.noop'); }); @@ -78,7 +101,68 @@ describe('docs.angularjs.org', function() { it('should display an error if the page does not exist', function() { browser.get('build/docs/index-production.html#!/api/does/not/exist'); - expect(element(by.css('h1')).getText()).toBe('Oops!'); + var mainHeader = element(by.css('.main-body h1 ')); + expect(mainHeader.getText()).toEqual('Oops!'); + }); + + it('should set "noindex" if the page does not exist', function() { + browser.get('build/docs/index-production.html#!/api/does/not/exist'); + var robots = element(by.css('meta[name="robots"][content="noindex"]')); + var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]')); + expect(robots.isPresent()).toBe(true); + expect(googleBot.isPresent()).toBe(true); + }); + + it('should remove "noindex" if the page exists', function() { + browser.get('build/docs/index-production.html#!/api'); + var robots = element(by.css('meta[name="robots"][content="noindex"]')); + var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]')); + expect(robots.isPresent()).toBe(false); + expect(googleBot.isPresent()).toBe(false); + }); + + describe('template request error', function() { + beforeEach(function() { + browser.addMockModule('httpMocker', function() { + angular.module('httpMocker', ['ngMock']) + .run(['$httpBackend', function($httpBackend) { + $httpBackend.whenGET('localhost:8000/build/docs/partials/api.html').respond(500, ''); + }]); + }); + }); + + it('should set "noindex" for robots if the request fails', function() { + // index-test includes ngMock + browser.get('build/docs/index-test.html#!/api'); + var robots = element(by.css('meta[name="robots"][content="noindex"]')); + var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]')); + expect(robots.isPresent()).toBe(true); + expect(googleBot.isPresent()).toBe(true); + }); + }); + + + describe('page bootstrap error', function() { + beforeEach(function() { + browser.addMockModule('httpMocker', function() { + // Require a module that does not exist to break the bootstrapping + angular.module('httpMocker', ['doesNotExist']); + }); + }); + + it('should have "noindex" for robots if bootstrapping fails', function() { + browser.get('build/docs/index.html#!/api').catch(function() { + // get() will fail on AngularJS bootstrap, but if we continue here, protractor + // will assume the app is ready + browser.ignoreSynchronization = true; + var robots = element(by.css('meta[name="robots"][content="noindex"]')); + var googleBot = element(by.css('meta[name="googlebot"][content="noindex"]')); + expect(robots.isPresent()).toBe(true); + expect(googleBot.isPresent()).toBe(true); + }); + }); + + }); }); diff --git a/docs/app/e2e/table-of-contents.scenario.js b/docs/app/e2e/table-of-contents.scenario.js new file mode 100644 index 000000000000..b2b355559c02 --- /dev/null +++ b/docs/app/e2e/table-of-contents.scenario.js @@ -0,0 +1,130 @@ +'use strict'; + +/** + * This scenario checks the presence of the table of contents for a sample of pages - API and guide. + * The expectations are kept vague so that they can be easily adjusted when the docs change. + */ + +describe('table of contents', function() { + + it('on provider pages', function() { + browser.get('build/docs/index.html#!/api/ng/provider/$controllerProvider'); + + var toc = element.all(by.css('toc-container > div > toc-tree')); + toc.getText().then(function(text) { + expect(text.join('')).toContain('Overview'); + expect(text.join('')).toContain('Methods'); + }); + + var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li')); + + tocFirstLevel.then(function(match) { + expect(match.length).toBe(2); + + expect(match[1].all(by.css('li')).count()).toBe(2); + }); + + }); + + it('on service pages', function() { + browser.get('build/docs/index.html#!/api/ng/service/$controller'); + + var toc = element.all(by.css('toc-container > div > toc-tree')); + toc.getText().then(function(text) { + expect(text.join('')).toContain('Overview'); + expect(text.join('')).toContain('Usage'); + }); + + var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li')); + + tocFirstLevel.then(function(match) { + expect(match.length).toBe(3); + + expect(match[2].all(by.css('li')).count()).toBe(2); + }); + }); + + it('on directive pages', function() { + browser.get('build/docs/index.html#!/api/ng/directive/input'); + + var toc = element.all(by.css('toc-container > div > toc-tree')); + toc.getText().then(function(text) { + expect(text.join('')).toContain('Overview'); + expect(text.join('')).toContain('Usage'); + expect(text.join('')).toContain('Directive Info'); + }); + + var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li')); + + tocFirstLevel.then(function(match) { + expect(match.length).toBe(4); + + expect(match[2].all(by.css('li')).count()).toBe(1); + }); + }); + + it('on function pages', function() { + browser.get('build/docs/index.html#!/api/ng/function/angular.bind'); + + var toc = element.all(by.css('toc-container > div > toc-tree')); + toc.getText().then(function(text) { + expect(text.join('')).toContain('Overview'); + expect(text.join('')).toContain('Usage'); + }); + + var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li')); + + tocFirstLevel.then(function(match) { + expect(match.length).toBe(2); + + expect(match[1].all(by.css('li')).count()).toBe(2); + }); + }); + + it('on type pages', function() { + browser.get('build/docs/index.html#!/api/ng/type/ModelOptions'); + + var toc = element.all(by.css('toc-container > div > toc-tree')); + toc.getText().then(function(text) { + expect(text.join('')).toContain('Overview'); + expect(text.join('')).toContain('Methods'); + }); + + var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li')); + + tocFirstLevel.then(function(match) { + expect(match.length).toBe(2); + + expect(match[1].all(by.css('li')).count()).toBe(2); + }); + }); + + it('on filter pages', function() { + browser.get('build/docs/index.html#!/api/ng/filter/date'); + + var toc = element.all(by.css('toc-container > div > toc-tree')); + toc.getText().then(function(text) { + expect(text.join('')).toContain('Overview'); + expect(text.join('')).toContain('Usage'); + }); + + var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li')); + + tocFirstLevel.then(function(match) { + expect(match.length).toBe(3); + + expect(match[1].all(by.css('li')).count()).toBe(2); + }); + }); + + it('on guide pages', function() { + browser.get('build/docs/index.html#!/guide/services'); + var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li')); + + tocFirstLevel.then(function(match) { + expect(match.length).toBe(5); + + expect(match[1].all(by.css('li')).count()).toBe(3); + }); + }); +}); diff --git a/docs/app/src/directives.js b/docs/app/src/directives.js index 229da10f3bc3..561946cdffcd 100644 --- a/docs/app/src/directives.js +++ b/docs/app/src/directives.js @@ -1,7 +1,8 @@ 'use strict'; -angular.module('directives', []) +var directivesModule = angular.module('directives', []); +directivesModule /** * backToTop Directive * @param {Function} $anchorScroll @@ -47,4 +48,136 @@ angular.module('directives', []) } } }; -}); +}) + +.directive('tocCollector', ['$rootScope', function($rootScope) { + return { + controller: ['$element', function($element) { + /* eslint-disable no-invalid-this */ + var ctrl = this; + + $rootScope.$on('$includeContentRequested', function() { + ctrl.hs = []; + ctrl.root = []; + }); + + this.hs = []; + this.root = []; + this.element = $element; + + this.register = function(h) { + var previousLevel; + + for (var i = ctrl.hs.length - 1; i >= 0; i--) { + if (ctrl.hs[i].level === (h.level - 1)) { + previousLevel = ctrl.hs[i]; + break; + } + } + + if (previousLevel) { + previousLevel.children.push(h); + } else { + this.root.push(h); + } + + ctrl.hs.push(h); + /* eslint-enable no-invalid-this */ + }; + }] + }; +}]) + +.component('tocTree', { + template: '', + bindings: { + items: '<' + }, + controller: ['$location', /** @this */ function($location) { + this.path = $location.path().replace(/^\/?(.+?)(\/index)?\/?$/, '$1'); + }] +}) +.directive('tocContainer', function() { + return { + scope: true, + restrict: 'E', + require: { + tocContainer: '', + tocCollector: '^^' + }, + controller: function() { + this.showToc = true; + this.items = []; + }, + controllerAs: '$ctrl', + link: function(scope, element, attrs, ctrls) { + ctrls.tocContainer.items = ctrls.tocCollector.root; + }, + template: '
' + + 'Contents' + + '
' + + '' + + '
' + }; +}) +.directive('header', function() { + return { + restrict: 'E', + controller: ['$element', function($element) { + // eslint-disable-next-line no-invalid-this + this.element = $element; + }] + }; +}) +.directive('h1', ['$compile', function($compile) { + return { + restrict: 'E', + require: { + tocCollector: '^^?', + header: '^^?' + }, + link: function(scope, element, attrs, ctrls) { + if (!ctrls.tocCollector) return; + + var tocContainer = angular.element(''); + var containerElement = ctrls.header ? ctrls.header.element : element; + + containerElement.after(tocContainer); + $compile(tocContainer)(scope); + } + }; +}]); + +for (var i = 2; i <= 5; i++) { + registerHDirective(i); +} + +function registerHDirective(i) { + directivesModule.directive('h' + i, function() { + return { + restrict: 'E', + require: { + 'tocCollector': '^^?' + }, + link: function(scope, element, attrs, ctrls) { + var toc = ctrls.tocCollector; + + if (!toc || !attrs.id) return; + + toc.register({ + level: i, + fragment: attrs.id, + title: element.text(), + children: [] + }); + + } + }; + }); +} + diff --git a/docs/app/src/docs.js b/docs/app/src/docs.js index 33b1be384beb..b6e6e49a2aa8 100644 --- a/docs/app/src/docs.js +++ b/docs/app/src/docs.js @@ -8,6 +8,8 @@ angular.module('DocsController', ['currentVersionData']) function($scope, $rootScope, $location, $window, $cookies, NG_PAGES, NG_NAVIGATION, CURRENT_NG_VERSION) { + var errorPartialPath = 'Error404.html'; + $scope.navClass = function(navItem) { return { active: navItem.href && this.currentPage && this.currentPage.path, @@ -16,8 +18,6 @@ angular.module('DocsController', ['currentVersionData']) }; }; - - $scope.$on('$includeContentLoaded', function() { var pagePath = $scope.currentPage ? $scope.currentPage.path : $location.path(); $window._gaq.push(['_trackPageview', pagePath]); @@ -26,6 +26,7 @@ angular.module('DocsController', ['currentVersionData']) $scope.$on('$includeContentError', function() { $scope.loading = false; + $scope.loadingError = true; }); $scope.$watch(function docsPathWatch() {return $location.path(); }, function docsPathWatchAction(path) { @@ -35,6 +36,7 @@ angular.module('DocsController', ['currentVersionData']) var currentPage = $scope.currentPage = NG_PAGES[path]; $scope.loading = true; + $scope.loadingError = false; if (currentPage) { $scope.partialPath = 'partials/' + path + '.html'; @@ -50,18 +52,22 @@ angular.module('DocsController', ['currentVersionData']) } else { $scope.currentArea = NG_NAVIGATION['api']; $scope.breadcrumb = []; - $scope.partialPath = 'Error404.html'; + $scope.partialPath = errorPartialPath; } }); + $scope.hasError = function() { + return $scope.partialPath === errorPartialPath || $scope.loadingError; + }; + /********************************** Initialize ***********************************/ $scope.versionNumber = CURRENT_NG_VERSION.full; $scope.version = CURRENT_NG_VERSION.full + ' ' + CURRENT_NG_VERSION.codeName; - $scope.loading = 0; - + $scope.loading = false; + $scope.loadingError = false; var INDEX_PATH = /^(\/|\/index[^.]*.html)$/; if (!$location.path() || INDEX_PATH.test($location.path())) { diff --git a/docs/app/src/examples.js b/docs/app/src/examples.js index b888db3b087d..7a5ebb62325f 100644 --- a/docs/app/src/examples.js +++ b/docs/app/src/examples.js @@ -55,7 +55,7 @@ angular.module('examples', []) return function(url, newWindow, fields) { /** * If the form posts to target="_blank", pop-up blockers can cause it not to work. - * If a user choses to bypass pop-up blocker one time and click the link, they will arrive at + * If a user chooses to bypass pop-up blocker one time and click the link, they will arrive at * a new default plnkr, not a plnkr with the desired template. Given this undesired behavior, * some may still want to open the plnk in a new window by opting-in via ctrl+click. The * newWindow param allows for this possibility. @@ -74,7 +74,7 @@ angular.module('examples', []) }]) .factory('createCopyrightNotice', function() { - var COPYRIGHT = 'Copyright ' + (new Date()).getFullYear() + ' Google Inc. All Rights Reserved.\n' + var COPYRIGHT = 'Copyright ' + (new Date()).getFullYear() + ' Google LLC. All Rights Reserved.\n' + 'Use of this source code is governed by an MIT-style license that\n' + 'can be found in the LICENSE file at http://angular.io/license'; var COPYRIGHT_JS_CSS = '\n\n/*\n' + COPYRIGHT + '\n*/'; @@ -159,10 +159,11 @@ angular.module('examples', []) }; - // Initialize the example data, so it's ready when clicking the open button. - // Otherwise pop-up blockers will prevent a new window from opening - ctrl.prepareExampleData(ctrl.example.path); - + ctrl.$onInit = function() { + // Initialize the example data, so it's ready when clicking the open button. + // Otherwise pop-up blockers will prevent a new window from opening + ctrl.prepareExampleData(ctrl.example.path); + }; }] }; }]) diff --git a/docs/app/src/search.js b/docs/app/src/search.js index 1be6614f3846..c239e5d5675c 100644 --- a/docs/app/src/search.js +++ b/docs/app/src/search.js @@ -67,6 +67,12 @@ angular.module('search', []) clearResults(); $scope.q = ''; }; + + $scope.handleResultClicked = function($event) { + if ($event.which === 1 && !$event.ctrlKey && !$event.metaKey) { + $scope.hideResults(); + } + }; }]) diff --git a/docs/app/src/versions.js b/docs/app/src/versions.js index 9f0d3dc0c5c4..aa5c3618b0fd 100644 --- a/docs/app/src/versions.js +++ b/docs/app/src/versions.js @@ -12,10 +12,16 @@ angular.module('versions', ['currentVersionData', 'allVersionsData']) /** @this VersionPickerController */ function VersionPickerController($location, $window, CURRENT_NG_VERSION, ALL_NG_VERSIONS) { - var versionStr = CURRENT_NG_VERSION.isSnapshot ? 'snapshot' : CURRENT_NG_VERSION.version; + var versionStr = CURRENT_NG_VERSION.version; + + if (CURRENT_NG_VERSION.isSnapshot) { + versionStr = CURRENT_NG_VERSION.distTag === 'latest' ? 'snapshot-stable' : 'snapshot'; + } this.versions = ALL_NG_VERSIONS; - this.selectedVersion = find(ALL_NG_VERSIONS, function(value) { return value.version.version === versionStr; }); + this.selectedVersion = find(ALL_NG_VERSIONS, function(value) { + return value.version.version === versionStr; + }); this.jumpToDocsVersion = function(value) { var currentPagePath = $location.path().replace(/\/$/, ''); diff --git a/docs/app/test/directivesSpec.js b/docs/app/test/directivesSpec.js index b9a24465f1b9..4d6483661fd0 100644 --- a/docs/app/test/directivesSpec.js +++ b/docs/app/test/directivesSpec.js @@ -1,40 +1,50 @@ 'use strict'; -describe('code', function() { - var prettyPrintOne, oldPP; +describe('directives', function() { var compile, scope; - var any = jasmine.any; beforeEach(module('directives')); - beforeEach(inject(function($rootScope, $compile) { - // Provide stub for pretty print function - oldPP = window.prettyPrintOne; - prettyPrintOne = window.prettyPrintOne = jasmine.createSpy(); + beforeEach(module(function($compileProvider) { + $compileProvider.debugInfoEnabled(false); + })); + beforeEach(inject(function($rootScope, $compile) { scope = $rootScope.$new(); compile = $compile; })); - afterEach(function() { - window.prettyPrintOne = oldPP; - }); + describe('code', function() { + var prettyPrintOne, oldPP; + var any = jasmine.any; + beforeEach(function() { + // Provide stub for pretty print function + oldPP = window.prettyPrintOne; + prettyPrintOne = window.prettyPrintOne = jasmine.createSpy(); + }); - it('should pretty print innerHTML', function() { - compile('var x;')(scope); - expect(prettyPrintOne).toHaveBeenCalledWith('var x;', null, false); - }); + afterEach(function() { + window.prettyPrintOne = oldPP; + }); - it('should allow language declaration', function() { - compile('')(scope); - expect(prettyPrintOne).toHaveBeenCalledWith(any(String), 'javascript', false); - }); - it('supports allow line numbers', function() { - compile('')(scope); - expect(prettyPrintOne).toHaveBeenCalledWith(any(String), null, true); + it('should pretty print innerHTML', function() { + compile('var x;')(scope); + expect(prettyPrintOne).toHaveBeenCalledWith('var x;', null, false); + }); + + it('should allow language declaration', function() { + compile('')(scope); + expect(prettyPrintOne).toHaveBeenCalledWith(any(String), 'javascript', false); + }); + + it('supports allow line numbers', function() { + compile('')(scope); + expect(prettyPrintOne).toHaveBeenCalledWith(any(String), null, true); + }); }); + }); diff --git a/docs/bower.json b/docs/bower.json deleted file mode 100644 index 4eae8d80b6b9..000000000000 --- a/docs/bower.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "AngularJS-docs-app", - "dependencies": { - "jquery": "2.2.3", - "lunr.js": "0.5.12", - "open-sans-fontface": "1.0.4", - "google-code-prettify": "1.0.1", - "bootstrap": "3.1.1" - } -} diff --git a/docs/config/index.js b/docs/config/index.js index ab5e45a3f8dc..6bf3a2744a53 100644 --- a/docs/config/index.js +++ b/docs/config/index.js @@ -22,6 +22,7 @@ module.exports = new Package('angularjs', [ .factory(require('./services/deployments/debug')) .factory(require('./services/deployments/default')) .factory(require('./services/deployments/jquery')) +.factory(require('./services/deployments/test')) .factory(require('./services/deployments/production')) .factory(require('./inline-tag-defs/type')) @@ -31,6 +32,7 @@ module.exports = new Package('angularjs', [ .processor(require('./processors/keywords')) .processor(require('./processors/pages-data')) .processor(require('./processors/versions-data')) +.processor(require('./processors/sitemap')) .config(function(dgeni, log, readFilesProcessor, writeFilesProcessor) { @@ -146,6 +148,7 @@ module.exports = new Package('angularjs', [ .config(function(checkAnchorLinksProcessor) { checkAnchorLinksProcessor.base = '/'; + checkAnchorLinksProcessor.errorOnUnmatchedLinks = true; // We are only interested in docs that have an area (i.e. they are pages) checkAnchorLinksProcessor.checkDoc = function(doc) { return doc.area; }; }) @@ -156,12 +159,14 @@ module.exports = new Package('angularjs', [ generateProtractorTestsProcessor, generateExamplesProcessor, debugDeployment, defaultDeployment, - jqueryDeployment, productionDeployment) { + jqueryDeployment, testDeployment, + productionDeployment) { generateIndexPagesProcessor.deployments = [ debugDeployment, defaultDeployment, jqueryDeployment, + testDeployment, productionDeployment ]; diff --git a/docs/config/processors/error-docs.js b/docs/config/processors/error-docs.js index c80297efbba5..ebcd18658939 100644 --- a/docs/config/processors/error-docs.js +++ b/docs/config/processors/error-docs.js @@ -5,18 +5,36 @@ * @description * Process "error" docType docs and generate errorNamespace docs */ -module.exports = function errorDocsProcessor(errorNamespaceMap, getMinerrInfo) { +module.exports = function errorDocsProcessor(log, errorNamespaceMap, getMinerrInfo) { return { $runAfter: ['tags-extracted'], $runBefore: ['extra-docs-added'], $process: function(docs) { + // Get the extracted min errors to compare with the error docs, and report any mismatch + var collectedErrors = require('../../../build/errors.json').errors; + var flatErrors = []; + + for (var namespace in collectedErrors) { + for (var error in collectedErrors[namespace]) { + flatErrors.push(namespace + ':' + error); + } + } + // Create error namespace docs and attach error docs to each docs.forEach(function(doc) { var parts, namespaceDoc; if (doc.docType === 'error') { + var matchingMinErr = flatErrors.indexOf(doc.name); + + if (matchingMinErr === -1) { + log.warn('Error doc: ' + doc.name + ' has no matching min error'); + } else { + flatErrors.splice(matchingMinErr, 1); + } + // Parse out the error info from the id parts = doc.name.split(':'); doc.namespace = parts[0]; @@ -41,6 +59,10 @@ module.exports = function errorDocsProcessor(errorNamespaceMap, getMinerrInfo) { } }); + flatErrors.forEach(function(value) { + log.warn('No error doc exists for min error: ' + value); + }); + errorNamespaceMap.forEach(function(errorNamespace) { docs.push(errorNamespace); }); diff --git a/docs/config/processors/keywords.js b/docs/config/processors/keywords.js index 8babc671dc5b..40fb97c63a89 100644 --- a/docs/config/processors/keywords.js +++ b/docs/config/processors/keywords.js @@ -47,13 +47,13 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) { } - areasToSearch = _.indexBy(this.areasToSearch); - propertiesToIgnore = _.indexBy(this.propertiesToIgnore); + areasToSearch = _.keyBy(this.areasToSearch); + propertiesToIgnore = _.keyBy(this.propertiesToIgnore); log.debug('Properties to ignore', propertiesToIgnore); - docTypesToIgnore = _.indexBy(this.docTypesToIgnore); + docTypesToIgnore = _.keyBy(this.docTypesToIgnore); log.debug('Doc types to ignore', docTypesToIgnore); - var ignoreWordsMap = _.indexBy(wordsToIgnore); + var ignoreWordsMap = _.keyBy(wordsToIgnore); // If the title contains a name starting with ng, e.g. "ngController", then add the module name // without the ng to the title text, e.g. "controller". diff --git a/docs/config/processors/pages-data.js b/docs/config/processors/pages-data.js index 9dc39b331257..c6e24bcb9568 100644 --- a/docs/config/processors/pages-data.js +++ b/docs/config/processors/pages-data.js @@ -224,7 +224,7 @@ module.exports = function generatePagesDataProcessor(log) { .map(function(doc) { return _.pick(doc, ['name', 'area', 'path']); }) - .indexBy('path') + .keyBy('path') .value(); docs.push({ diff --git a/docs/config/processors/sitemap.js b/docs/config/processors/sitemap.js new file mode 100644 index 000000000000..aea84da9a17a --- /dev/null +++ b/docs/config/processors/sitemap.js @@ -0,0 +1,25 @@ +'use strict'; + +var exclusionRegex = /^index|examples\/|ptore2e\//; + +module.exports = function createSitemap() { + return { + $runAfter: ['paths-computed'], + $runBefore: ['rendering-docs'], + $process: function(docs) { + docs.push({ + id: 'sitemap.xml', + path: 'sitemap.xml', + outputPath: '../sitemap.xml', + template: 'sitemap.template.xml', + urls: docs.filter(function(doc) { + return doc.path && + doc.outputPath && + !exclusionRegex.test(doc.outputPath); + }).map(function(doc) { + return doc.path; + }) + }); + } + }; +}; diff --git a/docs/config/processors/versions-data.js b/docs/config/processors/versions-data.js index 4e0d3aeffb47..fd7aceaa4d70 100644 --- a/docs/config/processors/versions-data.js +++ b/docs/config/processors/versions-data.js @@ -13,11 +13,11 @@ module.exports = function generateVersionDocProcessor(gitData) { return { $runAfter: ['generatePagesDataProcessor'], $runBefore: ['rendering-docs'], - // the blacklist is to remove rogue builds that are in the npm repository but not on code.angularjs.org - blacklist: ['1.3.4-build.3588'], + // Remove rogue builds that are in the npm repository but not on code.angularjs.org + ignoredBuilds: ['1.3.4-build.3588'], $process: function(docs) { - var blacklist = this.blacklist; + var ignoredBuilds = this.ignoredBuilds; var currentVersion = require('../../../build/version.json'); var output = exec('yarn info angular versions --json', { silent: true }).stdout.split('\n')[0]; var allVersions = processAllVersionsResponse(JSON.parse(output).data); @@ -47,9 +47,17 @@ module.exports = function generateVersionDocProcessor(gitData) { var latestMap = {}; + // When the docs are built on a tagged commit, yarn info won't include the latest release, + // so we add it manually based on the local version.json file. + var missesCurrentVersion = !currentVersion.isSnapshot && !versions.find(function(version) { + return version === currentVersion.version; + }); + + if (missesCurrentVersion) versions.push(currentVersion.version); + versions = versions .filter(function(versionStr) { - return blacklist.indexOf(versionStr) === -1; + return ignoredBuilds.indexOf(versionStr) === -1; }) .map(function(versionStr) { return semver.parse(versionStr); @@ -70,10 +78,28 @@ module.exports = function generateVersionDocProcessor(gitData) { }) .reverse(); + // List the latest version for each branch var latest = sortObject(latestMap, reverse(semver.compare)) .map(function(version) { return makeOption(version, 'Latest'); }); - return [makeOption({version: 'snapshot'}, 'Latest', 'master')] + // Get the stable release with the highest version + var highestStableRelease = versions.find(semverIsStable); + + // Generate master and stable snapshots + var snapshots = [ + makeOption( + {version: 'snapshot'}, + 'Latest', + 'master-snapshot' + ), + makeOption( + {version: 'snapshot-stable'}, + 'Latest', + createSnapshotStableLabel(highestStableRelease) + ) + ]; + + return snapshots .concat(latest) .concat(versions); } @@ -103,6 +129,19 @@ module.exports = function generateVersionDocProcessor(gitData) { function sortObject(obj, cmp) { return Object.keys(obj).map(function(key) { return obj[key]; }).sort(cmp); } + + // Adapted from + // https://github.com/kaelzhang/node-semver-stable/blob/34dd29842409295d49889d45871bec55a992b7f6/index.js#L25 + function semverIsStable(version) { + var semverObj = version.version; + return semverObj === null ? false : !semverObj.prerelease.length; + } + + function createSnapshotStableLabel(version) { + var label = version.label.replace(/.$/, 'x') + '-snapshot'; + + return label; + } } }; }; diff --git a/docs/config/services/deployments/debug.js b/docs/config/services/deployments/debug.js index f7381b129697..39cd327496e8 100644 --- a/docs/config/services/deployments/debug.js +++ b/docs/config/services/deployments/debug.js @@ -17,9 +17,9 @@ module.exports = function debugDeployment(getVersion) { '../angular-sanitize.js', '../angular-touch.js', '../angular-animate.js', - 'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js', + 'components/marked-' + getVersion('marked') + '/lib/marked.js', 'js/angular-bootstrap/dropdown-toggle.js', - 'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js', + 'components/lunr-' + getVersion('lunr') + '/lunr.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js', 'js/current-version-data.js', @@ -30,8 +30,8 @@ module.exports = function debugDeployment(getVersion) { ], stylesheets: [ 'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css', - 'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css', 'css/prettify-theme.css', + 'css/angular-topnav.css', 'css/docs.css', 'css/animations.css' ] diff --git a/docs/config/services/deployments/default.js b/docs/config/services/deployments/default.js index c489946d8884..181994f1e740 100644 --- a/docs/config/services/deployments/default.js +++ b/docs/config/services/deployments/default.js @@ -17,9 +17,9 @@ module.exports = function defaultDeployment(getVersion) { '../angular-sanitize.min.js', '../angular-touch.min.js', '../angular-animate.min.js', - 'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js', + 'components/marked-' + getVersion('marked') + '/marked.min.js', 'js/angular-bootstrap/dropdown-toggle.min.js', - 'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js', + 'components/lunr-' + getVersion('lunr') + '/lunr.min.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js', 'js/current-version-data.js', @@ -30,8 +30,8 @@ module.exports = function defaultDeployment(getVersion) { ], stylesheets: [ 'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css', - 'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css', 'css/prettify-theme.css', + 'css/angular-topnav.css', 'css/docs.css', 'css/animations.css' ] diff --git a/docs/config/services/deployments/jquery.js b/docs/config/services/deployments/jquery.js index b224830af43a..67263832e4f2 100644 --- a/docs/config/services/deployments/jquery.js +++ b/docs/config/services/deployments/jquery.js @@ -21,9 +21,9 @@ module.exports = function jqueryDeployment(getVersion) { '../angular-sanitize.min.js', '../angular-touch.min.js', '../angular-animate.min.js', - 'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js', + 'components/marked-' + getVersion('marked') + '/lib/marked.js', 'js/angular-bootstrap/dropdown-toggle.min.js', - 'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js', + 'components/lunr-' + getVersion('lunr') + '/lunr.min.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js', 'js/current-version-data.js', @@ -34,8 +34,8 @@ module.exports = function jqueryDeployment(getVersion) { ], stylesheets: [ 'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css', - 'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css', 'css/prettify-theme.css', + 'css/angular-topnav.css', 'css/docs.css', 'css/animations.css' ] diff --git a/docs/config/services/deployments/production.js b/docs/config/services/deployments/production.js index 747e7a1016bd..a37c127df593 100644 --- a/docs/config/services/deployments/production.js +++ b/docs/config/services/deployments/production.js @@ -7,24 +7,25 @@ var angularCodeUrl = '//code.angularjs.org/'; var cdnUrl = googleCdnUrl + versionInfo.cdnVersion; -// The plnkr examples must use the code.angularjs.org repo for the snapshot, -// and the cdn for the tagged version and, if the build is not tagged, the currentVersion. -// -// The currentVersion may not be available on the cdn (e.g. if built locally, or hasn't been pushed -// yet). This will lead to a 404, but this is preferable to loading a version with which the example -// might not work (possibly in subtle ways). -var examplesCdnUrl = versionInfo.currentVersion.isSnapshot ? - (angularCodeUrl + 'snapshot') : - (googleCdnUrl + (versionInfo.currentVersion.version || versionInfo.currentVersion)); +// The "examplesDependencyPath" here applies to the examples when they are opened in plnkr.co. +// The embedded examples instead always include the files from the *default* deployment, +// to ensure that the source files are always available. +// The plnkr examples must always use the code.angularjs.org source files. +// We cannot rely on the CDN files here, because they are not deployed by the time +// docs.angularjs.org and code.angularjs.org need them. +var versionPath = versionInfo.currentVersion.isSnapshot ? + 'snapshot' : + versionInfo.currentVersion.version; +var examplesDependencyPath = angularCodeUrl + versionPath + '/'; module.exports = function productionDeployment(getVersion) { return { name: 'production', examples: { commonFiles: { - scripts: [examplesCdnUrl + '/angular.min.js'] + scripts: [examplesDependencyPath + 'angular.min.js'] }, - dependencyPath: examplesCdnUrl + '/' + dependencyPath: examplesDependencyPath }, scripts: [ cdnUrl + '/angular.min.js', @@ -34,9 +35,9 @@ module.exports = function productionDeployment(getVersion) { cdnUrl + '/angular-sanitize.min.js', cdnUrl + '/angular-touch.min.js', cdnUrl + '/angular-animate.min.js', - 'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js', + 'components/marked-' + getVersion('marked') + '/marked.min.js', 'js/angular-bootstrap/dropdown-toggle.min.js', - 'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js', + 'components/lunr-' + getVersion('lunr') + '/lunr.min.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js', 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js', 'js/current-version-data.js', @@ -47,8 +48,8 @@ module.exports = function productionDeployment(getVersion) { ], stylesheets: [ 'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css', - 'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css', 'css/prettify-theme.css', + 'css/angular-topnav.css', 'css/docs.css', 'css/animations.css' ] diff --git a/docs/config/services/deployments/test.js b/docs/config/services/deployments/test.js new file mode 100644 index 000000000000..ba0805b5079a --- /dev/null +++ b/docs/config/services/deployments/test.js @@ -0,0 +1,40 @@ +'use strict'; + +module.exports = function testDeployment(getVersion) { + return { + name: 'test', + examples: { + commonFiles: { + scripts: ['../../../angular.js'] + }, + dependencyPath: '../../../' + }, + scripts: [ + '../angular.js', + '../angular-resource.js', + '../angular-route.js', + '../angular-cookies.js', + '../angular-mocks.js', + '../angular-sanitize.js', + '../angular-touch.js', + '../angular-animate.js', + 'components/marked-' + getVersion('marked') + '/lib/marked.js', + 'js/angular-bootstrap/dropdown-toggle.js', + 'components/lunr-' + getVersion('lunr') + '/lunr.js', + 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js', + 'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js', + 'js/current-version-data.js', + 'js/all-versions-data.js', + 'js/pages-data.js', + 'js/nav-data.js', + 'js/docs.js' + ], + stylesheets: [ + 'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css', + 'css/prettify-theme.css', + 'css/angular-topnav.css', + 'css/docs.css', + 'css/animations.css' + ] + }; +}; diff --git a/docs/config/services/getVersion.js b/docs/config/services/getVersion.js index 3471d716d245..4955f5d84d8d 100644 --- a/docs/config/services/getVersion.js +++ b/docs/config/services/getVersion.js @@ -4,14 +4,13 @@ var path = require('canonical-path'); /** * dgService getVersion * @description - * Find the current version of the bower component (or node module) + * Find the current version of the node module */ module.exports = function getVersion(readFilesProcessor) { - var basePath = readFilesProcessor.basePath; + var sourceFolder = path.resolve(readFilesProcessor.basePath, 'node_modules'); + var packageFile = 'package.json'; - return function(component, sourceFolder, packageFile) { - sourceFolder = path.resolve(basePath, sourceFolder || 'docs/bower_components'); - packageFile = packageFile || 'bower.json'; - return require(path.join(sourceFolder,component,packageFile)).version; + return function(component) { + return require(path.join(sourceFolder, component, packageFile)).version; }; }; diff --git a/docs/config/templates/app/error.template.html b/docs/config/templates/app/error.template.html index cd68fb9652b8..dc63c41b5f68 100644 --- a/docs/config/templates/app/error.template.html +++ b/docs/config/templates/app/error.template.html @@ -9,7 +9,7 @@

Error: {$ doc.namespace $}:{$ doc.name $}
{$ doc.formattedErrorMessage $}
-

Description

+

Description

{$ doc.description | marked $}
diff --git a/docs/config/templates/app/indexPage.template.html b/docs/config/templates/app/indexPage.template.html index 884763dcc838..126aab449e5e 100644 --- a/docs/config/templates/app/indexPage.template.html +++ b/docs/config/templates/app/indexPage.template.html @@ -1,3 +1,12 @@ +{# Macros #} +{%- macro addTag(name, attributes) %} + <{$ name $} + {%- for attrName, attrValue in attributes -%} + {$ ' ' + attrName $}="{$ attrValue $}" + {%- endfor -%} + > +{%- endmacro -%} + @@ -12,50 +21,39 @@ AngularJS + + {% for stylesheet in doc.stylesheets %} + {$- addTag('link', {rel: 'stylesheet', href: stylesheet, type: 'text/css'}) -$} + {% endfor %} + {% for script in doc.scripts %} + {$- addTag('script', {src: script}) -$} + {% endfor %} + + - +
-
- -
+ +
- + -
+
Show / Hide Table of Contents @@ -203,8 +193,9 @@

{{ key }}

-
Loading...
-
+
Loading …
+
There was an error loading this resource. Please try again later.
+
@@ -214,10 +205,10 @@

{{ key }}

Back to top

- Super-powered by Google ©2010-2016 + Super-powered by Google ©2010-2020 ( + ng-bind-template="v{{version}}" title="Changelog of this version of AngularJS"> )

diff --git a/docs/config/templates/app/sitemap.template.xml b/docs/config/templates/app/sitemap.template.xml new file mode 100644 index 000000000000..56953d903920 --- /dev/null +++ b/docs/config/templates/app/sitemap.template.xml @@ -0,0 +1,7 @@ + + + {%- for url in doc.urls %} + + https://docs.angularjs.org/{$ url $} + {% endfor %} + \ No newline at end of file diff --git a/docs/config/templates/ngdoc/api/api.template.html b/docs/config/templates/ngdoc/api/api.template.html index 3d93e863af17..0a622197b4e2 100644 --- a/docs/config/templates/ngdoc/api/api.template.html +++ b/docs/config/templates/ngdoc/api/api.template.html @@ -23,6 +23,7 @@

{$ doc.name $}

{% block description %}
+

Overview

{$ doc.description | marked $}
{% endblock %} @@ -51,7 +52,7 @@

Dependencies

{% block examples %} {%- if doc.examples %} -

Examples

+

{$ "Examples" if doc.examples | length > 1 else "Example" $}

{%- for example in doc.examples -%} {$ example | marked $} {%- endfor -%} diff --git a/docs/config/templates/ngdoc/api/directive.template.html b/docs/config/templates/ngdoc/api/directive.template.html index 0855acf49bbe..0a3480eee988 100644 --- a/docs/config/templates/ngdoc/api/directive.template.html +++ b/docs/config/templates/ngdoc/api/directive.template.html @@ -2,7 +2,7 @@ {% extends "api/api.template.html" %} {% block additional %} -

Directive Info

+

Directive Info