From 71e9ca0a913fbe7b9acd202d10dc60d11900d6e2 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 14:58:52 -0300 Subject: [PATCH 01/22] initial commit --- .gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e69de29b From 72ecf0ca49f4c7a3e7bd2dd192bd4129e7c30330 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 16:47:01 -0300 Subject: [PATCH 02/22] estrutura inicial do projeto com main controller e model carrinho --- .gitignore | 4 + assets/img/bata-bordada-floral.jpg | Bin 0 -> 44520 bytes assets/img/bata-bordada.jpg | Bin 0 -> 46337 bytes assets/img/conjunto-social.jpg | Bin 0 -> 31619 bytes assets/img/verstido-texturizado.jpg | Bin 0 -> 46701 bytes assets/img/vestido-azul.jpg | Bin 0 -> 35510 bytes assets/img/vestido-branco-basico.jpg | Bin 0 -> 29272 bytes assets/img/vestido-comteporaneo.jpg | Bin 0 -> 49644 bytes assets/img/vestido-floral-pequeno.jpg | Bin 0 -> 31051 bytes assets/img/vestido-listrado.jpg | Bin 0 -> 38489 bytes gulpfile.js | 0 index.html | 49 + js/app.js | 5 + js/config/route.config.js | 26 + js/controllers/main.controller.js | 7 + js/models/carrinho.model.js | 28 + package-lock.json | 3706 +++++++++++++++++++++++++ package.json | 32 + 18 files changed, 3857 insertions(+) create mode 100644 assets/img/bata-bordada-floral.jpg create mode 100644 assets/img/bata-bordada.jpg create mode 100644 assets/img/conjunto-social.jpg create mode 100644 assets/img/verstido-texturizado.jpg create mode 100644 assets/img/vestido-azul.jpg create mode 100644 assets/img/vestido-branco-basico.jpg create mode 100644 assets/img/vestido-comteporaneo.jpg create mode 100644 assets/img/vestido-floral-pequeno.jpg create mode 100644 assets/img/vestido-listrado.jpg create mode 100644 gulpfile.js create mode 100644 index.html create mode 100644 js/app.js create mode 100644 js/config/route.config.js create mode 100644 js/controllers/main.controller.js create mode 100644 js/models/carrinho.model.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index e69de29b..1033b96f 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,4 @@ +## NPM +node_modules/ +## Webstorm +.idea/ \ No newline at end of file diff --git a/assets/img/bata-bordada-floral.jpg b/assets/img/bata-bordada-floral.jpg new file mode 100644 index 0000000000000000000000000000000000000000..45495fd4618d313d727dd03bfc397a666315e2da GIT binary patch literal 44520 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 SGw=QHx}M(2`hT1FfB)G?U>wl^ literal 0 HcmV?d00001 diff --git a/assets/img/bata-bordada.jpg b/assets/img/bata-bordada.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e57125f907323317d52b89a765a0ae5cb83fdc5 GIT binary patch literal 46337 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 Xr=)s)I5)fQBkudY@7er4pD+K}LT^{h literal 0 HcmV?d00001 diff --git a/assets/img/verstido-texturizado.jpg b/assets/img/verstido-texturizado.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f3eabf7d6bde31de23ce0dd0a4074810608fbd65 GIT binary patch literal 46701 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

MsI5RhcD1KL#}VA;SlPXNl{Y}z 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 literal 0 HcmV?d00001 diff --git a/assets/img/vestido-azul.jpg b/assets/img/vestido-azul.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5fab277e45cb22fef919ff6241b0d25ec9dab731 GIT binary patch literal 35510 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 A`2YX_ literal 0 HcmV?d00001 diff --git a/assets/img/vestido-branco-basico.jpg b/assets/img/vestido-branco-basico.jpg new file mode 100644 index 0000000000000000000000000000000000000000..998f3ed37724205b8fca513fe89912391f6aebbb GIT binary patch literal 29272 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

  • A)QY-zAqXuwiNN=p_Z0x|Q&h_dcHq)e<~@_gRQldENE zT|LFlrV3APx9OV16B?~{e`nC;B`oun%S&h`3AI5tk5x)NnL!pwAl#%WdmP=$UjPsh zH*f&(A2)LeyZ)Zz(3GA%hx^1oM~6lIf&TBxAKm`&{>v?T&k)MT1uCnCk7A^gO>})W z?Y>yXZ{_}Jvb}@tm)JcxeHQCfp%qiAabZ}qHFX?C6gZf|R`Sga>7YR##VCk~7r;bE zrGi6N-Jhf~F>*<_Z1$-fu#94FgaFQ4!2k*X?`zLKnrB>^ZL{{Z4a zKo0@^9T-3W01iEXque`p_7p?|xqnAP`@g^PgZKXccSHPd-R$@MpViZ^;r{^4xcgtk z{we;~>-{}{;rjmo!}LG${?Y#ce_!~2Z|?X10Jo&Sf%XggeSz%#f8Wx7WBzrw@P60Z Y4{u)o0JLv!x7Z%v*U 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 E*-@~ literal 0 HcmV?d00001 diff --git a/assets/img/vestido-listrado.jpg b/assets/img/vestido-listrado.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27594f9bed39d775ccdd0b1e354d060cf46f9c61 GIT binary patch literal 38489 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 literal 0 HcmV?d00001 diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..e69de29b diff --git a/index.html b/index.html new file mode 100644 index 00000000..67565261 --- /dev/null +++ b/index.html @@ -0,0 +1,49 @@ + + + + + Desafio Front-End + + + + + + + + + + + + + + + + + + +
    +

    Profite - CNPJ 05.559.134/0001-60 End: Voluntários da Pátria, 301/702 Botafogo - RJ - 22270-000

    +
    + + + + + \ No newline at end of file diff --git a/js/app.js b/js/app.js new file mode 100644 index 00000000..5be62a2d --- /dev/null +++ b/js/app.js @@ -0,0 +1,5 @@ +(function () { + 'use strict' + + angular.module('profite', ['ngRoute']); +})(); \ No newline at end of file diff --git a/js/config/route.config.js b/js/config/route.config.js new file mode 100644 index 00000000..c8250176 --- /dev/null +++ b/js/config/route.config.js @@ -0,0 +1,26 @@ +(function(){ + 'use strict'; + + angular + .module('profite') + .config(routeConfig) + + /** @ngInject */ + function routeConfig($routeProvider, $locationProvider){ + + $locationProvider.hashPrefix(""); + + $routeProvider.when('/home', { + templateUrl: 'view/home.html', + controllerAs: 'vm', + controller: 'homeCtrl' + }); + $routeProvider.when('/carrinho', { + templateUrl: 'view/carrinho.html', + controllerAs: 'vm', + controller: 'carrinhoCtrl' + }); + $routeProvider.otherwise({redirectTo: '/home'}); + } + +}()); \ No newline at end of file diff --git a/js/controllers/main.controller.js b/js/controllers/main.controller.js new file mode 100644 index 00000000..8c4a9d13 --- /dev/null +++ b/js/controllers/main.controller.js @@ -0,0 +1,7 @@ +angular.module('profite') + .controller('mainCtrl', function ($scope, $routeParams, Carrinho) { + $scope.qtdCompra = 0; + $scope.$on('ListaCompras', function () { + $scope.qtdCompra = Carrinho.count(); + }); + }); \ No newline at end of file diff --git a/js/models/carrinho.model.js b/js/models/carrinho.model.js new file mode 100644 index 00000000..d2fb1785 --- /dev/null +++ b/js/models/carrinho.model.js @@ -0,0 +1,28 @@ +angular.module('profite') + .factory('Carrinho', function ($rootScope) { + var ListaCompra = []; + + function set(item) { + ListaCompra.push(item); + $rootScope.$broadcast('ListaCompras'); + }; + + function get() { + return ListaCompra; + }; + + function count() { + return ListaCompra.length; + }; + + function remove(item) { + ListaCompra.splice(ListaCompra.indexOf(item), 1); + }; + + return { + set: set, + get: get, + count: count, + remove: remove + } + }); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..f4e2b320 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3706 @@ +{ + "name": "desafio-profite", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accord": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", + "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", + "dev": true, + "requires": { + "convert-source-map": "1.5.1", + "glob": "7.1.2", + "indx": "0.2.3", + "lodash.clone": "4.5.0", + "lodash.defaults": "4.2.0", + "lodash.flatten": "4.4.0", + "lodash.merge": "4.6.1", + "lodash.partialright": "4.2.1", + "lodash.pick": "4.4.0", + "lodash.uniq": "4.5.0", + "resolve": "1.5.0", + "semver": "5.5.0", + "uglify-js": "2.8.29", + "when": "3.7.8" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + } + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "angular": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.6.9.tgz", + "integrity": "sha512-6igWH2GIsxV+J38wNWCh8oyjaZsrIPIDO35twloIUyjlF2Yit6UyLAWujHP05ma/LFxTsx4NtYibRoMNBXPR1A==" + }, + "angular-route": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.6.9.tgz", + "integrity": "sha512-giE0PD0T17ZvtJmAB6di27YPPSzYC1kP1BDpM2ZIGZUbs02PvJWRIgYA8z3dy9olzCS35TOwxmE2fJoHWTMm1A==" + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true, + "optional": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "dev": true, + "requires": { + "lodash": "4.17.5" + }, + "dependencies": { + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + } + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true, + "optional": true + }, + "atob": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", + "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "kind-of": "6.0.2", + "repeat-element": "1.1.2", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" + } + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true, + "optional": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "clean-css": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz", + "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } + }, + "clone": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", + "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "dev": true, + "requires": { + "inherits": "2.0.3", + "process-nextick-args": "2.0.0", + "readable-stream": "2.3.5" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "optional": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.0.tgz", + "integrity": "sha512-7B1ilBwtYSbetCgTY1NJFg+gVpestg0fdA1MhC1Vs4ssyfSXnCAjFr+QcQM9/RedXC0EaUx1sG8Smgw2VfgKEg==", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-with-sourcemaps": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.5.tgz", + "integrity": "sha512-YtnS0VEY+e2Khzsey/6mra9EoM6h/5gxaC0e3mcHpA5yfDxafhygytNmcJWodvUgyXzSiL5MSkPO6bQGgfliHw==", + "dev": true, + "requires": { + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "optional": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "1.0.3" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deprecated": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", + "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "1.1.14" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "end-of-stream": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "dev": true, + "requires": { + "once": "1.3.3" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "optional": true, + "requires": { + "prr": "1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fancy-log": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.2.tgz", + "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", + "dev": true, + "requires": { + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "time-stamp": "1.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "find-index": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", + "dev": true + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.9", + "resolve-dir": "1.0.1" + } + }, + "fined": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.0.tgz", + "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", + "dev": true + }, + "flagged-respawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.0.tgz", + "integrity": "sha1-Tnmumy6zi/hrO7Vr8+ClaqX8q9c=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "dev": true, + "requires": { + "globule": "0.1.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.3.3" + } + }, + "glob-stream": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "dev": true, + "requires": { + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + } + } + }, + "glob-watcher": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", + "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "dev": true, + "requires": { + "gaze": "0.5.2" + } + }, + "glob2base": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "dev": true, + "requires": { + "find-index": "0.1.1" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.1", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.0" + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "dev": true, + "requires": { + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" + }, + "dependencies": { + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "dev": true, + "requires": { + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "glogg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.1.tgz", + "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "dev": true, + "requires": { + "natives": "1.1.2" + } + }, + "gulp": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "dev": true, + "requires": { + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.1.0", + "liftoff": "2.5.0", + "minimist": "1.2.0", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" + } + }, + "gulp-clean-css": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.9.3.tgz", + "integrity": "sha512-mw5Qrio7W3rvswmVlZ7eaxOhBIp6zQMBFLgcHoi/xbOtaKT5zmElkHt8mvbRre7fMt5eLgppIkW+j9Cm+O/UqQ==", + "dev": true, + "requires": { + "clean-css": "4.1.11", + "plugin-error": "1.0.1", + "through2": "2.0.3", + "vinyl-sourcemaps-apply": "0.2.1" + } + }, + "gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "requires": { + "concat-with-sourcemaps": "1.0.5", + "through2": "2.0.3", + "vinyl": "2.1.0" + }, + "dependencies": { + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", + "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, + "requires": { + "clone": "2.1.1", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.2", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + } + } + }, + "gulp-less": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.0.tgz", + "integrity": "sha512-cHSgNy6TEGpjuGkjBEhWBtt//YPxtw/Og4VQUlJ2w5izQOk1S+m1v98c4bAYsG79M944mv5x/Ct84RWiF8UvCw==", + "dev": true, + "requires": { + "accord": "0.29.0", + "less": "3.0.1", + "object-assign": "4.1.1", + "plugin-error": "0.1.2", + "replace-ext": "1.0.0", + "through2": "2.0.3", + "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + } + } + }, + "gulp-shell": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.6.5.tgz", + "integrity": "sha512-f3m1WcS0o2B72/PGj1Jbv9zYR9rynBh/EQJv64n01xQUo7j7anols0eww9GG/WtDTzGVQLrupVDYkifRFnj5Zg==", + "dev": true, + "requires": { + "async": "2.6.0", + "chalk": "2.3.2", + "fancy-log": "1.3.2", + "lodash": "4.17.5", + "lodash.template": "4.4.0", + "plugin-error": "0.1.2", + "through2": "2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "chalk": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", + "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.3.0" + } + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + }, + "lodash.template": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.templatesettings": "4.1.0" + } + }, + "lodash.templatesettings": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", + "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0" + } + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + } + }, + "supports-color": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", + "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "gulp-uglify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-3.0.0.tgz", + "integrity": "sha1-DfAzHXKg0wLj434QlIXd3zPG0co=", + "dev": true, + "requires": { + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash": "4.17.5", + "make-error-cause": "1.2.2", + "through2": "2.0.3", + "uglify-js": "3.3.15", + "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-js": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.15.tgz", + "integrity": "sha512-bqtBCAINYXX/OkdnqMGpbXr+OPWc00hsozRpk+dAtfnbdk2jjKiLmyOkQ7zamg648lVMnzATL8JrSN6LmaVpYA==", + "dev": true, + "requires": { + "commander": "2.15.0", + "source-map": "0.6.1" + } + } + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.2", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", + "replace-ext": "0.0.1", + "through2": "2.0.3", + "vinyl": "0.5.3" + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "1.0.1" + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "optional": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "1.0.0" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.14.1" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "indx": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/indx/-/indx-0.2.3.tgz", + "integrity": "sha1-Fdz1bunPZcAjTFE8J/vVgOcPvFA=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.3.3", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "1.0.0", + "is-windows": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-odd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", + "dev": true, + "requires": { + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true, + "optional": true + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true, + "optional": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "less": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.0.1.tgz", + "integrity": "sha512-qUR4uNv88/c0mpnGOULgMLRXXSD6X0tYo4cVrokzsvn68+nuj8rskInCSe2eLAVYWGD/oAlq8P7J/FeZ/euKiw==", + "dev": true, + "requires": { + "errno": "0.1.7", + "graceful-fs": "4.1.11", + "image-size": "0.5.5", + "mime": "1.6.0", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.81.0", + "source-map": "0.5.7" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true, + "optional": true + } + } + }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "dev": true, + "requires": { + "extend": "3.0.1", + "findup-sync": "2.0.0", + "fined": "1.1.0", + "flagged-respawn": "1.0.0", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.5.0" + } + }, + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "dev": true + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.merge": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", + "dev": true + }, + "lodash.partialright": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.partialright/-/lodash.partialright-4.2.1.tgz", + "integrity": "sha1-ATDYDoM2MmTUAHTzKbij56ihzEs=", + "dev": true + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "make-error": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", + "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", + "dev": true + }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "1.3.4" + } + }, + "make-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.0.tgz", + "integrity": "sha1-V7713IXSOSO6I3ZzJNjo+PPZaUs=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "1.0.1" + } + }, + "micromatch": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.9.tgz", + "integrity": "sha512-SlIz6sv5UPaAVVFRKodKjCg48EbNoIhgetzfK/Cy0v5U52Z6zB136M8tp0UC9jM53LYbmIRihJszvvqpKkfm9g==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.1", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "1.33.0" + } + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, + "nanomatch": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, + "natives": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.2.tgz", + "integrity": "sha512-5bRASydE1gu6zPOenLN043++J8xj1Ob7ArkfdYO3JN4DF5rDmG7bMoiybkTyD+GnXQEMixVeDHMzuqm6kpBmiA==", + "dev": true + }, + "normalize.css": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.0.tgz", + "integrity": "sha512-iXcbM3NWr0XkNyfiSBsoPezi+0V92P9nj84yVV1/UZxRUrGczgX/X91KMAGM0omWLY2+2Q1gKD/XRn4gQRDB2A==" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true, + "optional": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "1.0.0", + "make-iterator": "1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "orchestrator": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", + "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "dev": true, + "requires": { + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.1" + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "0.1.2" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true, + "optional": true + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "1.1.0", + "arr-diff": "4.0.0", + "arr-union": "3.1.0", + "extend-shallow": "3.0.2" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "2.0.6" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true, + "optional": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true, + "optional": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.5.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "resolve": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", + "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "0.1.4" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "0.1.15" + } + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "sequencify": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", + "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.1", + "use": "3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "optional": true, + "requires": { + "hoek": "2.16.3" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", + "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", + "dev": true, + "requires": { + "atob": "2.0.3", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sparkles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", + "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + } + }, + "sshpk": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "dev": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "stream-consume": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.5", + "xtend": "4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz", + "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, + "tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" + } + } + } + }, + "unique-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", + "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", + "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", + "dev": true, + "requires": { + "kind-of": "6.0.2" + } + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", + "dev": true, + "optional": true + }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "requires": { + "user-home": "1.1.1" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "1.0.3", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-fs": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", + "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", + "dev": true, + "requires": { + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.1" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "when": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", + "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", + "dev": true + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..35f1202b --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "desafio-profite", + "version": "1.0.0", + "description": "Desafio para o processo seletivo de Desenvolvedor Front-End na Profite", + "main": "gulpfile.js", + "dependencies": { + "angular": "^1.6.9", + "angular-route": "^1.6.9", + "normalize.css": "^8.0.0" + }, + "devDependencies": { + "gulp": "^3.9.1", + "gulp-clean-css": "^3.9.3", + "gulp-concat": "^2.6.1", + "gulp-less": "^4.0.0", + "gulp-shell": "^0.6.5", + "gulp-uglify": "^3.0.0" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/patrick-narciso/get-devs.git" + }, + "author": "Patrick Narciso", + "license": "ISC", + "bugs": { + "url": "https://github.com/patrick-narciso/get-devs/issues" + }, + "homepage": "https://github.com/patrick-narciso/get-devs#readme" +} From 7b2db343b5e2c08fea62513991be0bd147f0b88b Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 16:58:00 -0300 Subject: [PATCH 03/22] controller de carrinho --- js/controllers/carrinho.controller.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 js/controllers/carrinho.controller.js diff --git a/js/controllers/carrinho.controller.js b/js/controllers/carrinho.controller.js new file mode 100644 index 00000000..0d911974 --- /dev/null +++ b/js/controllers/carrinho.controller.js @@ -0,0 +1,18 @@ +angular.module('profite') + .controller('carrinhoCtrl', function ($scope, Carrinho) { + $scope.ListaCompras = Carrinho.get(); + + $scope.total = function () { + var t = 0; + for(var i in $scope.ListaCompras){ + t += $scope.ListaCompras[i].pagamento.por; + } + return "R$ " + t.toFixed(2).replace(".", ","); + }; + + $scope.remove = function (item) { + Carrinho.remove(item); + $scope.ListaCompras = Carrinho.get(); + $scope.$emit('ListaCompras'); + }; + }); \ No newline at end of file From 49ad3879847d2c44eb5e632b44492362c2eda9e6 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 19:17:39 -0300 Subject: [PATCH 04/22] =?UTF-8?q?cria=C3=A7=C3=A3o=20model=20de=20cores=20?= =?UTF-8?q?e=20value=20da=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/models/carrinho.model.js | 8 ++++---- js/models/cores.model.js | 39 +++++++++++++++++++++++++++++++++++++ js/models/models.js | 2 ++ js/value/config.value.js | 4 ++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 js/models/cores.model.js create mode 100644 js/models/models.js create mode 100644 js/value/config.value.js diff --git a/js/models/carrinho.model.js b/js/models/carrinho.model.js index d2fb1785..f1ad0710 100644 --- a/js/models/carrinho.model.js +++ b/js/models/carrinho.model.js @@ -5,19 +5,19 @@ angular.module('profite') function set(item) { ListaCompra.push(item); $rootScope.$broadcast('ListaCompras'); - }; + } function get() { return ListaCompra; - }; + } function count() { return ListaCompra.length; - }; + } function remove(item) { ListaCompra.splice(ListaCompra.indexOf(item), 1); - }; + } return { set: set, diff --git a/js/models/cores.model.js b/js/models/cores.model.js new file mode 100644 index 00000000..2cd4bec5 --- /dev/null +++ b/js/models/cores.model.js @@ -0,0 +1,39 @@ +angular.module('profite') + .factory('Cores', function ($q, $http, config) { + var def = $q.defer(); + var listaCores = []; + + function getService() { + if (listaCores.length) { + def.resolve(listaCores); + } else { + $http + .get(config.apiUrl + "/cores") + .success(function (res) { + listaCores = res; + def.resolve(res); + }).error(function (err) { + listaCores = err; + def.resolve(err); + }); + } + + } + + function get() { + def.promise; + } + + function find(busca) { + listaCores.map(function (item) { + return item.id === busca.id; + }); + } + + getService(); + + return { + get: get, + find: find + } + }); \ No newline at end of file diff --git a/js/models/models.js b/js/models/models.js new file mode 100644 index 00000000..e0dbafbc --- /dev/null +++ b/js/models/models.js @@ -0,0 +1,2 @@ +angular.module('profite') + .factory() \ No newline at end of file diff --git a/js/value/config.value.js b/js/value/config.value.js new file mode 100644 index 00000000..6a2c5aeb --- /dev/null +++ b/js/value/config.value.js @@ -0,0 +1,4 @@ +angular.module('profite') + .value('config', { + apiUrl: "http://private-4586c-profite.apiary-mock.com" + }); \ No newline at end of file From d5ec3b2eed46e9271a446bee83e87db8788153c1 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 19:23:32 -0300 Subject: [PATCH 05/22] =?UTF-8?q?cria=C3=A7=C3=A3o=20model=20de=20pre?= =?UTF-8?q?=C3=A7o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/models/preco.model.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 js/models/preco.model.js diff --git a/js/models/preco.model.js b/js/models/preco.model.js new file mode 100644 index 00000000..c0029a8c --- /dev/null +++ b/js/models/preco.model.js @@ -0,0 +1,38 @@ +angular.module('profite') + .factory('Preco', function ($q, $http, config) { + var def = $q.defer(); + var listaPreco = []; + function getService() { + if(listaPreco.length){ + def.resolve( listaPreco ); + }else{ + $http + .get(config.apiUrl + "/faixa-preco") + .success(function (res) { + listaPreco = res; + def.resolve(res); + }) + .error(function (err) { + def.reject(err); + }); + } + } + + function get() { + def.promise; + } + + function find(busca) { + listaPreco.map(function (item) { + return item.id === busca.id; + }); + } + + getService(); + + return { + get: get, + find: find + } + + }); \ No newline at end of file From f4a9df0905022fc55d96c3008c3790e8c4f8883f Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 19:33:55 -0300 Subject: [PATCH 06/22] =?UTF-8?q?cria=C3=A7=C3=A3o=20model=20de=20produtos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/models/produtos.model.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 js/models/produtos.model.js diff --git a/js/models/produtos.model.js b/js/models/produtos.model.js new file mode 100644 index 00000000..a8e4952a --- /dev/null +++ b/js/models/produtos.model.js @@ -0,0 +1,25 @@ +angular.module('profite') + .factory('Produtos', function($http, config){ + + function get(skip, success, error) { + if(skip === 1) { + $http.get(config.apiUrl + "/produtos") + .success(function (res) { + success(res); + }).error(function (err) { + error(err); + }); + } else if(skip === 2) { + $http.get(config.apiUrl + "/mais-produtos") + .success(function (res) { + success(res); + }).error(function (err) { + error(err); + }); + } + + return { + get: get + } + } + }); \ No newline at end of file From 2077a93dac8208adad6e809fb75c7bb1056f46ae Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 19:37:11 -0300 Subject: [PATCH 07/22] =?UTF-8?q?cria=C3=A7=C3=A3o=20model=20de=20tamanhos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/models/preco.model.js | 8 ++++---- js/models/tamanhos.model.js | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 js/models/tamanhos.model.js diff --git a/js/models/preco.model.js b/js/models/preco.model.js index c0029a8c..e3c9fdd5 100644 --- a/js/models/preco.model.js +++ b/js/models/preco.model.js @@ -3,13 +3,13 @@ angular.module('profite') var def = $q.defer(); var listaPreco = []; function getService() { - if(listaPreco.length){ - def.resolve( listaPreco ); + if(listaTamanhos.length){ + def.resolve( listaTamanhos ); }else{ $http .get(config.apiUrl + "/faixa-preco") .success(function (res) { - listaPreco = res; + listaTamanhos = res; def.resolve(res); }) .error(function (err) { @@ -23,7 +23,7 @@ angular.module('profite') } function find(busca) { - listaPreco.map(function (item) { + listaTamanhos.map(function (item) { return item.id === busca.id; }); } diff --git a/js/models/tamanhos.model.js b/js/models/tamanhos.model.js new file mode 100644 index 00000000..1bcecd86 --- /dev/null +++ b/js/models/tamanhos.model.js @@ -0,0 +1,38 @@ +angular.module('profite') + .factory('Tamanhos', function ($q, $http, config) { + var def = $q.defer(); + var listaTamanhos = []; + function getService() { + if(listaTamanhos.length){ + def.resolve( listaTamanhos ); + }else{ + $http + .get(config.apiUrl + "/tamanhos") + .success(function (res) { + listaTamanhos = res; + def.resolve(res); + }) + .error(function (err) { + def.reject(err); + }); + } + } + + function get() { + def.promise; + } + + function find(busca) { + listaTamanhos.map(function (item) { + return item.id === busca.id; + }); + } + + getService(); + + return { + get: get, + find: find + } + + }); \ No newline at end of file From 5b72f85108b04bf557cd6d35adac611e3294826f Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 19:40:18 -0300 Subject: [PATCH 08/22] =?UTF-8?q?altera=C3=A7=C3=B5es=20na=20factory=20de?= =?UTF-8?q?=20models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/models/models.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/js/models/models.js b/js/models/models.js index e0dbafbc..4040e6a8 100644 --- a/js/models/models.js +++ b/js/models/models.js @@ -1,2 +1,10 @@ angular.module('profite') - .factory() \ No newline at end of file + .factory('Model', function (Cores, Tamanhos, Preco, Produtos, Carrinho) { + return { + Cores: Cores, + Tamanhos: Tamanhos, + Preco: Preco, + Produtos: Produtos, + Carrinho: Carrinho + } + }); \ No newline at end of file From 08b0bdd3ea60e6c8cf1c9bb1bea9d35e984a266d Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 19:49:21 -0300 Subject: [PATCH 09/22] =?UTF-8?q?inclus=C3=A3o=20de=20contants=20e=20confi?= =?UTF-8?q?g=20no=20app.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/app.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/js/app.js b/js/app.js index 5be62a2d..18cb609e 100644 --- a/js/app.js +++ b/js/app.js @@ -1,5 +1,16 @@ (function () { - 'use strict' + 'use strict'; - angular.module('profite', ['ngRoute']); + angular.module('profite', ['ngRoute']) + .run(['$rootScope', function ($rootScope) { + $rootScope.listaCompra = []; + }]) + .config(['$compileProvider', function ($compileProvider) { + $compileProvider.debugInfoEnabled(false); + }]) + .constant('OrdenarOpt', [ + {id: 1, label: "Mais Recentes"}, + {id: 2, label: "Menor Preço"}, + {id: 3, label: "Maior Preço"} + ]); })(); \ No newline at end of file From 582d89c08fb0dca72580c2cd0bd095ef0f437079 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 20:02:21 -0300 Subject: [PATCH 10/22] =?UTF-8?q?inclus=C3=A3o=20do=20service=20mobile.sup?= =?UTF-8?q?port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/services/mobile.support.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 js/services/mobile.support.js diff --git a/js/services/mobile.support.js b/js/services/mobile.support.js new file mode 100644 index 00000000..1d9bba2f --- /dev/null +++ b/js/services/mobile.support.js @@ -0,0 +1,28 @@ +angular.module('Profite') + .factory('MobileSupport', function () { + return { + + abreFiltroMobile: function abreFiltroMobile($event) { + var box = angular.element($event.target.nextElementSibling); + box.addClass('open-filter'); + }, + + fechaFiltroMobile: function fechaFiltroMobile($event) { + var box = angular.element($event.target.parentElement); + box.removeClass('open-filter'); + }, + + troggleCategoriaFiltroMobile: function troggleCategoriaFiltroMobile($event) { + var lista = angular.element($event.target.nextElementSibling); + var span = angular.element($event.target.children); + if (lista.hasClass('abre-lista')) { + lista.removeClass('abre-lista'); + span.text("+"); + } else { + lista.addClass('abre-lista'); + span.text("-"); + } + } + + }; + }); \ No newline at end of file From 41bd5867891d4d0dcc9cf707075074f187f45ba1 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 20:06:22 -0300 Subject: [PATCH 11/22] =?UTF-8?q?inclus=C3=A3o=20do=20service=20filtro.sup?= =?UTF-8?q?port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/services/filtro.support.js | 92 +++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 js/services/filtro.support.js diff --git a/js/services/filtro.support.js b/js/services/filtro.support.js new file mode 100644 index 00000000..8774dab8 --- /dev/null +++ b/js/services/filtro.support.js @@ -0,0 +1,92 @@ +angular.module('Profite') + .factory('Filtro', function ($interval) { + return { + filtroCor: function filtroCor(scope) { + if (scope.corSelecionada.id > 0) { + var temp = scope.precoSelecionado.id > 0 || scope.tamanhoSelecionado.id > 0 ? angular.copy(scope.Produtos) : angular.copy(scope.produtosCopia); + scope.Produtos = []; + for (var i in temp) { + for (var j in temp[i].caracteristicas.cores) { + if (temp[i].caracteristicas.cores[j] === scope.corSelecionada.id) { + scope.Produtos.push(temp[i]); + break; + } + } + } + } + }, + + filtroTamanho: function filtroTamanho(scope) { + if (scope.tamanhoSelecionado.id > 0) { + var temp = scope.precoSelecionado.id > 0 || scope.corSelecionada.id > 0 ? angular.copy(scope.Produtos) : angular.copy(scope.produtosCopia); + scope.Produtos = []; + for (var i in temp) { + for (var j in temp[i].caracteristicas.tamanho) { + if (temp[i].caracteristicas.tamanho[j] === scope.tamanhoSelecionado.id) { + scope.Produtos.push(temp[i]); + break; + } + } + } + } + }, + + filtroPreco: function filtroPreco(scope) { + if (scope.precoSelecionado.id > 0) { + var temp = scope.tamanhoSelecionado.id > 0 || scope.corSelecionada.id > 0 ? angular.copy(scope.Produtos) : angular.copy(scope.produtosCopia); + scope.Produtos = []; + for (var i in temp) { + if (scope.precoSelecionado.to !== null) { + + if (temp[i].pagamento.por >= scope.precoSelecionado.from && temp[i].pagamento.por <= scope.precoSelecionado.to) { + scope.Produtos.push(temp[i]); + } + } else if (scope.precoSelecionado.to === null) { + + if (temp[i].pagamento.por >= scope.precoSelecionado.from) { + scope.Produtos.push(temp[i]); + } + } + } + } + }, + + semFiltro: function semFiltro(scope) { + if (scope.precoSelecionado.id < 1 && scope.tamanhoSelecionado.id < 1 && scope.corSelecionada.id < 1) { + scope.Produtos = angular.copy(scope.produtosCopia); + } + }, + + ordenar: function ordenar(scopectrl) { + if (scopectrl.ordenarTipo > 0) { + switch (scopectrl.ordenarTipo) { + case 1: + scopectrl.Produtos.sort(function (a, b) { + return new Date(a.produto.data) < new Date(b.produto.data); + }); + scopectrl.$digest(); + break; + case 2: + scopectrl.Produtos.sort(function (a, b) { + return a.pagamento.por > b.pagamento.por; + }); + scopectrl.$digest(); + break; + case 3: + scopectrl.Produtos.sort(function (a, b) { + return a.pagamento.por < b.pagamento.por; + }); + scopectrl.$digest(); + break; + } + } + }, + + semResultado: function semResultado(scope) { + if (scope.Produtos !== null && !scope.Produtos.length) { + scope.Produtos = null; + } + } + + }; + }); \ No newline at end of file From 7c73fffe4bd2ce7cc681abd76c62a55483b98ebe Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 20:15:09 -0300 Subject: [PATCH 12/22] =?UTF-8?q?inclus=C3=A3o=20do=20service=20controller?= =?UTF-8?q?.support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/services/controller.support.js | 73 +++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 js/services/controller.support.js diff --git a/js/services/controller.support.js b/js/services/controller.support.js new file mode 100644 index 00000000..e5be9970 --- /dev/null +++ b/js/services/controller.support.js @@ -0,0 +1,73 @@ +angular.module('Profite') + .factory('CTRLSupport', function (Filtro) { + var encontraProdutoAdicionado = function encontraProdutoAdicionado(atual, model) { + for (var j in model.Carrinho.get()) { + var item = model.Carrinho.get()[j]; + if (atual.produto.id === item.produto.id) { + atual.produto.addCart = true; + } + } + }; + return { + msgErro: function msgErro(msg, scope, time) { + scope.mensagemErro = msg; + time(function () { + scope.mensagemErro = null; + }, 4000); + }, + + getProdutos: function getProdutos(skip, scope, Util, Model, time) { + Model.Produtos.get(skip, function (res) { + for (var i in res) { + res[i].produto.addCart = false; + scope.Produtos.push(res[i]); + scope.produtosCopia.push(res[i]); + } + scope.pagina++; + Util.filtro(scope); + Util.existsCompras(Model, scope); + }, function (err) { + Util.msgErro("Não há mais produtos.", scope, time); + console.error(err); + }); + }, + + filtro: function filtro(scope, id) { + Filtro.filtroCor(scope); + Filtro.filtroTamanho(scope); + Filtro.filtroPreco(scope); + Filtro.semResultado(scope); + Filtro.ordenar(scope, id); + Filtro.semFiltro(scope); + }, + + existCompras: function existCompras(model, scope) { + if (model.Carrinho.count() > 0) { + for (var i in scope.Produtos) { + encontraProdutoAdicionado(scope.Produtos[i], model); + } + } + }, + + carregaCarrinho: function carregaCarrinho(next, Util) { + if (next.$$route.originalPath !== undefined) { + var path = next.$$route.originalPath.replace("/", ""); + if (angular.equals(path, "home")) { + Util.existsCompras(Model, $scope); + } + } + }, + + limpaFiltros: function limpaFiltros(scope, util) { + scope.corSelecionada = { id: 0 }; + scope.precoSelecionado = { id: 0 }; + scope.tamanhoSelecionado = { id: 0 }; + scope.ordenarTipo = null; + util.filtro(scope); + }, + + showBtnLimpa: function showBtnLimpa(scope) { + return scope.corSelecionada.id > 0 || scope.precoSelecionado.id > 0 || scope.tamanhoSelecionado.id > 0 || scope.ordenarTipo > 0; + } + }; + }); \ No newline at end of file From 9daa7f92a819d6e74f02d339e42978dd090e3562 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 20:22:19 -0300 Subject: [PATCH 13/22] =?UTF-8?q?inclus=C3=A3o=20do=20service=20Util?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/services/Util.js | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 js/services/Util.js diff --git a/js/services/Util.js b/js/services/Util.js new file mode 100644 index 00000000..4ba12cb2 --- /dev/null +++ b/js/services/Util.js @@ -0,0 +1,57 @@ +angular.module('Profite') + .factory('Util', function(Filtro, Collapse, MobileSupport, CTRLSupport){ + return { + msgErro: function msgErro(msg, scope, time) { + CTRLSupport.msgErro(msg, scope, time); + }, + + trataClasseBtn: function trataClasseBtn(ocasiao) { + Collapse.trataClasseBtn(ocasiao); + }, + + collapse: function collapse(itensCollapseCor) { + Collapse.collapse(itensCollapseCor); + }, + + troggleColapse: function troggleColapse(scope, Util) { + Collapse.troggleColapse(scope, Util); + }, + + getProdutos: function getProdutos(skip, scope, Util, Model, time) { + CTRLSupport.getProdutos(skip, scope, Util, Model, time); + }, + + filtro: function filtro(scope, id) { + CTRLSupport.filtro(scope, id); + }, + + abreFiltroMobile: function abreFiltroMobile($event) { + MobileSupport.abreFiltroMobile($event); + }, + + fechaFiltroMobile: function fechaFiltroMobile($event) { + MobileSupport.fechaFiltroMobile($event); + }, + + troggleCategoriaFiltroMobile: function troggleCategoriaFiltroMobile($event) { + MobileSupport.troggleCategoriaFiltroMobile($event); + }, + + existsCompras: function existsCompras(model, scope) { + CTRLSupport.existCompras(model, scope); + }, + + carregaCarrinho: function carregaCarrinho(next, Util) { + CTRLSupport.carregaCarrinho(next, Util); + }, + + limpaFiltros: function limpaFiltros(scope, util) { + CTRLSupport.limpaFiltros(scope, util); + }, + + showBtnLimpa: function showBtnLimpa(scope) { + return CTRLSupport.showBtnLimpa(scope); + } + + }; + }); \ No newline at end of file From ae50e012fbb1df7751b9e1b553cd51f016649ad9 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 20:24:02 -0300 Subject: [PATCH 14/22] =?UTF-8?q?inclus=C3=A3o=20do=20service=20collapse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/services/collapse.js | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 js/services/collapse.js diff --git a/js/services/collapse.js b/js/services/collapse.js new file mode 100644 index 00000000..712c2150 --- /dev/null +++ b/js/services/collapse.js @@ -0,0 +1,48 @@ +angular.module('Profite') + .factory('Collapse', function () { + return { + trataClasseBtn: function trataClasseBtn(ocasiao) { + var bt = document.querySelector("#btnCores"); + var seta = bt.querySelector('.arrow'); + var classeSeta = seta.className; + if (!ocasiao) { + classeSeta = classeSeta.split(" "); + classeSeta.splice(1, classeSeta.indexOf('open')); + seta.className = classeSeta.join(" "); + } else { + seta.className += " open"; + } + }, + + collapse: function collapse(itensCollapseCor) { + var boxcores = document.querySelector("#lista-cores"); + angular.element(boxcores).ready(function () { + var listaCores = boxcores.querySelectorAll("li"); + if (listaCores.length) { + for (var i = 0; i < listaCores.length; i++) { + if (i > itensCollapseCor && listaCores[i].className.indexOf("corextra") < 0) { + angular.element(listaCores[i]).addClass('corextra hide'); + } + } + } + }); + }, + + troggleColapse: function troggleColapse(scope, Util) { + var lista = document.querySelectorAll(".corextra"); + for (var i = 0; i < lista.length; i++) { + if (lista[i].className.indexOf("hide") < 0) { + angular.element(lista[i]).addClass('hide'); + scope.txtExpandCores = "Ver todas as cores"; + } else if (lista[i].className.indexOf("hide") > 0) { + var el = angular.element(lista[i]); + el.removeClass('hide'); + scope.txtExpandCores = "Ocultar as cores"; + } + } + scope.coresOpen = !scope.coresOpen; + Util.trataClasseBtn(scope.coresOpen); + } + + }; + }); \ No newline at end of file From c342052de583401e7f0ee536cd47a937143c361c Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sat, 17 Mar 2018 20:27:32 -0300 Subject: [PATCH 15/22] =?UTF-8?q?inclus=C3=A3o=20do=20controller=20home?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/controllers/home.controller.js | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 js/controllers/home.controller.js diff --git a/js/controllers/home.controller.js b/js/controllers/home.controller.js new file mode 100644 index 00000000..db61a5da --- /dev/null +++ b/js/controllers/home.controller.js @@ -0,0 +1,90 @@ +angular.module('Profite') + .controller('homeCtrl', function ($scope, $rootScope, OrdenarOpt, Model, $timeout, Util) { + var itensCollapseCor = 5; + $scope.ordernarList = OrdenarOpt; + $scope.ordenarTipo = null; + $scope.Cores = []; + $scope.Precos = []; + $scope.Tamanhos = []; + $scope.Produtos = []; + $scope.produtosCopia = []; + $scope.corSelecionada = { id: 0 }; + $scope.precoSelecionado = { id: 0 }; + $scope.tamanhoSelecionado = { id: 0 }; + $scope.pagina = 1; + $scope.txtExpandCores = "Ver todas as cores"; + $scope.coresOpen = false; + $scope.mensagemErro = null; + + Model.Cores.get().then(function (res) { + $scope.Cores = res;Util.collapse(itensCollapseCor); + }, function (err) { + console.log(err); + }); + Model.Preco.get().then(function (res) { + $scope.Precos = res; + }, function (err) { + console.error(err); + }); + Model.Tamanhos.get().then(function (res) { + $scope.Tamanhos = res; + }, function (err) { + console.error(err); + }); + + $scope.selecionaCor = function (cor) { + $scope.corSelecionada = cor;Util.filtro($scope); + }; + $scope.selecionaPreco = function (preco) { + $scope.precoSelecionado = preco;Util.filtro($scope); + }; + $scope.selecionaTamanho = function (tamanho) { + $scope.tamanhoSelecionado = tamanho;Util.filtro($scope); + }; + $scope.replaceValor = function (valor) { + return valor.toFixed(2).replace(".", ","); + }; + $scope.addCarrinho = function (item) { + item.produto.addCart = true; + Model.Carrinho.set(item); + }; + + $scope.troggleColapse = function () { + Util.troggleColapse($scope, Util); + }; + + $scope.carregarMaisProdutos = function () { + Util.getProdutos($scope.pagina, $scope, Util, Model, $timeout); + }; + + $scope.ordenarChange = function (id) { + $scope.ordenarTipo = id; + Util.filtro($scope); + }; + + $scope.expandOptions = function ($event) { + Util.abreFiltroMobile($event); + }; + + $scope.fecharFiltro = function ($event) { + Util.fechaFiltroMobile($event); + }; + + $scope.troggleCategoriaFiltroMobile = function ($event) { + Util.troggleCategoriaFiltroMobile($event); + }; + + $scope.carregarMaisProdutos(); + + $rootScope.$on('$routeChangeStart', function (event, next, current) { + Util.carregaCarrinho(next, Util); + }); + + $scope.showLimpaFiltros = function () { + return Util.showBtnLimpa($scope); + }; + + $scope.limpaFiltros = function () { + Util.limpaFiltros($scope, Util); + }; + }); \ No newline at end of file From 5e4ecb30477619447f81a44ba49dc78552275c50 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sun, 18 Mar 2018 01:54:06 -0300 Subject: [PATCH 16/22] =?UTF-8?q?inclus=C3=A3o=20de=20fontes=20imagens=20e?= =?UTF-8?q?=20svgs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baskerville-normal-webfont.eot | Bin 0 -> 65732 bytes .../baskerville-normal-webfont.svg | 245 ++++++++++++++++++ .../baskerville-normal-webfont.ttf | Bin 0 -> 65524 bytes .../baskerville-normal-webfont.woff | Bin 0 -> 35172 bytes .../baskerville-normal-webfont.woff2 | Bin 0 -> 28512 bytes .../bell-centennial-address-bt-webfont.eot | Bin 0 -> 35980 bytes .../bell-centennial-address-bt-webfont.svg | 202 +++++++++++++++ .../bell-centennial-address-bt-webfont.ttf | Bin 0 -> 35768 bytes .../bell-centennial-address-bt-webfont.woff | Bin 0 -> 19664 bytes .../bell-centennial-address-bt-webfont.woff2 | Bin 0 -> 15240 bytes .../bellcent-add-bt-address-webfont.eot | Bin 0 -> 36174 bytes .../bellcent-add-bt-address-webfont.svg | 203 +++++++++++++++ .../bellcent-add-bt-address-webfont.ttf | Bin 0 -> 36032 bytes .../bellcent-add-bt-address-webfont.woff | Bin 0 -> 19668 bytes .../bellcent-add-bt-address-webfont.woff2 | Bin 0 -> 15228 bytes assets/img/favicon.png | Bin 0 -> 961 bytes assets/svg/bag-white.svg | 11 + assets/svg/bag.svg | 10 + assets/svg/carrinho.svg | 13 + assets/svg/logo-profite.svg | 20 ++ 20 files changed, 704 insertions(+) create mode 100644 assets/fonts/baskerville/baskerville-normal-webfont.eot create mode 100644 assets/fonts/baskerville/baskerville-normal-webfont.svg create mode 100644 assets/fonts/baskerville/baskerville-normal-webfont.ttf create mode 100644 assets/fonts/baskerville/baskerville-normal-webfont.woff create mode 100644 assets/fonts/baskerville/baskerville-normal-webfont.woff2 create mode 100644 assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot create mode 100644 assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.svg create mode 100644 assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf create mode 100644 assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff create mode 100644 assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff2 create mode 100644 assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot create mode 100644 assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg create mode 100644 assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf create mode 100644 assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff create mode 100644 assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2 create mode 100644 assets/img/favicon.png create mode 100644 assets/svg/bag-white.svg create mode 100644 assets/svg/bag.svg create mode 100644 assets/svg/carrinho.svg create mode 100644 assets/svg/logo-profite.svg diff --git a/assets/fonts/baskerville/baskerville-normal-webfont.eot b/assets/fonts/baskerville/baskerville-normal-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..10eb1c99a4537279a1fe5d8d22c0a062a846643e GIT binary patch literal 65732 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/fonts/baskerville/baskerville-normal-webfont.ttf b/assets/fonts/baskerville/baskerville-normal-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..33f22d84a30140724000a06eefd3bde646019428 GIT binary patch literal 65524 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 lyrt|s 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 DUwMu9 literal 0 HcmV?d00001 diff --git a/assets/fonts/baskerville/baskerville-normal-webfont.woff b/assets/fonts/baskerville/baskerville-normal-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..0d3f6dbc7ff2571f9169b73e6d4a8137b19286b6 GIT binary patch literal 35172 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/baskerville/baskerville-normal-webfont.woff2 b/assets/fonts/baskerville/baskerville-normal-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..bc9e3165cd1f0f57f3c907fcd4ac0e33e088c4da GIT binary patch literal 28512 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot b/assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..2d7d44fd717eedd3c4388df4a054fb2c0569fb03 GIT binary patch literal 35980 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 z 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf b/assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a2a971f8f98a1214ed5ca00b920a9256a93b7041 GIT binary patch literal 35768 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 V3!T!xovI`YVZf&>8!(2k{|EoQny>%> literal 0 HcmV?d00001 diff --git a/assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff b/assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..5cd01947e3e6c20c22517bb2431a7830e68ec6dd GIT binary patch literal 19664 zcmY&l-WnCd(J#De~p?}z_iz?)jRoBVJC008e60N??wc$X$&W~^@n z00WYK>zgT>rb)ZNaeQV<%j_5~Y`4iiXR;jmdXY1$$07(278~(#7 z9AR%z**O^h#7g})Ph~%Nd}}i_a5Xmgxo4@7A0O;L&`GHNfi8j=V9 z_49vsM%~s7^^ElNc7P2az~J=^^g#Z3^G*CZq6Yw=odCB0z<=ZVI@4_a{=xpii&=-z z!2LZ=zb6ZOXT}qU`bGvO$A%`xMkfaT`GI9JNJe15(%qDQ=}W$+4E6Tn4i0Sg_V)gc zH}sjXfG9*pM+d6ChUx#`oO*gDetH;SBKV`dX1@;T174}35c$@^`~3Zr{KNfI_=EpM z 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%0A+F;hIN`?rg5kCB+m8q8rK zM&-x*4Jt7H=v{h$B8{A$AAM#6p1lgsUWhv_!#(<8%%%A2$MvP?OVA%MxfUbsdDpHZ zqnQ-u{9TY8TshjSAcG7h2R^-fJ8{Zq73*z`&lZPqSCq8Fn1doDJB$1_=Iw{Sxm>f1SO7 z8o_z&Ec*?6lRX1mK8LFM-vJvIVKo+r(~W z&$G?!7IrJ!!cMaPV83T?;~h^nyn-zT&4l96YRJJ8xRbl^)|*701ns7UJNIJk9^Pq7qEZA71c{|H4c9F7@IBo-$z1;$An zPGa+olX#rOdGr$m?u^kS(48Hd+i_=Au3Lv8Iq|)OB^{?OoH{Wm{nJTGl3==g;^NZL GjQwB07wb>} literal 0 HcmV?d00001 diff --git a/assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg b/assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg new file mode 100644 index 00000000..e2feb6ea --- /dev/null +++ b/assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf b/assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..fbeb3548abb813bf9233cf8cbf75ccc542ab3544 GIT binary patch literal 36032 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff b/assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff new file mode 100644 index 0000000000000000000000000000000000000000..505a68b7dc8b3d07546fc69b56d809963f6c8b1c GIT binary patch literal 19668 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2 b/assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ccc3b329925e36aa6c373fd7395aef6b25732876 GIT binary patch literal 15228 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 Cziy2H literal 0 HcmV?d00001 diff --git a/assets/img/favicon.png b/assets/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..da1f718214fa7cb9327b3a29b62ce1ebd3cdb71f GIT binary patch literal 961 zcmV;y13vtTP)FfuosL; zrGN}Rfq~#I=l!jG+SQa<|=7m*2T*PTGC#(X*vOzVNkq!c+;I)8<;01UOA`0hq1~p)+pM@-{ zFjlJVIdB0qfYt(noM1}1?| z36Avum%#`>1bm?Le+;D2mr3?Uy%^9XZUnS z>112LPPc6pQzr4URpHv@%y?Eh2`FKZPjMJmL6KIv2G=9QCGvaMqUDM0S|Oe~>h795?MQ^hK)U9C;Uz)1~< zs=2>$wbd{g#M0EKum<0InsBBry@L9ot7gf!7gy*Qt$G%7y;kuQ^tboLF{Y{mMj jf4xqg5%! + + + + + diff --git a/assets/svg/bag.svg b/assets/svg/bag.svg new file mode 100644 index 00000000..01a45f24 --- /dev/null +++ b/assets/svg/bag.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/assets/svg/carrinho.svg b/assets/svg/carrinho.svg new file mode 100644 index 00000000..cee8a051 --- /dev/null +++ b/assets/svg/carrinho.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/assets/svg/logo-profite.svg b/assets/svg/logo-profite.svg new file mode 100644 index 00000000..c6e28fc0 --- /dev/null +++ b/assets/svg/logo-profite.svg @@ -0,0 +1,20 @@ + + + + + + + + From cb60ddcd9c15709f17007b6d5c659cdeb4c4c44f Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sun, 18 Mar 2018 01:54:59 -0300 Subject: [PATCH 17/22] =?UTF-8?q?inclus=C3=A3o=20dos=20arquivos=20de=20est?= =?UTF-8?q?ilo=20em=20.less?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gulpfile.js | 62 ++++ index.html | 5 +- less/main.less | 7 + less/modules/classes.less | 48 +++ less/modules/fonts.less | 37 ++ less/modules/grid.less | 49 +++ less/modules/layout.less | 432 ++++++++++++++++++++++ less/modules/mobile.less | 157 ++++++++ less/modules/util.less | 20 + less/modules/variaveis.less | 9 + package-lock.json | 703 ++++++++++++++++++++++++++++++++++++ package.json | 3 + view/carrinho.html | 44 +++ view/home.html | 130 +++++++ 14 files changed, 1704 insertions(+), 2 deletions(-) mode change 100644 => 100755 gulpfile.js create mode 100644 less/main.less create mode 100644 less/modules/classes.less create mode 100644 less/modules/fonts.less create mode 100644 less/modules/grid.less create mode 100644 less/modules/layout.less create mode 100644 less/modules/mobile.less create mode 100644 less/modules/util.less create mode 100644 less/modules/variaveis.less create mode 100644 view/carrinho.html create mode 100644 view/home.html diff --git a/gulpfile.js b/gulpfile.js old mode 100644 new mode 100755 index e69de29b..0d43d7ba --- a/gulpfile.js +++ b/gulpfile.js @@ -0,0 +1,62 @@ +var gulp = require("gulp"), + uglify = require("gulp-uglify"), + concat = require("gulp-concat"), + less = require("gulp-less"), + cleanCSS = require("gulp-clean-css"), + clean = require("gulp-clean"), + connect = require("gulp-connect"), + paths = { + less: ['./less/**/*.less'], + controller: ['./js/controller/*.js'], + service: ['./js/services/*.js'], + model: ['./js/models/*.js'], + value: ['.js/value/*.js'], + config: ['.js/config/*.js'], + app: ['./js/*.js'] + }; + + gulp.task('clean', function () { + return gulp.src('dist/') + .pipe(clean()); + }); + + gulp.task('less', function() { + return gulp.src(['./less/*.less', './less/**/*.less']) + .pipe(less()) + .pipe(concat('main.css')) + .pipe(gulp.dest('./dist/css')); + }); + + gulp.task('fetchApp', function () { + return gulp.src('./js/**/*.js') + .pipe(concat('main.js')) + .pipe(gulp.dest('./dist/js')) + }); + + gulp.task('buildJs', function () { + return gulp.src('./dist/js/main.js') + .pipe(uglify({mangle: false})) + .pipe(gulp.dest('./dist/js')) + }); + + gulp.task("buildCss", function () { + return gulp.src('./dist/css/main.css') + .pipe(cleanCSS()) + .pipe(gulp.dest('./dist/css')); + }); + + gulp.task('webserver', function() { + connect.server({ + livereload: true, + port: 8000, + host: '127.0.0.1' + }); + }); + + gulp.task("build", ['buildJs', "buildCss"]); + + gulp.task('default', ['less', 'fetchApp', 'webserver'],function () { + gulp.watch(paths.less, ['less']); + gulp.watch('./js/**/*.js', ["fetchApp"]); + }); + diff --git a/index.html b/index.html index 67565261..cbf67db2 100644 --- a/index.html +++ b/index.html @@ -14,7 +14,7 @@ - +

    @@ -26,7 +26,7 @@

    - +
    {{qtdCompra}} @@ -44,6 +44,7 @@

    + \ No newline at end of file diff --git a/less/main.less b/less/main.less new file mode 100644 index 00000000..c5b62614 --- /dev/null +++ b/less/main.less @@ -0,0 +1,7 @@ +@import 'modules/variaveis'; +@import 'modules/util'; +@import 'modules/fonts'; +@import 'modules/grid'; +@import 'modules/classes'; +@import 'modules/layout'; +@import 'modules/mobile'; \ No newline at end of file diff --git a/less/modules/classes.less b/less/modules/classes.less new file mode 100644 index 00000000..34883bec --- /dev/null +++ b/less/modules/classes.less @@ -0,0 +1,48 @@ +.txt-hide{ + font-size: 0.01px; + color: transparent; + text-indent: -999999px; +} +.list { + margin: 0; + padding: 0; + display: block; +} +.list li { + list-style: none; +} +.list.list-block li { + display: block; +} +.list.list-inline li { + display: inline; +} +.list.list-iblock li { + display: inline-block; +} +.fleft{ + float: left; +} +.fright{ + float: right; +} +.fnone{ + float: none; +} +.txt-left { + text-align: left +} +.txt-right { + text-align: right +} +.txt-center { + text-align: center +} +.txt-justify { + text-align: justify +} +.hide, +.hide-desktop{ + display:none!important; + transition: all .3s; +} diff --git a/less/modules/fonts.less b/less/modules/fonts.less new file mode 100644 index 00000000..a72e9a4a --- /dev/null +++ b/less/modules/fonts.less @@ -0,0 +1,37 @@ +@font-face { + font-family: 'bellcent_add_btaddress'; + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot'); + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot?#iefix') format('embedded-opentype'), + url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2') format('woff2'), + url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff') format('woff'), + url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf') format('truetype'), + url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'bellcentennial_add_btaddress'; + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot'); + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot?#iefix') format('embedded-opentype'), + url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff2') format('woff2'), + url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff') format('woff'), + url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf') format('truetype'), + url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'baskerville_regular'; + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot'); + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot?#iefix') format('embedded-opentype'), + url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff2') format('woff2'), + url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff') format('woff'), + url('../../assets/fonts/baskerville/baskerville-normal-webfont.ttf') format('truetype'), + url('../../assets/fonts/baskerville/baskerville-normal-webfont.svg#webfontregular') format('svg'); + font-weight: normal; + font-style: normal; +} \ No newline at end of file diff --git a/less/modules/grid.less b/less/modules/grid.less new file mode 100644 index 00000000..b809e486 --- /dev/null +++ b/less/modules/grid.less @@ -0,0 +1,49 @@ +@import '../main'; +@widthGrid: 940; +@colunas: 12; +@gutter: 20; + +.container{ + width:~"@{widthGrid}px"; + height: auto; + display: block; + margin-left: auto; + margin-right: auto; + + [class^=col]:first-child{ + padding-left: 0; + } + + [class^=col]:last-child{ + padding-right: 0; + } +} +.row{ + width: 100%; + height: auto; + display: block; + position: relative; + box-sizing: border-box; + .clear; +} + +[class^=col]{ + float: left; + height: auto; + padding-left: ~"@{gutter}px"; + padding-right: ~"@{gutter}px"; + position: relative; +} + + +@widthColum: (@widthGrid / @colunas); +.loopGrid(@index) when (@index > 0){ + .loopGrid(@index - 1); + .col-@{index}{ + width: ((@widthColum * @index) / @widthGrid) * 100%; + } + .col-pull-@{index}{ + left: ((@widthColum * @index) / @widthGrid) * 100%; + } +} +.loopGrid(@colunas); \ No newline at end of file diff --git a/less/modules/layout.less b/less/modules/layout.less new file mode 100644 index 00000000..7bcc3ed6 --- /dev/null +++ b/less/modules/layout.less @@ -0,0 +1,432 @@ +@import '../main'; +*, *::after, *::before{ + box-sizing: border-box; +} + +body{ + font-size: 1em; + color: #000; + font-family: @opensans; +} + +#logo{ + display: block; + width: 104px; + height: 29px; + + a{ + width: inherit; + height: inherit; + display: block; + .bg-center("../../assets/svg/logo-profite.svg"); + } +} +header{ + .box-full; + border-bottom: @corPrimaria 2px solid; + padding: 10px 0; +} + +footer{ + .box-full; + background: #000; + padding: 5px 0; + + p{ + @em: return; + .pxToem(12); + font-size: @@em; + color: #999; + font-family: @opensans; + } +} +#bag{ + background: transparent; + border: none; + position: relative; + width: 32px; + height: 27px; + float: right; + cursor: pointer; + margin-top: 23px; + outline: none; +} +#bag img{ + position: absolute; + top: 0; + left:0; +} +#bag #count{ + position: absolute; + right: 0; + bottom: 0; + width: 20px; + height: 20px; + background: @corPrimaria; + border-radius: 50px; + display: block; +} +#bag #count span{ + @em: return; + .pxToem(12); + width: inherit; + height: inherit; + text-align: center; + vertical-align: middle; + color:#fff; + font-family: @opensans; + font-size: @@em; + font-weight: 600; + display: table-cell; +} +.widget{ + width: 90%; + height: auto; + display:block; + margin: 0 0 40px; + .titulo{ + @em: return; + .pxToem(20); + font-size: @@em; + font-family: @bellcent; + letter-spacing: 0.10em; + color:#000; + margin: 0 0 25px; + text-transform: uppercase; + } + ul li{ + @em: return; + .pxToem(13); + font-family: @opensans; + font-size: @@em; + font-weight: 400; + color:#000; + line-height: 24px; + cursor: pointer; + } +} +.checkbox{ + width: 15px; + height: 15px; + display: inline-block; + border: #d8d8d8 solid 1px; + background: #fff; + position: relative; + margin-bottom: -3px; + margin-right: 2px; + + &::before{ + content: ""; + width: 9px; + height: 9px; + position: absolute; + background: #fff; + left: 2px; + top: 2px; + transition: background 0.3s ease-out; + } + &.selecionado::before{ + background: @corPrimaria; + transition: background 0.3s ease-in; + } +} +.widget ul li.blocos{ + width: 32px; + height: 32px; + text-align: center; + border: #e5e5e5 solid 1px; + margin-right: 10px; + margin-bottom: 10px; + color:#a9a9a9; + font-family: @opensans; + font-weight: 400; + line-height: normal; + padding: 6px 0 0; + + &:nth-child(4n){ + margin-right: 0; + } + + &.selecionado{ + color: #000; + border-color: @corPrimaria; + } +} + +.produtos{ + width: 175px; + height: auto; + text-align: center; + display:inline-block; + margin: 0 0 80px; + .clear; + + > img{ + vertical-align: bottom; + } + + .thumb{ + width:100%; + height: 330px; + display:block; + + img{ + width: auto; + height: 100%; + } + } + + .box-titulo{ + height: 35px; + width: 80%; + display: table; + margin: 25px auto 15px; + .titulo{ + @em: return; + .pxToem(14); + font-family: @bellcent; + font-size: @@em; + color:#000; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; + width: 100%; + height: inherit; + } + } + + .boxprice{ + .box-full; + + .valores{ + width: 70%; + float:left; + } + .add-cart{ + background: @corPrimaria; + width: 29%; + height: 100%; + text-align: center; + border:none; + padding: 8px 0; + cursor: pointer; + float:right; + outline:none; + transition: background .3s ease-in; + + &[disabled], &[disabled]:hover{ + background: #000; + cursor: no-drop; + } + + &:hover{ + background: @corPrimariaHover; + transition: background .3s ease-out; + } + } + } + .boxprice .valores{ + h5{ + @em: return; + .pxToem(16); + font-family: @baskeville; + font-size: @@em; + color:#000; + letter-spacing: 0.10em; + margin:0; + display: block; + + small{ + color:#666666; + text-decoration: line-through; + } + } + p{ + @em: return; + .pxToem(14); + font-family: @baskeville; + font-size: @@em; + color:#000; + display: block; + margin: 6px 0 0; + } + } +} + +#lista-produtos li:nth-child(3n - 1) .produtos{ + margin-left: 80px; + margin-right: 80px; +} + +.btn{ + @em: return; + .pxToem(14); + font-size: @@em; + font-family: @bell; + display: table; + width: auto; + height: auto; + margin: 50px auto; + padding: 15px 60px; + border: none; + color:#fff; + border-radius: 0; + text-align: center; + background: @corPrimaria; + letter-spacing: 0.10em; + cursor: pointer; + text-transform: uppercase; + text-decoration: none; + + &:hover{ + background: @corPrimariaHover; + } +} + +.btn-txt{ + @em: return; + .pxToem(12); + font-size: @@em; + font-family: @opensans; + display: block; + width: 100%; + height: auto; + margin:10px 0 0; + padding:0; + border: none; + color:#666; + border-radius: 0; + text-align: left; + background: transparent; + cursor: pointer; + text-decoration: underline; + outline: none; + + .arrow{ + font-family: cursive; + font-size: 16px; + display: inline-block; + transform: rotate(90deg); + + &.open{ + transform: rotate(-90deg); + } + } + + &:hover{ + color: @corPrimariaHover; + } +} + +#msg-erro{ + background: rgba(0,0,0,.8); + width: 250px; + position: fixed; + z-index: 5000; + color: #fff; + border-radius: 50px; + padding: 8px 50px; + font-size: 13px; + top: 0; + left: 50%; + text-align: center; + margin-left: -125px; +} + +.title-section{ + @em: return; + .pxToem(30); + font-size: @@em; + font-family: @bell; + display: block; + letter-spacing: 0.10em; + text-transform: uppercase; + margin:0 +} +.title-section-box{ + padding: 45px 0; + + .sel-camp{ + @em: return; + .pxToem(12); + font-size: @@em; + font-family: @opensans; + color: #000; + border:#666 solid 1px; + .box-full; + + } + +} + +.tabela{ + width: 100%; + tr td{ + padding: 4px; + } + tr:nth-child(odd) td{ + background: #f2f2f2; + } + + tfoot tr td{ + background: #bfbfbf!important; + } +} + +#remove{ + width: 25px; + height: 25px; + display: table-cell; + vertical-align: middle; + margin: 0 auto; + background: @corPrimaria; + border: none; + border-radius: 50px; + font-size: 13px; + color: #fff; + cursor:pointer; + outline: none; + transition: all .3s ease-in; + + &:hover{ + background: darkred; + transition: all .3s ease-out; + } +} + +#limpar-filtros{ + width: 150px; + height: auto; + background: @corPrimaria; + padding: 6px 0; + border:none; + cursor:pointer; + color:#fff; + font-size: 14px; + font-weight: 700; + font-family: @opensans; + margin: 0 0 55px; + outline: none; + letter-spacing: 0.05em; + transition: all .3s ease-in; + + span{ + width: 33px; + height: inherit; + padding: 6px 0; + background: @corPrimariaHover; + display: inline-block; + margin: -6px 0px -8px -18px; + transition: all .3s ease-in; + } + + &:hover{ + background: @corPrimariaHover; + transition: all .3s ease-out; + + span{ + background: @corPrimaria; + transition: all .3s ease-out; + } + } +} \ No newline at end of file diff --git a/less/modules/mobile.less b/less/modules/mobile.less new file mode 100644 index 00000000..dcf8515e --- /dev/null +++ b/less/modules/mobile.less @@ -0,0 +1,157 @@ +@import '../main'; +@media (min-width: 300px) and (max-width: 939px){ + .container{ + width: 100%; + } + .hide-mobile{ + display: none!important; + } + .hide-desktop{ + display: initial!important; + } + .title-section{ + text-align: center; + } + .mobile-block{ + width: 100%; + } + .container .row > div { + padding: 0!important; + } + + #lista-produtos li .produtos{ + margin-left: 3px!important; + margin-right: 3px!important; + width: 135px; + } + + aside, article{ + float: none!important; + width: 100%!important; + padding:0!important; + .clear; + + } + aside{ + width: 90%!important; + margin: 0 auto 65px; + } + + .widget{ + width: 100%; + } + + .btn-filtros{ + width: 100%; + height: auto; + padding: 10px 0; + background: transparent; + color :#000; + text-align: center; + font-family: @opensans; + font-weight: 400; + font-size:18px; + color:#666666; + border:none; + cursor: pointer; + border: #666 solid 1px; + outline: none; + } + + .mobile-box-filtro, + .mobile-box-ordenar{ + width:50%; + float:left; + } + .titulo-filtro-tipo{ + border-bottom: solid 1px #000; + padding-bottom: 5px; + } + + .widget .titulo{ + @em: return; + .pxToem(14); + font-size: @@em; + cursor:pointer; + + span{ + float: right; + margin-top: -3px; + } + } + + .widget ul{ + display:none!important; + } + + .widget ul.abre-lista{ + display:block!important; + } + + .widget ul li.blocos:nth-child(4n) { + margin-right: 10px; + } + + .widget ul li{ + font-size: 15px; + line-height: 34px; + padding-left: 12px; + } + + .mobile-widget{ + display:none; + + p{ + font-size: 16px; + width: 100%; + display: block!important; + border-bottom: #000 solid 1px; + margin: 0 0 15px; + padding:0 12px 8px; + cursor: pointer; + span{ + float: right; + margin-top: -3px; + margin-right: 12px; + } + } + } + + #lista-cores-mobile{ + display:block!important; + } + + .mobile-widget.open-filter{ + display:block!important; + display: block!important; + position: absolute; + background: #fff; + width: 100%; + left: 0; + top:0; + z-index: 1; + } + + .hide{ + display: block!important; + } + #btnCores{ + display: none; + } +} + +@media (min-width: 424px) and (max-width: 939px){ + #lista-produtos li .produtos { + margin-left: 20px!important; + margin-right: 0px!important; + width: 175px; + } +} + +@media (min-width: 767px) and (max-width: 939px){ + #lista-produtos li .produtos { + margin-left: 50px!important; + margin-right: 0px!important; + width: 175px; + } +} \ No newline at end of file diff --git a/less/modules/util.less b/less/modules/util.less new file mode 100644 index 00000000..81d8257d --- /dev/null +++ b/less/modules/util.less @@ -0,0 +1,20 @@ +.bg-center(@local) when (isstring(@local)){ + background: url(@local) no-repeat center transparent; +} +.box-full{ + width: 100%; + height: auto; + display: block; +} +.clear{ + &::after{ + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; + } +} +.pxToem(@valor){ + @return: (@valor / @baseFonte) * 1em; +} \ No newline at end of file diff --git a/less/modules/variaveis.less b/less/modules/variaveis.less new file mode 100644 index 00000000..f04f4586 --- /dev/null +++ b/less/modules/variaveis.less @@ -0,0 +1,9 @@ +@bellcent: 'bellcent_add_btaddress', sans-serif; +@bell: 'bellcentennial_add_btaddress', sans-serif; +@baskeville: 'baskerville_regular', sans-serif; +@opensans: 'Open Sans', sans-serif; + +@baseFonte: 16; + +@corPrimaria: #ff9f1c; +@corPrimariaHover: darken( @corPrimaria, 20% ); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f4e2b320..667deaa3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,16 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "2.1.18", + "negotiator": "0.6.1" + } + }, "accord": { "version": "0.29.0", "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", @@ -311,6 +321,12 @@ } } }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, "bcrypt-pbkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", @@ -327,6 +343,47 @@ "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", "dev": true }, + "body-parser": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.14.2.tgz", + "integrity": "sha1-EBXLH+LEQ4WCWVgdtTMy+NDPUPk=", + "dev": true, + "requires": { + "bytes": "2.2.0", + "content-type": "1.0.4", + "debug": "2.2.0", + "depd": "1.1.2", + "http-errors": "1.3.1", + "iconv-lite": "0.4.13", + "on-finished": "2.3.0", + "qs": "5.2.0", + "raw-body": "2.1.7", + "type-is": "1.6.16" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "qs": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.0.tgz", + "integrity": "sha1-qfMRQq9GjLcrJbMBNrokVoNJFr4=", + "dev": true + } + } + }, "boom": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", @@ -386,6 +443,12 @@ } } }, + "bytes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", + "integrity": "sha1-/TVGSkA/b5EXwt42Cez/nK4ABYg=", + "dev": true + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -682,6 +745,30 @@ } } }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "1.3.2", + "utils-merge": "1.0.1" + } + }, + "connect-livereload": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.5.4.tgz", + "integrity": "sha1-gBV9E3HJ83zBQDmrGJWXDRGdw7w=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, "convert-source-map": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", @@ -781,18 +868,36 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, "deprecated": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", "dev": true }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, "duplexer2": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", @@ -812,6 +917,18 @@ "jsbn": "0.1.1" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, "end-of-stream": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", @@ -831,12 +948,39 @@ "prr": "1.0.1" } }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "etag": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz", + "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "0.1.1", + "from": "0.1.7", + "map-stream": "0.1.0", + "pause-stream": "0.0.11", + "split": "0.3.3", + "stream-combiner": "0.0.4", + "through": "2.3.8" + } + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -1018,6 +1162,15 @@ "time-stamp": "1.1.0" } }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1041,6 +1194,21 @@ } } }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } + }, "find-index": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", @@ -1127,6 +1295,18 @@ "map-cache": "0.2.2" } }, + "fresh": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz", + "integrity": "sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8=", + "dev": true + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1344,6 +1524,103 @@ "vinyl-fs": "0.3.14" } }, + "gulp-clean": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/gulp-clean/-/gulp-clean-0.4.0.tgz", + "integrity": "sha512-DARK8rNMo4lHOFLGTiHEJdf19GuoBDHqGUaypz+fOhrvOs3iFO7ntdYtdpNxv+AzSJBx/JfypF0yEj9ks1IStQ==", + "dev": true, + "requires": { + "fancy-log": "1.3.2", + "plugin-error": "0.1.2", + "rimraf": "2.6.2", + "through2": "2.0.3", + "vinyl": "2.1.0" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "clone": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", + "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", + "dev": true, + "requires": { + "clone": "2.1.1", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.2", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + } + } + }, "gulp-clean-css": { "version": "3.9.3", "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.9.3.tgz", @@ -1401,6 +1678,23 @@ } } }, + "gulp-connect": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/gulp-connect/-/gulp-connect-5.5.0.tgz", + "integrity": "sha512-oRBLjw/4EVaZb8g8OcxOVdGD8ZXYrRiWKcNxlrGjxb/6Cp0GDdqw7ieX7D8xJrQS7sbXT+G94u63pMJF3MMjQA==", + "dev": true, + "requires": { + "ansi-colors": "1.1.0", + "connect": "3.6.6", + "connect-livereload": "0.5.4", + "event-stream": "3.3.4", + "fancy-log": "1.3.2", + "send": "0.13.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "tiny-lr": "0.2.1" + } + }, "gulp-less": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.0.tgz", @@ -1777,6 +2071,22 @@ "parse-passwd": "1.0.0" } }, + "http-errors": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", + "integrity": "sha1-GX4izevUGYWF6GlO9nhhl7ke2UI=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "statuses": "1.3.1" + } + }, + "http-parser-js": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.11.tgz", + "integrity": "sha512-QCR5O2AjjMW8Mo4HyI1ctFcv+O99j/0g367V3YoVnrNw5hkDvAWZD0lWGcc+F4yN3V55USPCVix4efb75HxFfA==", + "dev": true + }, "http-signature": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", @@ -1789,6 +2099,12 @@ "sshpk": "1.14.1" } }, + "iconv-lite": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", + "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=", + "dev": true + }, "image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -2117,6 +2433,12 @@ "resolve": "1.5.0" } }, + "livereload-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.3.0.tgz", + "integrity": "sha512-j1R0/FeGa64Y+NmqfZhyoVRzcFlOZ8sNlKzHjh4VvLULFACZhn68XrX5DFg2FhMvSMJmROuFxRSa560ECWKBMg==", + "dev": true + }, "lodash": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", @@ -2337,6 +2659,12 @@ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -2346,6 +2674,12 @@ "object-visit": "1.0.1" } }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, "micromatch": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.9.tgz", @@ -2483,6 +2817,12 @@ "integrity": "sha512-5bRASydE1gu6zPOenLN043++J8xj1Ob7ArkfdYO3JN4DF5rDmG7bMoiybkTyD+GnXQEMixVeDHMzuqm6kpBmiA==", "dev": true }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, "normalize.css": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.0.tgz", @@ -2609,6 +2949,15 @@ "isobject": "3.0.1" } }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", @@ -2658,6 +3007,12 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -2691,6 +3046,15 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, "performance-now": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", @@ -2759,6 +3123,31 @@ "dev": true, "optional": true }, + "range-parser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz", + "integrity": "sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU=", + "dev": true + }, + "raw-body": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", + "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=", + "dev": true, + "requires": { + "bytes": "2.4.0", + "iconv-lite": "0.4.13", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", + "dev": true + } + } + }, "readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -2885,6 +3274,40 @@ "align-text": "0.1.4" } }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + } + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -2906,12 +3329,181 @@ "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", "dev": true }, + "send": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.13.2.tgz", + "integrity": "sha1-dl52B8gFVFK7pvCwUllTUJhgNt4=", + "dev": true, + "requires": { + "debug": "2.2.0", + "depd": "1.1.2", + "destroy": "1.0.4", + "escape-html": "1.0.3", + "etag": "1.7.0", + "fresh": "0.3.0", + "http-errors": "1.3.1", + "mime": "1.3.4", + "ms": "0.7.1", + "on-finished": "2.3.0", + "range-parser": "1.0.3", + "statuses": "1.2.1" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", + "dev": true + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "statuses": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz", + "integrity": "sha1-3e1FzBglbVHtQK7BQkidXGECbSg=", + "dev": true + } + } + }, "sequencify": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", "dev": true }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "1.3.5", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "1.0.3", + "http-errors": "1.6.2", + "mime-types": "2.1.18", + "parseurl": "1.3.2" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.2" + }, + "dependencies": { + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.4.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + } + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -2935,6 +3527,12 @@ } } }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -3117,6 +3715,15 @@ "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", "dev": true }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -3230,6 +3837,21 @@ } } }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "0.1.1" + } + }, "stream-consume": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", @@ -3274,6 +3896,12 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, "through2": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", @@ -3331,6 +3959,43 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, + "tiny-lr": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz", + "integrity": "sha1-s/26gC5dVqM8L28QeUsy5Hescp0=", + "dev": true, + "requires": { + "body-parser": "1.14.2", + "debug": "2.2.0", + "faye-websocket": "0.10.0", + "livereload-js": "2.3.0", + "parseurl": "1.3.2", + "qs": "5.1.0" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "qs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-5.1.0.tgz", + "integrity": "sha1-TZMuXH6kEcynajEtOaYGIA/VDNk=", + "dev": true + } + } + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -3400,6 +4065,16 @@ "dev": true, "optional": true }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.18" + } + }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", @@ -3465,6 +4140,12 @@ "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", "dev": true }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -3538,6 +4219,12 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, "uuid": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", @@ -3651,6 +4338,22 @@ "source-map": "0.5.7" } }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": "0.4.11", + "websocket-extensions": "0.1.3" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, "when": { "version": "3.7.8", "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", diff --git a/package.json b/package.json index 35f1202b..c6bd4041 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,16 @@ }, "devDependencies": { "gulp": "^3.9.1", + "gulp-clean": "^0.4.0", "gulp-clean-css": "^3.9.3", "gulp-concat": "^2.6.1", + "gulp-connect": "^5.5.0", "gulp-less": "^4.0.0", "gulp-shell": "^0.6.5", "gulp-uglify": "^3.0.0" }, "scripts": { + "start": "gulp", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/view/carrinho.html b/view/carrinho.html new file mode 100644 index 00000000..fcf66dd9 --- /dev/null +++ b/view/carrinho.html @@ -0,0 +1,44 @@ + \ No newline at end of file diff --git a/view/home.html b/view/home.html new file mode 100644 index 00000000..c2be5315 --- /dev/null +++ b/view/home.html @@ -0,0 +1,130 @@ +

    {{mensagemErro}}

    + +
    +
    +
    +

    Vestidos

    +
    +
    + +
    +
    +
    + + + +
    +
    +
    + +
    +
    + +

    Aguarde, Carregando os Produtos...

    +

    Não temos produtos com o filtro solicitado

    + +
      +
    • +
      +
      + {{item.produto.nome}} +
      +
      +

      {{item.produto.nome}}

      +
      +
      +
      +
      + R${{item.pagamento.de}} + R$ {{item.pagamento.por}} +
      +

      até {{item.pagamento.qtdParcela}}x de R$ {{ replaceValor(item.pagamento.por / item.pagamento.qtdParcela) }}

      +
      + +
      +
      +
    • +
    + + +
    +
    +
    \ No newline at end of file From 5bae620e97dc7e3c2888eca3275bfc9cd88b4ed3 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sun, 18 Mar 2018 01:55:58 -0300 Subject: [PATCH 18/22] =?UTF-8?q?altera=C3=A7=C3=B5es=20nos=20modelos=20e?= =?UTF-8?q?=20controllers=20da=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/app.js | 18 ++++++-------- js/config/route.config.js | 14 +++-------- js/controllers/home.controller.js | 41 +++++++++++++++++++++---------- js/models/cores.model.js | 41 +++++++++---------------------- js/models/preco.model.js | 37 +++++++--------------------- js/models/produtos.model.js | 24 ++++++------------ js/models/tamanhos.model.js | 35 ++++++-------------------- js/services/Util.js | 8 +++--- js/services/collapse.js | 2 +- js/services/controller.support.js | 32 ++++++++++++------------ js/services/filtro.support.js | 2 +- js/services/mobile.support.js | 2 +- js/value/config.value.js | 2 +- 13 files changed, 98 insertions(+), 160 deletions(-) diff --git a/js/app.js b/js/app.js index 18cb609e..48653240 100644 --- a/js/app.js +++ b/js/app.js @@ -1,16 +1,12 @@ -(function () { - 'use strict'; - - angular.module('profite', ['ngRoute']) - .run(['$rootScope', function ($rootScope) { +var app = angular.module('profite', ['ngRoute']); + app.run(['$rootScope', function ($rootScope) { $rootScope.listaCompra = []; - }]) - .config(['$compileProvider', function ($compileProvider) { + }]); + app.config(['$compileProvider', function ($compileProvider) { $compileProvider.debugInfoEnabled(false); - }]) - .constant('OrdenarOpt', [ + }]); + app.constant('OrdenarOpt', [ {id: 1, label: "Mais Recentes"}, {id: 2, label: "Menor Preço"}, {id: 3, label: "Maior Preço"} - ]); -})(); \ No newline at end of file + ]); \ No newline at end of file diff --git a/js/config/route.config.js b/js/config/route.config.js index c8250176..743af0b2 100644 --- a/js/config/route.config.js +++ b/js/config/route.config.js @@ -1,26 +1,18 @@ -(function(){ - 'use strict'; - - angular +angular .module('profite') - .config(routeConfig) + .config(routeConfig); - /** @ngInject */ function routeConfig($routeProvider, $locationProvider){ $locationProvider.hashPrefix(""); $routeProvider.when('/home', { templateUrl: 'view/home.html', - controllerAs: 'vm', controller: 'homeCtrl' }); $routeProvider.when('/carrinho', { templateUrl: 'view/carrinho.html', - controllerAs: 'vm', controller: 'carrinhoCtrl' }); $routeProvider.otherwise({redirectTo: '/home'}); - } - -}()); \ No newline at end of file + } \ No newline at end of file diff --git a/js/controllers/home.controller.js b/js/controllers/home.controller.js index db61a5da..ad191458 100644 --- a/js/controllers/home.controller.js +++ b/js/controllers/home.controller.js @@ -1,5 +1,5 @@ -angular.module('Profite') - .controller('homeCtrl', function ($scope, $rootScope, OrdenarOpt, Model, $timeout, Util) { +angular.module('profite') + .controller('homeCtrl', function ($http, $scope, $rootScope, OrdenarOpt, Model, $timeout, Util, config) { var itensCollapseCor = 5; $scope.ordernarList = OrdenarOpt; $scope.ordenarTipo = null; @@ -17,29 +17,40 @@ angular.module('Profite') $scope.mensagemErro = null; Model.Cores.get().then(function (res) { - $scope.Cores = res;Util.collapse(itensCollapseCor); + $scope.Cores = res.data; + Util.collapse(itensCollapseCor); }, function (err) { - console.log(err); + alert(err); }); + + Model.Produtos.get($scope.pagina,$scope).then(function (value) { + $scope.Produtos = value.data; + }, function (reason) { + console.log(reason); + }); + Model.Preco.get().then(function (res) { - $scope.Precos = res; - }, function (err) { - console.error(err); + $scope.Precos = res.data; + }, function (reason) { + console.log(reason); }); Model.Tamanhos.get().then(function (res) { - $scope.Tamanhos = res; + $scope.Tamanhos = res.data; }, function (err) { console.error(err); }); $scope.selecionaCor = function (cor) { - $scope.corSelecionada = cor;Util.filtro($scope); + $scope.corSelecionada = cor; + Util.filtro($scope); }; $scope.selecionaPreco = function (preco) { - $scope.precoSelecionado = preco;Util.filtro($scope); + $scope.precoSelecionado = preco; + Util.filtro($scope); }; $scope.selecionaTamanho = function (tamanho) { - $scope.tamanhoSelecionado = tamanho;Util.filtro($scope); + $scope.tamanhoSelecionado = tamanho; + Util.filtro($scope); }; $scope.replaceValor = function (valor) { return valor.toFixed(2).replace(".", ","); @@ -54,7 +65,11 @@ angular.module('Profite') }; $scope.carregarMaisProdutos = function () { - Util.getProdutos($scope.pagina, $scope, Util, Model, $timeout); + Model.Produtos.get(2,$scope).then(function (value) { + $scope.Produtos = value.data; + }, function (reason) { + console.log(reason); + }); }; $scope.ordenarChange = function (id) { @@ -74,7 +89,7 @@ angular.module('Profite') Util.troggleCategoriaFiltroMobile($event); }; - $scope.carregarMaisProdutos(); + // $scope.carregarMaisProdutos(); $rootScope.$on('$routeChangeStart', function (event, next, current) { Util.carregaCarrinho(next, Util); diff --git a/js/models/cores.model.js b/js/models/cores.model.js index 2cd4bec5..54163295 100644 --- a/js/models/cores.model.js +++ b/js/models/cores.model.js @@ -1,39 +1,20 @@ angular.module('profite') .factory('Cores', function ($q, $http, config) { - var def = $q.defer(); - var listaCores = []; - - function getService() { - if (listaCores.length) { - def.resolve(listaCores); - } else { - $http - .get(config.apiUrl + "/cores") - .success(function (res) { - listaCores = res; - def.resolve(res); - }).error(function (err) { - listaCores = err; - def.resolve(err); - }); - } - - } function get() { - def.promise; + return $http.get(config.apiUrl + "/cores"); } - - function find(busca) { - listaCores.map(function (item) { - return item.id === busca.id; - }); - } - - getService(); + // + // function find(busca) { + // listaCores.map(function (item) { + // return item.id === busca.id; + // }); + // } + // + // getService(); return { - get: get, - find: find + get: get + // find: find } }); \ No newline at end of file diff --git a/js/models/preco.model.js b/js/models/preco.model.js index e3c9fdd5..546f5e69 100644 --- a/js/models/preco.model.js +++ b/js/models/preco.model.js @@ -1,38 +1,19 @@ angular.module('profite') - .factory('Preco', function ($q, $http, config) { - var def = $q.defer(); - var listaPreco = []; - function getService() { - if(listaTamanhos.length){ - def.resolve( listaTamanhos ); - }else{ - $http - .get(config.apiUrl + "/faixa-preco") - .success(function (res) { - listaTamanhos = res; - def.resolve(res); - }) - .error(function (err) { - def.reject(err); - }); - } - } + .factory('Preco', function ($http, config) { function get() { - def.promise; - } - - function find(busca) { - listaTamanhos.map(function (item) { - return item.id === busca.id; - }); + return $http.get(config.apiUrl + '/faixa-preco'); } - getService(); + // function find(busca) { + // listaPreco.map(function (item) { + // return item.id === busca.id; + // }); + // } return { - get: get, - find: find + get: get + // find: find } }); \ No newline at end of file diff --git a/js/models/produtos.model.js b/js/models/produtos.model.js index a8e4952a..2ee4f8cb 100644 --- a/js/models/produtos.model.js +++ b/js/models/produtos.model.js @@ -1,25 +1,17 @@ angular.module('profite') .factory('Produtos', function($http, config){ - function get(skip, success, error) { + function get(skip, scope) { if(skip === 1) { - $http.get(config.apiUrl + "/produtos") - .success(function (res) { - success(res); - }).error(function (err) { - error(err); - }); + return $http.get(config.apiUrl + "/produtos"); + scope.pagina++; } else if(skip === 2) { - $http.get(config.apiUrl + "/mais-produtos") - .success(function (res) { - success(res); - }).error(function (err) { - error(err); - }); + return $http.get(config.apiUrl + "/mais-produtos"); } - return { - get: get - } + } + + return { + get: get } }); \ No newline at end of file diff --git a/js/models/tamanhos.model.js b/js/models/tamanhos.model.js index 1bcecd86..fb663400 100644 --- a/js/models/tamanhos.model.js +++ b/js/models/tamanhos.model.js @@ -1,38 +1,19 @@ angular.module('profite') .factory('Tamanhos', function ($q, $http, config) { - var def = $q.defer(); - var listaTamanhos = []; - function getService() { - if(listaTamanhos.length){ - def.resolve( listaTamanhos ); - }else{ - $http - .get(config.apiUrl + "/tamanhos") - .success(function (res) { - listaTamanhos = res; - def.resolve(res); - }) - .error(function (err) { - def.reject(err); - }); - } - } function get() { - def.promise; - } - - function find(busca) { - listaTamanhos.map(function (item) { - return item.id === busca.id; - }); + return $http.get(config.apiUrl + '/tamanhos'); } - getService(); + // function find(busca) { + // listaTamanhos.map(function (item) { + // return item.id === busca.id; + // }); + // } return { - get: get, - find: find + get: get + // find: find } }); \ No newline at end of file diff --git a/js/services/Util.js b/js/services/Util.js index 4ba12cb2..22b6add5 100644 --- a/js/services/Util.js +++ b/js/services/Util.js @@ -1,4 +1,4 @@ -angular.module('Profite') +angular.module('profite') .factory('Util', function(Filtro, Collapse, MobileSupport, CTRLSupport){ return { msgErro: function msgErro(msg, scope, time) { @@ -17,9 +17,9 @@ angular.module('Profite') Collapse.troggleColapse(scope, Util); }, - getProdutos: function getProdutos(skip, scope, Util, Model, time) { - CTRLSupport.getProdutos(skip, scope, Util, Model, time); - }, + // getProdutos: function getProdutos(skip, scope, Util, Model, time) { + // CTRLSupport.getProdutos(skip, scope, Util, Model, time); + // }, filtro: function filtro(scope, id) { CTRLSupport.filtro(scope, id); diff --git a/js/services/collapse.js b/js/services/collapse.js index 712c2150..d8d99a9b 100644 --- a/js/services/collapse.js +++ b/js/services/collapse.js @@ -1,4 +1,4 @@ -angular.module('Profite') +angular.module('profite') .factory('Collapse', function () { return { trataClasseBtn: function trataClasseBtn(ocasiao) { diff --git a/js/services/controller.support.js b/js/services/controller.support.js index e5be9970..bdf95113 100644 --- a/js/services/controller.support.js +++ b/js/services/controller.support.js @@ -1,4 +1,4 @@ -angular.module('Profite') +angular.module('profite') .factory('CTRLSupport', function (Filtro) { var encontraProdutoAdicionado = function encontraProdutoAdicionado(atual, model) { for (var j in model.Carrinho.get()) { @@ -16,21 +16,21 @@ angular.module('Profite') }, 4000); }, - getProdutos: function getProdutos(skip, scope, Util, Model, time) { - Model.Produtos.get(skip, function (res) { - for (var i in res) { - res[i].produto.addCart = false; - scope.Produtos.push(res[i]); - scope.produtosCopia.push(res[i]); - } - scope.pagina++; - Util.filtro(scope); - Util.existsCompras(Model, scope); - }, function (err) { - Util.msgErro("Não há mais produtos.", scope, time); - console.error(err); - }); - }, + // getProdutos: function getProdutos(skip, scope, Util, Model, time) { + // Model.Produtos.get(skip).then( function (res) { + // for (var i in res) { + // res[i].produto.addCart = false; + // scope.Produtos.push(res[i]); + // scope.produtosCopia.push(res[i]); + // } + // scope.pagina++; + // Util.filtro(scope); + // Util.existsCompras(Model, scope); + // }, function (err) { + // Util.msgErro("Não há mais produtos.", scope, time); + // console.error(err); + // }); + // }, filtro: function filtro(scope, id) { Filtro.filtroCor(scope); diff --git a/js/services/filtro.support.js b/js/services/filtro.support.js index 8774dab8..21978f8b 100644 --- a/js/services/filtro.support.js +++ b/js/services/filtro.support.js @@ -1,4 +1,4 @@ -angular.module('Profite') +angular.module('profite') .factory('Filtro', function ($interval) { return { filtroCor: function filtroCor(scope) { diff --git a/js/services/mobile.support.js b/js/services/mobile.support.js index 1d9bba2f..0cf0130f 100644 --- a/js/services/mobile.support.js +++ b/js/services/mobile.support.js @@ -1,4 +1,4 @@ -angular.module('Profite') +angular.module('profite') .factory('MobileSupport', function () { return { diff --git a/js/value/config.value.js b/js/value/config.value.js index 6a2c5aeb..7d35992b 100644 --- a/js/value/config.value.js +++ b/js/value/config.value.js @@ -1,4 +1,4 @@ angular.module('profite') .value('config', { - apiUrl: "http://private-4586c-profite.apiary-mock.com" + apiUrl: "https://private-4586c-profite.apiary-mock.com" }); \ No newline at end of file From 23f005d29e6673080ef7ed72665fdad6c502cb64 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sun, 18 Mar 2018 01:56:28 -0300 Subject: [PATCH 19/22] arquivos compilados --- dist/css/main.css | 2948 +++++++++++++++++++++++++++++++++++++++++++++ dist/js/main.js | 575 +++++++++ 2 files changed, 3523 insertions(+) create mode 100644 dist/css/main.css create mode 100644 dist/js/main.js diff --git a/dist/css/main.css b/dist/css/main.css new file mode 100644 index 00000000..cf80f93e --- /dev/null +++ b/dist/css/main.css @@ -0,0 +1,2948 @@ +.box-full { + width: 100%; + height: auto; + display: block; +} +.clear::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +@font-face { + font-family: 'bellcent_add_btaddress'; + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot'); + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2') format('woff2'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff') format('woff'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf') format('truetype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'bellcentennial_add_btaddress'; + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot'); + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff2') format('woff2'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff') format('woff'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf') format('truetype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'baskerville_regular'; + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot'); + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff2') format('woff2'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff') format('woff'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.ttf') format('truetype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.svg#webfontregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.container { + width: 940px; + height: auto; + display: block; + margin-left: auto; + margin-right: auto; +} +.container [class^=col]:first-child { + padding-left: 0; +} +.container [class^=col]:last-child { + padding-right: 0; +} +.row { + width: 100%; + height: auto; + display: block; + position: relative; + box-sizing: border-box; +} +.row::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +[class^=col] { + float: left; + height: auto; + padding-left: 20px; + padding-right: 20px; + position: relative; +} +.col-1 { + width: 8.33333333%; +} +.col-pull-1 { + left: 8.33333333%; +} +.col-2 { + width: 16.66666667%; +} +.col-pull-2 { + left: 16.66666667%; +} +.col-3 { + width: 25%; +} +.col-pull-3 { + left: 25%; +} +.col-4 { + width: 33.33333333%; +} +.col-pull-4 { + left: 33.33333333%; +} +.col-5 { + width: 41.66666667%; +} +.col-pull-5 { + left: 41.66666667%; +} +.col-6 { + width: 50%; +} +.col-pull-6 { + left: 50%; +} +.col-7 { + width: 58.33333333%; +} +.col-pull-7 { + left: 58.33333333%; +} +.col-8 { + width: 66.66666667%; +} +.col-pull-8 { + left: 66.66666667%; +} +.col-9 { + width: 75%; +} +.col-pull-9 { + left: 75%; +} +.col-10 { + width: 83.33333333%; +} +.col-pull-10 { + left: 83.33333333%; +} +.col-11 { + width: 91.66666667%; +} +.col-pull-11 { + left: 91.66666667%; +} +.col-12 { + width: 100%; +} +.col-pull-12 { + left: 100%; +} +.txt-hide { + font-size: 0.01px; + color: transparent; + text-indent: -999999px; +} +.list { + margin: 0; + padding: 0; + display: block; +} +.list li { + list-style: none; +} +.list.list-block li { + display: block; +} +.list.list-inline li { + display: inline; +} +.list.list-iblock li { + display: inline-block; +} +.fleft { + float: left; +} +.fright { + float: right; +} +.fnone { + float: none; +} +.txt-left { + text-align: left; +} +.txt-right { + text-align: right; +} +.txt-center { + text-align: center; +} +.txt-justify { + text-align: justify; +} +.hide, +.hide-desktop { + display: none!important; + transition: all .3s; +} +*, +*::after, +*::before { + box-sizing: border-box; +} +body { + font-size: 1em; + color: #000; + font-family: 'Open Sans', sans-serif; +} +#logo { + display: block; + width: 104px; + height: 29px; +} +#logo a { + width: inherit; + height: inherit; + display: block; + background: url("../../assets/svg/logo-profite.svg") no-repeat center transparent; +} +header { + width: 100%; + height: auto; + display: block; + border-bottom: #ff9f1c 2px solid; + padding: 10px 0; +} +footer { + width: 100%; + height: auto; + display: block; + background: #000; + padding: 5px 0; +} +footer p { + font-size: 0.75em; + color: #999; + font-family: 'Open Sans', sans-serif; +} +#bag { + background: transparent; + border: none; + position: relative; + width: 32px; + height: 27px; + float: right; + cursor: pointer; + margin-top: 23px; + outline: none; +} +#bag img { + position: absolute; + top: 0; + left: 0; +} +#bag #count { + position: absolute; + right: 0; + bottom: 0; + width: 20px; + height: 20px; + background: #ff9f1c; + border-radius: 50px; + display: block; +} +#bag #count span { + width: inherit; + height: inherit; + text-align: center; + vertical-align: middle; + color: #fff; + font-family: 'Open Sans', sans-serif; + font-size: 0.75em; + font-weight: 600; + display: table-cell; +} +.widget { + width: 90%; + height: auto; + display: block; + margin: 0 0 40px; +} +.widget .titulo { + font-size: 1.25em; + font-family: 'bellcent_add_btaddress', sans-serif; + letter-spacing: 0.10em; + color: #000; + margin: 0 0 25px; + text-transform: uppercase; +} +.widget ul li { + font-family: 'Open Sans', sans-serif; + font-size: 0.8125em; + font-weight: 400; + color: #000; + line-height: 24px; + cursor: pointer; +} +.checkbox { + width: 15px; + height: 15px; + display: inline-block; + border: #d8d8d8 solid 1px; + background: #fff; + position: relative; + margin-bottom: -3px; + margin-right: 2px; +} +.checkbox::before { + content: ""; + width: 9px; + height: 9px; + position: absolute; + background: #fff; + left: 2px; + top: 2px; + transition: background 0.3s ease-out; +} +.checkbox.selecionado::before { + background: #ff9f1c; + transition: background 0.3s ease-in; +} +.widget ul li.blocos { + width: 32px; + height: 32px; + text-align: center; + border: #e5e5e5 solid 1px; + margin-right: 10px; + margin-bottom: 10px; + color: #a9a9a9; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + line-height: normal; + padding: 6px 0 0; +} +.widget ul li.blocos:nth-child(4n) { + margin-right: 0; +} +.widget ul li.blocos.selecionado { + color: #000; + border-color: #ff9f1c; +} +.produtos { + width: 175px; + height: auto; + text-align: center; + display: inline-block; + margin: 0 0 80px; +} +.produtos::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +.produtos > img { + vertical-align: bottom; +} +.produtos .thumb { + width: 100%; + height: 330px; + display: block; +} +.produtos .thumb img { + width: auto; + height: 100%; +} +.produtos .box-titulo { + height: 35px; + width: 80%; + display: table; + margin: 25px auto 15px; +} +.produtos .box-titulo .titulo { + font-family: 'bellcent_add_btaddress', sans-serif; + font-size: 0.875em; + color: #000; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; + width: 100%; + height: inherit; +} +.produtos .boxprice { + width: 100%; + height: auto; + display: block; +} +.produtos .boxprice .valores { + width: 70%; + float: left; +} +.produtos .boxprice .add-cart { + background: #ff9f1c; + width: 29%; + height: 100%; + text-align: center; + border: none; + padding: 8px 0; + cursor: pointer; + float: right; + outline: none; + transition: background 0.3s ease-in; +} +.produtos .boxprice .add-cart[disabled], +.produtos .boxprice .add-cart[disabled]:hover { + background: #000; + cursor: no-drop; +} +.produtos .boxprice .add-cart:hover { + background: #b56800; + transition: background 0.3s ease-out; +} +.produtos .boxprice .valores h5 { + font-family: 'baskerville_regular', sans-serif; + font-size: 1em; + color: #000; + letter-spacing: 0.10em; + margin: 0; + display: block; +} +.produtos .boxprice .valores h5 small { + color: #666666; + text-decoration: line-through; +} +.produtos .boxprice .valores p { + font-family: 'baskerville_regular', sans-serif; + font-size: 0.875em; + color: #000; + display: block; + margin: 6px 0 0; +} +#lista-produtos li:nth-child(3n - 1) .produtos { + margin-left: 80px; + margin-right: 80px; +} +.btn { + font-size: 0.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: table; + width: auto; + height: auto; + margin: 50px auto; + padding: 15px 60px; + border: none; + color: #fff; + border-radius: 0; + text-align: center; + background: #ff9f1c; + letter-spacing: 0.10em; + cursor: pointer; + text-transform: uppercase; + text-decoration: none; +} +.btn:hover { + background: #b56800; +} +.btn-txt { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + display: block; + width: 100%; + height: auto; + margin: 10px 0 0; + padding: 0; + border: none; + color: #666; + border-radius: 0; + text-align: left; + background: transparent; + cursor: pointer; + text-decoration: underline; + outline: none; +} +.btn-txt .arrow { + font-family: cursive; + font-size: 16px; + display: inline-block; + transform: rotate(90deg); +} +.btn-txt .arrow.open { + transform: rotate(-90deg); +} +.btn-txt:hover { + color: #b56800; +} +#msg-erro { + background: rgba(0, 0, 0, 0.8); + width: 250px; + position: fixed; + z-index: 5000; + color: #fff; + border-radius: 50px; + padding: 8px 50px; + font-size: 13px; + top: 0; + left: 50%; + text-align: center; + margin-left: -125px; +} +.title-section { + font-size: 1.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: block; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; +} +.title-section-box { + padding: 45px 0; +} +.title-section-box .sel-camp { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + color: #000; + border: #666 solid 1px; + width: 100%; + height: auto; + display: block; +} +.tabela { + width: 100%; +} +.tabela tr td { + padding: 4px; +} +.tabela tr:nth-child(odd) td { + background: #f2f2f2; +} +.tabela tfoot tr td { + background: #bfbfbf!important; +} +#remove { + width: 25px; + height: 25px; + display: table-cell; + vertical-align: middle; + margin: 0 auto; + background: #ff9f1c; + border: none; + border-radius: 50px; + font-size: 13px; + color: #fff; + cursor: pointer; + outline: none; + transition: all 0.3s ease-in; +} +#remove:hover { + background: darkred; + transition: all 0.3s ease-out; +} +#limpar-filtros { + width: 150px; + height: auto; + background: #ff9f1c; + padding: 6px 0; + border: none; + cursor: pointer; + color: #fff; + font-size: 14px; + font-weight: 700; + font-family: 'Open Sans', sans-serif; + margin: 0 0 55px; + outline: none; + letter-spacing: 0.05em; + transition: all 0.3s ease-in; +} +#limpar-filtros span { + width: 33px; + height: inherit; + padding: 6px 0; + background: #b56800; + display: inline-block; + margin: -6px 0px -8px -18px; + transition: all 0.3s ease-in; +} +#limpar-filtros:hover { + background: #b56800; + transition: all 0.3s ease-out; +} +#limpar-filtros:hover span { + background: #ff9f1c; + transition: all 0.3s ease-out; +} +@media (min-width: 300px) and (max-width: 939px) { + .container { + width: 100%; + } + .hide-mobile { + display: none!important; + } + .hide-desktop { + display: initial!important; + } + .title-section { + text-align: center; + } + .mobile-block { + width: 100%; + } + .container .row > div { + padding: 0!important; + } + #lista-produtos li .produtos { + margin-left: 3px!important; + margin-right: 3px!important; + width: 135px; + } + aside, + article { + float: none!important; + width: 100%!important; + padding: 0!important; + } + aside::after, + article::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; + } + aside { + width: 90%!important; + margin: 0 auto 65px; + } + .widget { + width: 100%; + } + .btn-filtros { + width: 100%; + height: auto; + padding: 10px 0; + background: transparent; + color: #000; + text-align: center; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + font-size: 18px; + color: #666666; + border: none; + cursor: pointer; + border: #666 solid 1px; + outline: none; + } + .mobile-box-filtro, + .mobile-box-ordenar { + width: 50%; + float: left; + } + .titulo-filtro-tipo { + border-bottom: solid 1px #000; + padding-bottom: 5px; + } + .widget .titulo { + font-size: 0.875em; + cursor: pointer; + } + .widget .titulo span { + float: right; + margin-top: -3px; + } + .widget ul { + display: none!important; + } + .widget ul.abre-lista { + display: block!important; + } + .widget ul li.blocos:nth-child(4n) { + margin-right: 10px; + } + .widget ul li { + font-size: 15px; + line-height: 34px; + padding-left: 12px; + } + .mobile-widget { + display: none; + } + .mobile-widget p { + font-size: 16px; + width: 100%; + display: block!important; + border-bottom: #000 solid 1px; + margin: 0 0 15px; + padding: 0 12px 8px; + cursor: pointer; + } + .mobile-widget p span { + float: right; + margin-top: -3px; + margin-right: 12px; + } + #lista-cores-mobile { + display: block!important; + } + .mobile-widget.open-filter { + display: block!important; + position: absolute; + background: #fff; + width: 100%; + left: 0; + top: 0; + z-index: 1; + } + .hide { + display: block!important; + } + #btnCores { + display: none; + } +} +@media (min-width: 424px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 20px!important; + margin-right: 0px!important; + width: 175px; + } +} +@media (min-width: 767px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 50px!important; + margin-right: 0px!important; + width: 175px; + } +} + +.txt-hide { + font-size: 0.01px; + color: transparent; + text-indent: -999999px; +} +.list { + margin: 0; + padding: 0; + display: block; +} +.list li { + list-style: none; +} +.list.list-block li { + display: block; +} +.list.list-inline li { + display: inline; +} +.list.list-iblock li { + display: inline-block; +} +.fleft { + float: left; +} +.fright { + float: right; +} +.fnone { + float: none; +} +.txt-left { + text-align: left; +} +.txt-right { + text-align: right; +} +.txt-center { + text-align: center; +} +.txt-justify { + text-align: justify; +} +.hide, +.hide-desktop { + display: none!important; + transition: all .3s; +} + +@font-face { + font-family: 'bellcent_add_btaddress'; + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot'); + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2') format('woff2'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff') format('woff'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf') format('truetype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'bellcentennial_add_btaddress'; + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot'); + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff2') format('woff2'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff') format('woff'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf') format('truetype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'baskerville_regular'; + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot'); + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff2') format('woff2'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff') format('woff'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.ttf') format('truetype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.svg#webfontregular') format('svg'); + font-weight: normal; + font-style: normal; +} + +.box-full { + width: 100%; + height: auto; + display: block; +} +.clear::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +@font-face { + font-family: 'bellcent_add_btaddress'; + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot'); + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2') format('woff2'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff') format('woff'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf') format('truetype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'bellcentennial_add_btaddress'; + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot'); + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff2') format('woff2'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff') format('woff'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf') format('truetype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'baskerville_regular'; + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot'); + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff2') format('woff2'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff') format('woff'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.ttf') format('truetype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.svg#webfontregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.txt-hide { + font-size: 0.01px; + color: transparent; + text-indent: -999999px; +} +.list { + margin: 0; + padding: 0; + display: block; +} +.list li { + list-style: none; +} +.list.list-block li { + display: block; +} +.list.list-inline li { + display: inline; +} +.list.list-iblock li { + display: inline-block; +} +.fleft { + float: left; +} +.fright { + float: right; +} +.fnone { + float: none; +} +.txt-left { + text-align: left; +} +.txt-right { + text-align: right; +} +.txt-center { + text-align: center; +} +.txt-justify { + text-align: justify; +} +.hide, +.hide-desktop { + display: none!important; + transition: all .3s; +} +*, +*::after, +*::before { + box-sizing: border-box; +} +body { + font-size: 1em; + color: #000; + font-family: 'Open Sans', sans-serif; +} +#logo { + display: block; + width: 104px; + height: 29px; +} +#logo a { + width: inherit; + height: inherit; + display: block; + background: url("../../assets/svg/logo-profite.svg") no-repeat center transparent; +} +header { + width: 100%; + height: auto; + display: block; + border-bottom: #ff9f1c 2px solid; + padding: 10px 0; +} +footer { + width: 100%; + height: auto; + display: block; + background: #000; + padding: 5px 0; +} +footer p { + font-size: 0.75em; + color: #999; + font-family: 'Open Sans', sans-serif; +} +#bag { + background: transparent; + border: none; + position: relative; + width: 32px; + height: 27px; + float: right; + cursor: pointer; + margin-top: 23px; + outline: none; +} +#bag img { + position: absolute; + top: 0; + left: 0; +} +#bag #count { + position: absolute; + right: 0; + bottom: 0; + width: 20px; + height: 20px; + background: #ff9f1c; + border-radius: 50px; + display: block; +} +#bag #count span { + width: inherit; + height: inherit; + text-align: center; + vertical-align: middle; + color: #fff; + font-family: 'Open Sans', sans-serif; + font-size: 0.75em; + font-weight: 600; + display: table-cell; +} +.widget { + width: 90%; + height: auto; + display: block; + margin: 0 0 40px; +} +.widget .titulo { + font-size: 1.25em; + font-family: 'bellcent_add_btaddress', sans-serif; + letter-spacing: 0.10em; + color: #000; + margin: 0 0 25px; + text-transform: uppercase; +} +.widget ul li { + font-family: 'Open Sans', sans-serif; + font-size: 0.8125em; + font-weight: 400; + color: #000; + line-height: 24px; + cursor: pointer; +} +.checkbox { + width: 15px; + height: 15px; + display: inline-block; + border: #d8d8d8 solid 1px; + background: #fff; + position: relative; + margin-bottom: -3px; + margin-right: 2px; +} +.checkbox::before { + content: ""; + width: 9px; + height: 9px; + position: absolute; + background: #fff; + left: 2px; + top: 2px; + transition: background 0.3s ease-out; +} +.checkbox.selecionado::before { + background: #ff9f1c; + transition: background 0.3s ease-in; +} +.widget ul li.blocos { + width: 32px; + height: 32px; + text-align: center; + border: #e5e5e5 solid 1px; + margin-right: 10px; + margin-bottom: 10px; + color: #a9a9a9; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + line-height: normal; + padding: 6px 0 0; +} +.widget ul li.blocos:nth-child(4n) { + margin-right: 0; +} +.widget ul li.blocos.selecionado { + color: #000; + border-color: #ff9f1c; +} +.produtos { + width: 175px; + height: auto; + text-align: center; + display: inline-block; + margin: 0 0 80px; +} +.produtos::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +.produtos > img { + vertical-align: bottom; +} +.produtos .thumb { + width: 100%; + height: 330px; + display: block; +} +.produtos .thumb img { + width: auto; + height: 100%; +} +.produtos .box-titulo { + height: 35px; + width: 80%; + display: table; + margin: 25px auto 15px; +} +.produtos .box-titulo .titulo { + font-family: 'bellcent_add_btaddress', sans-serif; + font-size: 0.875em; + color: #000; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; + width: 100%; + height: inherit; +} +.produtos .boxprice { + width: 100%; + height: auto; + display: block; +} +.produtos .boxprice .valores { + width: 70%; + float: left; +} +.produtos .boxprice .add-cart { + background: #ff9f1c; + width: 29%; + height: 100%; + text-align: center; + border: none; + padding: 8px 0; + cursor: pointer; + float: right; + outline: none; + transition: background 0.3s ease-in; +} +.produtos .boxprice .add-cart[disabled], +.produtos .boxprice .add-cart[disabled]:hover { + background: #000; + cursor: no-drop; +} +.produtos .boxprice .add-cart:hover { + background: #b56800; + transition: background 0.3s ease-out; +} +.produtos .boxprice .valores h5 { + font-family: 'baskerville_regular', sans-serif; + font-size: 1em; + color: #000; + letter-spacing: 0.10em; + margin: 0; + display: block; +} +.produtos .boxprice .valores h5 small { + color: #666666; + text-decoration: line-through; +} +.produtos .boxprice .valores p { + font-family: 'baskerville_regular', sans-serif; + font-size: 0.875em; + color: #000; + display: block; + margin: 6px 0 0; +} +#lista-produtos li:nth-child(3n - 1) .produtos { + margin-left: 80px; + margin-right: 80px; +} +.btn { + font-size: 0.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: table; + width: auto; + height: auto; + margin: 50px auto; + padding: 15px 60px; + border: none; + color: #fff; + border-radius: 0; + text-align: center; + background: #ff9f1c; + letter-spacing: 0.10em; + cursor: pointer; + text-transform: uppercase; + text-decoration: none; +} +.btn:hover { + background: #b56800; +} +.btn-txt { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + display: block; + width: 100%; + height: auto; + margin: 10px 0 0; + padding: 0; + border: none; + color: #666; + border-radius: 0; + text-align: left; + background: transparent; + cursor: pointer; + text-decoration: underline; + outline: none; +} +.btn-txt .arrow { + font-family: cursive; + font-size: 16px; + display: inline-block; + transform: rotate(90deg); +} +.btn-txt .arrow.open { + transform: rotate(-90deg); +} +.btn-txt:hover { + color: #b56800; +} +#msg-erro { + background: rgba(0, 0, 0, 0.8); + width: 250px; + position: fixed; + z-index: 5000; + color: #fff; + border-radius: 50px; + padding: 8px 50px; + font-size: 13px; + top: 0; + left: 50%; + text-align: center; + margin-left: -125px; +} +.title-section { + font-size: 1.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: block; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; +} +.title-section-box { + padding: 45px 0; +} +.title-section-box .sel-camp { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + color: #000; + border: #666 solid 1px; + width: 100%; + height: auto; + display: block; +} +.tabela { + width: 100%; +} +.tabela tr td { + padding: 4px; +} +.tabela tr:nth-child(odd) td { + background: #f2f2f2; +} +.tabela tfoot tr td { + background: #bfbfbf!important; +} +#remove { + width: 25px; + height: 25px; + display: table-cell; + vertical-align: middle; + margin: 0 auto; + background: #ff9f1c; + border: none; + border-radius: 50px; + font-size: 13px; + color: #fff; + cursor: pointer; + outline: none; + transition: all 0.3s ease-in; +} +#remove:hover { + background: darkred; + transition: all 0.3s ease-out; +} +#limpar-filtros { + width: 150px; + height: auto; + background: #ff9f1c; + padding: 6px 0; + border: none; + cursor: pointer; + color: #fff; + font-size: 14px; + font-weight: 700; + font-family: 'Open Sans', sans-serif; + margin: 0 0 55px; + outline: none; + letter-spacing: 0.05em; + transition: all 0.3s ease-in; +} +#limpar-filtros span { + width: 33px; + height: inherit; + padding: 6px 0; + background: #b56800; + display: inline-block; + margin: -6px 0px -8px -18px; + transition: all 0.3s ease-in; +} +#limpar-filtros:hover { + background: #b56800; + transition: all 0.3s ease-out; +} +#limpar-filtros:hover span { + background: #ff9f1c; + transition: all 0.3s ease-out; +} +@media (min-width: 300px) and (max-width: 939px) { + .container { + width: 100%; + } + .hide-mobile { + display: none!important; + } + .hide-desktop { + display: initial!important; + } + .title-section { + text-align: center; + } + .mobile-block { + width: 100%; + } + .container .row > div { + padding: 0!important; + } + #lista-produtos li .produtos { + margin-left: 3px!important; + margin-right: 3px!important; + width: 135px; + } + aside, + article { + float: none!important; + width: 100%!important; + padding: 0!important; + } + aside::after, + article::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; + } + aside { + width: 90%!important; + margin: 0 auto 65px; + } + .widget { + width: 100%; + } + .btn-filtros { + width: 100%; + height: auto; + padding: 10px 0; + background: transparent; + color: #000; + text-align: center; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + font-size: 18px; + color: #666666; + border: none; + cursor: pointer; + border: #666 solid 1px; + outline: none; + } + .mobile-box-filtro, + .mobile-box-ordenar { + width: 50%; + float: left; + } + .titulo-filtro-tipo { + border-bottom: solid 1px #000; + padding-bottom: 5px; + } + .widget .titulo { + font-size: 0.875em; + cursor: pointer; + } + .widget .titulo span { + float: right; + margin-top: -3px; + } + .widget ul { + display: none!important; + } + .widget ul.abre-lista { + display: block!important; + } + .widget ul li.blocos:nth-child(4n) { + margin-right: 10px; + } + .widget ul li { + font-size: 15px; + line-height: 34px; + padding-left: 12px; + } + .mobile-widget { + display: none; + } + .mobile-widget p { + font-size: 16px; + width: 100%; + display: block!important; + border-bottom: #000 solid 1px; + margin: 0 0 15px; + padding: 0 12px 8px; + cursor: pointer; + } + .mobile-widget p span { + float: right; + margin-top: -3px; + margin-right: 12px; + } + #lista-cores-mobile { + display: block!important; + } + .mobile-widget.open-filter { + display: block!important; + position: absolute; + background: #fff; + width: 100%; + left: 0; + top: 0; + z-index: 1; + } + .hide { + display: block!important; + } + #btnCores { + display: none; + } +} +@media (min-width: 424px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 20px!important; + margin-right: 0px!important; + width: 175px; + } +} +@media (min-width: 767px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 50px!important; + margin-right: 0px!important; + width: 175px; + } +} +.container { + width: 940px; + height: auto; + display: block; + margin-left: auto; + margin-right: auto; +} +.container [class^=col]:first-child { + padding-left: 0; +} +.container [class^=col]:last-child { + padding-right: 0; +} +.row { + width: 100%; + height: auto; + display: block; + position: relative; + box-sizing: border-box; +} +.row::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +[class^=col] { + float: left; + height: auto; + padding-left: 20px; + padding-right: 20px; + position: relative; +} +.col-1 { + width: 8.33333333%; +} +.col-pull-1 { + left: 8.33333333%; +} +.col-2 { + width: 16.66666667%; +} +.col-pull-2 { + left: 16.66666667%; +} +.col-3 { + width: 25%; +} +.col-pull-3 { + left: 25%; +} +.col-4 { + width: 33.33333333%; +} +.col-pull-4 { + left: 33.33333333%; +} +.col-5 { + width: 41.66666667%; +} +.col-pull-5 { + left: 41.66666667%; +} +.col-6 { + width: 50%; +} +.col-pull-6 { + left: 50%; +} +.col-7 { + width: 58.33333333%; +} +.col-pull-7 { + left: 58.33333333%; +} +.col-8 { + width: 66.66666667%; +} +.col-pull-8 { + left: 66.66666667%; +} +.col-9 { + width: 75%; +} +.col-pull-9 { + left: 75%; +} +.col-10 { + width: 83.33333333%; +} +.col-pull-10 { + left: 83.33333333%; +} +.col-11 { + width: 91.66666667%; +} +.col-pull-11 { + left: 91.66666667%; +} +.col-12 { + width: 100%; +} +.col-pull-12 { + left: 100%; +} + +.box-full { + width: 100%; + height: auto; + display: block; +} +.clear::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +@font-face { + font-family: 'bellcent_add_btaddress'; + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot'); + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2') format('woff2'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff') format('woff'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf') format('truetype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'bellcentennial_add_btaddress'; + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot'); + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff2') format('woff2'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff') format('woff'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf') format('truetype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'baskerville_regular'; + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot'); + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff2') format('woff2'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff') format('woff'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.ttf') format('truetype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.svg#webfontregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.container { + width: 940px; + height: auto; + display: block; + margin-left: auto; + margin-right: auto; +} +.container [class^=col]:first-child { + padding-left: 0; +} +.container [class^=col]:last-child { + padding-right: 0; +} +.row { + width: 100%; + height: auto; + display: block; + position: relative; + box-sizing: border-box; +} +.row::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +[class^=col] { + float: left; + height: auto; + padding-left: 20px; + padding-right: 20px; + position: relative; +} +.col-1 { + width: 8.33333333%; +} +.col-pull-1 { + left: 8.33333333%; +} +.col-2 { + width: 16.66666667%; +} +.col-pull-2 { + left: 16.66666667%; +} +.col-3 { + width: 25%; +} +.col-pull-3 { + left: 25%; +} +.col-4 { + width: 33.33333333%; +} +.col-pull-4 { + left: 33.33333333%; +} +.col-5 { + width: 41.66666667%; +} +.col-pull-5 { + left: 41.66666667%; +} +.col-6 { + width: 50%; +} +.col-pull-6 { + left: 50%; +} +.col-7 { + width: 58.33333333%; +} +.col-pull-7 { + left: 58.33333333%; +} +.col-8 { + width: 66.66666667%; +} +.col-pull-8 { + left: 66.66666667%; +} +.col-9 { + width: 75%; +} +.col-pull-9 { + left: 75%; +} +.col-10 { + width: 83.33333333%; +} +.col-pull-10 { + left: 83.33333333%; +} +.col-11 { + width: 91.66666667%; +} +.col-pull-11 { + left: 91.66666667%; +} +.col-12 { + width: 100%; +} +.col-pull-12 { + left: 100%; +} +.txt-hide { + font-size: 0.01px; + color: transparent; + text-indent: -999999px; +} +.list { + margin: 0; + padding: 0; + display: block; +} +.list li { + list-style: none; +} +.list.list-block li { + display: block; +} +.list.list-inline li { + display: inline; +} +.list.list-iblock li { + display: inline-block; +} +.fleft { + float: left; +} +.fright { + float: right; +} +.fnone { + float: none; +} +.txt-left { + text-align: left; +} +.txt-right { + text-align: right; +} +.txt-center { + text-align: center; +} +.txt-justify { + text-align: justify; +} +.hide, +.hide-desktop { + display: none!important; + transition: all .3s; +} +@media (min-width: 300px) and (max-width: 939px) { + .container { + width: 100%; + } + .hide-mobile { + display: none!important; + } + .hide-desktop { + display: initial!important; + } + .title-section { + text-align: center; + } + .mobile-block { + width: 100%; + } + .container .row > div { + padding: 0!important; + } + #lista-produtos li .produtos { + margin-left: 3px!important; + margin-right: 3px!important; + width: 135px; + } + aside, + article { + float: none!important; + width: 100%!important; + padding: 0!important; + } + aside::after, + article::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; + } + aside { + width: 90%!important; + margin: 0 auto 65px; + } + .widget { + width: 100%; + } + .btn-filtros { + width: 100%; + height: auto; + padding: 10px 0; + background: transparent; + color: #000; + text-align: center; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + font-size: 18px; + color: #666666; + border: none; + cursor: pointer; + border: #666 solid 1px; + outline: none; + } + .mobile-box-filtro, + .mobile-box-ordenar { + width: 50%; + float: left; + } + .titulo-filtro-tipo { + border-bottom: solid 1px #000; + padding-bottom: 5px; + } + .widget .titulo { + font-size: 0.875em; + cursor: pointer; + } + .widget .titulo span { + float: right; + margin-top: -3px; + } + .widget ul { + display: none!important; + } + .widget ul.abre-lista { + display: block!important; + } + .widget ul li.blocos:nth-child(4n) { + margin-right: 10px; + } + .widget ul li { + font-size: 15px; + line-height: 34px; + padding-left: 12px; + } + .mobile-widget { + display: none; + } + .mobile-widget p { + font-size: 16px; + width: 100%; + display: block!important; + border-bottom: #000 solid 1px; + margin: 0 0 15px; + padding: 0 12px 8px; + cursor: pointer; + } + .mobile-widget p span { + float: right; + margin-top: -3px; + margin-right: 12px; + } + #lista-cores-mobile { + display: block!important; + } + .mobile-widget.open-filter { + display: block!important; + position: absolute; + background: #fff; + width: 100%; + left: 0; + top: 0; + z-index: 1; + } + .hide { + display: block!important; + } + #btnCores { + display: none; + } +} +@media (min-width: 424px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 20px!important; + margin-right: 0px!important; + width: 175px; + } +} +@media (min-width: 767px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 50px!important; + margin-right: 0px!important; + width: 175px; + } +} +*, +*::after, +*::before { + box-sizing: border-box; +} +body { + font-size: 1em; + color: #000; + font-family: 'Open Sans', sans-serif; +} +#logo { + display: block; + width: 104px; + height: 29px; +} +#logo a { + width: inherit; + height: inherit; + display: block; + background: url("../../assets/svg/logo-profite.svg") no-repeat center transparent; +} +header { + width: 100%; + height: auto; + display: block; + border-bottom: #ff9f1c 2px solid; + padding: 10px 0; +} +footer { + width: 100%; + height: auto; + display: block; + background: #000; + padding: 5px 0; +} +footer p { + font-size: 0.75em; + color: #999; + font-family: 'Open Sans', sans-serif; +} +#bag { + background: transparent; + border: none; + position: relative; + width: 32px; + height: 27px; + float: right; + cursor: pointer; + margin-top: 23px; + outline: none; +} +#bag img { + position: absolute; + top: 0; + left: 0; +} +#bag #count { + position: absolute; + right: 0; + bottom: 0; + width: 20px; + height: 20px; + background: #ff9f1c; + border-radius: 50px; + display: block; +} +#bag #count span { + width: inherit; + height: inherit; + text-align: center; + vertical-align: middle; + color: #fff; + font-family: 'Open Sans', sans-serif; + font-size: 0.75em; + font-weight: 600; + display: table-cell; +} +.widget { + width: 90%; + height: auto; + display: block; + margin: 0 0 40px; +} +.widget .titulo { + font-size: 1.25em; + font-family: 'bellcent_add_btaddress', sans-serif; + letter-spacing: 0.10em; + color: #000; + margin: 0 0 25px; + text-transform: uppercase; +} +.widget ul li { + font-family: 'Open Sans', sans-serif; + font-size: 0.8125em; + font-weight: 400; + color: #000; + line-height: 24px; + cursor: pointer; +} +.checkbox { + width: 15px; + height: 15px; + display: inline-block; + border: #d8d8d8 solid 1px; + background: #fff; + position: relative; + margin-bottom: -3px; + margin-right: 2px; +} +.checkbox::before { + content: ""; + width: 9px; + height: 9px; + position: absolute; + background: #fff; + left: 2px; + top: 2px; + transition: background 0.3s ease-out; +} +.checkbox.selecionado::before { + background: #ff9f1c; + transition: background 0.3s ease-in; +} +.widget ul li.blocos { + width: 32px; + height: 32px; + text-align: center; + border: #e5e5e5 solid 1px; + margin-right: 10px; + margin-bottom: 10px; + color: #a9a9a9; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + line-height: normal; + padding: 6px 0 0; +} +.widget ul li.blocos:nth-child(4n) { + margin-right: 0; +} +.widget ul li.blocos.selecionado { + color: #000; + border-color: #ff9f1c; +} +.produtos { + width: 175px; + height: auto; + text-align: center; + display: inline-block; + margin: 0 0 80px; +} +.produtos::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +.produtos > img { + vertical-align: bottom; +} +.produtos .thumb { + width: 100%; + height: 330px; + display: block; +} +.produtos .thumb img { + width: auto; + height: 100%; +} +.produtos .box-titulo { + height: 35px; + width: 80%; + display: table; + margin: 25px auto 15px; +} +.produtos .box-titulo .titulo { + font-family: 'bellcent_add_btaddress', sans-serif; + font-size: 0.875em; + color: #000; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; + width: 100%; + height: inherit; +} +.produtos .boxprice { + width: 100%; + height: auto; + display: block; +} +.produtos .boxprice .valores { + width: 70%; + float: left; +} +.produtos .boxprice .add-cart { + background: #ff9f1c; + width: 29%; + height: 100%; + text-align: center; + border: none; + padding: 8px 0; + cursor: pointer; + float: right; + outline: none; + transition: background 0.3s ease-in; +} +.produtos .boxprice .add-cart[disabled], +.produtos .boxprice .add-cart[disabled]:hover { + background: #000; + cursor: no-drop; +} +.produtos .boxprice .add-cart:hover { + background: #b56800; + transition: background 0.3s ease-out; +} +.produtos .boxprice .valores h5 { + font-family: 'baskerville_regular', sans-serif; + font-size: 1em; + color: #000; + letter-spacing: 0.10em; + margin: 0; + display: block; +} +.produtos .boxprice .valores h5 small { + color: #666666; + text-decoration: line-through; +} +.produtos .boxprice .valores p { + font-family: 'baskerville_regular', sans-serif; + font-size: 0.875em; + color: #000; + display: block; + margin: 6px 0 0; +} +#lista-produtos li:nth-child(3n - 1) .produtos { + margin-left: 80px; + margin-right: 80px; +} +.btn { + font-size: 0.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: table; + width: auto; + height: auto; + margin: 50px auto; + padding: 15px 60px; + border: none; + color: #fff; + border-radius: 0; + text-align: center; + background: #ff9f1c; + letter-spacing: 0.10em; + cursor: pointer; + text-transform: uppercase; + text-decoration: none; +} +.btn:hover { + background: #b56800; +} +.btn-txt { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + display: block; + width: 100%; + height: auto; + margin: 10px 0 0; + padding: 0; + border: none; + color: #666; + border-radius: 0; + text-align: left; + background: transparent; + cursor: pointer; + text-decoration: underline; + outline: none; +} +.btn-txt .arrow { + font-family: cursive; + font-size: 16px; + display: inline-block; + transform: rotate(90deg); +} +.btn-txt .arrow.open { + transform: rotate(-90deg); +} +.btn-txt:hover { + color: #b56800; +} +#msg-erro { + background: rgba(0, 0, 0, 0.8); + width: 250px; + position: fixed; + z-index: 5000; + color: #fff; + border-radius: 50px; + padding: 8px 50px; + font-size: 13px; + top: 0; + left: 50%; + text-align: center; + margin-left: -125px; +} +.title-section { + font-size: 1.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: block; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; +} +.title-section-box { + padding: 45px 0; +} +.title-section-box .sel-camp { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + color: #000; + border: #666 solid 1px; + width: 100%; + height: auto; + display: block; +} +.tabela { + width: 100%; +} +.tabela tr td { + padding: 4px; +} +.tabela tr:nth-child(odd) td { + background: #f2f2f2; +} +.tabela tfoot tr td { + background: #bfbfbf!important; +} +#remove { + width: 25px; + height: 25px; + display: table-cell; + vertical-align: middle; + margin: 0 auto; + background: #ff9f1c; + border: none; + border-radius: 50px; + font-size: 13px; + color: #fff; + cursor: pointer; + outline: none; + transition: all 0.3s ease-in; +} +#remove:hover { + background: darkred; + transition: all 0.3s ease-out; +} +#limpar-filtros { + width: 150px; + height: auto; + background: #ff9f1c; + padding: 6px 0; + border: none; + cursor: pointer; + color: #fff; + font-size: 14px; + font-weight: 700; + font-family: 'Open Sans', sans-serif; + margin: 0 0 55px; + outline: none; + letter-spacing: 0.05em; + transition: all 0.3s ease-in; +} +#limpar-filtros span { + width: 33px; + height: inherit; + padding: 6px 0; + background: #b56800; + display: inline-block; + margin: -6px 0px -8px -18px; + transition: all 0.3s ease-in; +} +#limpar-filtros:hover { + background: #b56800; + transition: all 0.3s ease-out; +} +#limpar-filtros:hover span { + background: #ff9f1c; + transition: all 0.3s ease-out; +} + +.box-full { + width: 100%; + height: auto; + display: block; +} +.clear::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +@font-face { + font-family: 'bellcent_add_btaddress'; + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot'); + src: url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff2') format('woff2'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.woff') format('woff'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.ttf') format('truetype'), url('../../assets/fonts/bellcent/bellcent-add-bt-address-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'bellcentennial_add_btaddress'; + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot'); + src: url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff2') format('woff2'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.woff') format('woff'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.ttf') format('truetype'), url('../../assets/fonts/bell-centennial/bell-centennial-address-bt-webfont.svg#bellcent_add_btaddress') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'baskerville_regular'; + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot'); + src: url('../../assets/fonts/baskerville/baskerville-normal-webfont.eot?#iefix') format('embedded-opentype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff2') format('woff2'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.woff') format('woff'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.ttf') format('truetype'), url('../../assets/fonts/baskerville/baskerville-normal-webfont.svg#webfontregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.container { + width: 940px; + height: auto; + display: block; + margin-left: auto; + margin-right: auto; +} +.container [class^=col]:first-child { + padding-left: 0; +} +.container [class^=col]:last-child { + padding-right: 0; +} +.row { + width: 100%; + height: auto; + display: block; + position: relative; + box-sizing: border-box; +} +.row::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +[class^=col] { + float: left; + height: auto; + padding-left: 20px; + padding-right: 20px; + position: relative; +} +.col-1 { + width: 8.33333333%; +} +.col-pull-1 { + left: 8.33333333%; +} +.col-2 { + width: 16.66666667%; +} +.col-pull-2 { + left: 16.66666667%; +} +.col-3 { + width: 25%; +} +.col-pull-3 { + left: 25%; +} +.col-4 { + width: 33.33333333%; +} +.col-pull-4 { + left: 33.33333333%; +} +.col-5 { + width: 41.66666667%; +} +.col-pull-5 { + left: 41.66666667%; +} +.col-6 { + width: 50%; +} +.col-pull-6 { + left: 50%; +} +.col-7 { + width: 58.33333333%; +} +.col-pull-7 { + left: 58.33333333%; +} +.col-8 { + width: 66.66666667%; +} +.col-pull-8 { + left: 66.66666667%; +} +.col-9 { + width: 75%; +} +.col-pull-9 { + left: 75%; +} +.col-10 { + width: 83.33333333%; +} +.col-pull-10 { + left: 83.33333333%; +} +.col-11 { + width: 91.66666667%; +} +.col-pull-11 { + left: 91.66666667%; +} +.col-12 { + width: 100%; +} +.col-pull-12 { + left: 100%; +} +.txt-hide { + font-size: 0.01px; + color: transparent; + text-indent: -999999px; +} +.list { + margin: 0; + padding: 0; + display: block; +} +.list li { + list-style: none; +} +.list.list-block li { + display: block; +} +.list.list-inline li { + display: inline; +} +.list.list-iblock li { + display: inline-block; +} +.fleft { + float: left; +} +.fright { + float: right; +} +.fnone { + float: none; +} +.txt-left { + text-align: left; +} +.txt-right { + text-align: right; +} +.txt-center { + text-align: center; +} +.txt-justify { + text-align: justify; +} +.hide, +.hide-desktop { + display: none!important; + transition: all .3s; +} +*, +*::after, +*::before { + box-sizing: border-box; +} +body { + font-size: 1em; + color: #000; + font-family: 'Open Sans', sans-serif; +} +#logo { + display: block; + width: 104px; + height: 29px; +} +#logo a { + width: inherit; + height: inherit; + display: block; + background: url("../../assets/svg/logo-profite.svg") no-repeat center transparent; +} +header { + width: 100%; + height: auto; + display: block; + border-bottom: #ff9f1c 2px solid; + padding: 10px 0; +} +footer { + width: 100%; + height: auto; + display: block; + background: #000; + padding: 5px 0; +} +footer p { + font-size: 0.75em; + color: #999; + font-family: 'Open Sans', sans-serif; +} +#bag { + background: transparent; + border: none; + position: relative; + width: 32px; + height: 27px; + float: right; + cursor: pointer; + margin-top: 23px; + outline: none; +} +#bag img { + position: absolute; + top: 0; + left: 0; +} +#bag #count { + position: absolute; + right: 0; + bottom: 0; + width: 20px; + height: 20px; + background: #ff9f1c; + border-radius: 50px; + display: block; +} +#bag #count span { + width: inherit; + height: inherit; + text-align: center; + vertical-align: middle; + color: #fff; + font-family: 'Open Sans', sans-serif; + font-size: 0.75em; + font-weight: 600; + display: table-cell; +} +.widget { + width: 90%; + height: auto; + display: block; + margin: 0 0 40px; +} +.widget .titulo { + font-size: 1.25em; + font-family: 'bellcent_add_btaddress', sans-serif; + letter-spacing: 0.10em; + color: #000; + margin: 0 0 25px; + text-transform: uppercase; +} +.widget ul li { + font-family: 'Open Sans', sans-serif; + font-size: 0.8125em; + font-weight: 400; + color: #000; + line-height: 24px; + cursor: pointer; +} +.checkbox { + width: 15px; + height: 15px; + display: inline-block; + border: #d8d8d8 solid 1px; + background: #fff; + position: relative; + margin-bottom: -3px; + margin-right: 2px; +} +.checkbox::before { + content: ""; + width: 9px; + height: 9px; + position: absolute; + background: #fff; + left: 2px; + top: 2px; + transition: background 0.3s ease-out; +} +.checkbox.selecionado::before { + background: #ff9f1c; + transition: background 0.3s ease-in; +} +.widget ul li.blocos { + width: 32px; + height: 32px; + text-align: center; + border: #e5e5e5 solid 1px; + margin-right: 10px; + margin-bottom: 10px; + color: #a9a9a9; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + line-height: normal; + padding: 6px 0 0; +} +.widget ul li.blocos:nth-child(4n) { + margin-right: 0; +} +.widget ul li.blocos.selecionado { + color: #000; + border-color: #ff9f1c; +} +.produtos { + width: 175px; + height: auto; + text-align: center; + display: inline-block; + margin: 0 0 80px; +} +.produtos::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} +.produtos > img { + vertical-align: bottom; +} +.produtos .thumb { + width: 100%; + height: 330px; + display: block; +} +.produtos .thumb img { + width: auto; + height: 100%; +} +.produtos .box-titulo { + height: 35px; + width: 80%; + display: table; + margin: 25px auto 15px; +} +.produtos .box-titulo .titulo { + font-family: 'bellcent_add_btaddress', sans-serif; + font-size: 0.875em; + color: #000; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; + width: 100%; + height: inherit; +} +.produtos .boxprice { + width: 100%; + height: auto; + display: block; +} +.produtos .boxprice .valores { + width: 70%; + float: left; +} +.produtos .boxprice .add-cart { + background: #ff9f1c; + width: 29%; + height: 100%; + text-align: center; + border: none; + padding: 8px 0; + cursor: pointer; + float: right; + outline: none; + transition: background 0.3s ease-in; +} +.produtos .boxprice .add-cart[disabled], +.produtos .boxprice .add-cart[disabled]:hover { + background: #000; + cursor: no-drop; +} +.produtos .boxprice .add-cart:hover { + background: #b56800; + transition: background 0.3s ease-out; +} +.produtos .boxprice .valores h5 { + font-family: 'baskerville_regular', sans-serif; + font-size: 1em; + color: #000; + letter-spacing: 0.10em; + margin: 0; + display: block; +} +.produtos .boxprice .valores h5 small { + color: #666666; + text-decoration: line-through; +} +.produtos .boxprice .valores p { + font-family: 'baskerville_regular', sans-serif; + font-size: 0.875em; + color: #000; + display: block; + margin: 6px 0 0; +} +#lista-produtos li:nth-child(3n - 1) .produtos { + margin-left: 80px; + margin-right: 80px; +} +.btn { + font-size: 0.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: table; + width: auto; + height: auto; + margin: 50px auto; + padding: 15px 60px; + border: none; + color: #fff; + border-radius: 0; + text-align: center; + background: #ff9f1c; + letter-spacing: 0.10em; + cursor: pointer; + text-transform: uppercase; + text-decoration: none; +} +.btn:hover { + background: #b56800; +} +.btn-txt { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + display: block; + width: 100%; + height: auto; + margin: 10px 0 0; + padding: 0; + border: none; + color: #666; + border-radius: 0; + text-align: left; + background: transparent; + cursor: pointer; + text-decoration: underline; + outline: none; +} +.btn-txt .arrow { + font-family: cursive; + font-size: 16px; + display: inline-block; + transform: rotate(90deg); +} +.btn-txt .arrow.open { + transform: rotate(-90deg); +} +.btn-txt:hover { + color: #b56800; +} +#msg-erro { + background: rgba(0, 0, 0, 0.8); + width: 250px; + position: fixed; + z-index: 5000; + color: #fff; + border-radius: 50px; + padding: 8px 50px; + font-size: 13px; + top: 0; + left: 50%; + text-align: center; + margin-left: -125px; +} +.title-section { + font-size: 1.875em; + font-family: 'bellcentennial_add_btaddress', sans-serif; + display: block; + letter-spacing: 0.10em; + text-transform: uppercase; + margin: 0; +} +.title-section-box { + padding: 45px 0; +} +.title-section-box .sel-camp { + font-size: 0.75em; + font-family: 'Open Sans', sans-serif; + color: #000; + border: #666 solid 1px; + width: 100%; + height: auto; + display: block; +} +.tabela { + width: 100%; +} +.tabela tr td { + padding: 4px; +} +.tabela tr:nth-child(odd) td { + background: #f2f2f2; +} +.tabela tfoot tr td { + background: #bfbfbf!important; +} +#remove { + width: 25px; + height: 25px; + display: table-cell; + vertical-align: middle; + margin: 0 auto; + background: #ff9f1c; + border: none; + border-radius: 50px; + font-size: 13px; + color: #fff; + cursor: pointer; + outline: none; + transition: all 0.3s ease-in; +} +#remove:hover { + background: darkred; + transition: all 0.3s ease-out; +} +#limpar-filtros { + width: 150px; + height: auto; + background: #ff9f1c; + padding: 6px 0; + border: none; + cursor: pointer; + color: #fff; + font-size: 14px; + font-weight: 700; + font-family: 'Open Sans', sans-serif; + margin: 0 0 55px; + outline: none; + letter-spacing: 0.05em; + transition: all 0.3s ease-in; +} +#limpar-filtros span { + width: 33px; + height: inherit; + padding: 6px 0; + background: #b56800; + display: inline-block; + margin: -6px 0px -8px -18px; + transition: all 0.3s ease-in; +} +#limpar-filtros:hover { + background: #b56800; + transition: all 0.3s ease-out; +} +#limpar-filtros:hover span { + background: #ff9f1c; + transition: all 0.3s ease-out; +} +@media (min-width: 300px) and (max-width: 939px) { + .container { + width: 100%; + } + .hide-mobile { + display: none!important; + } + .hide-desktop { + display: initial!important; + } + .title-section { + text-align: center; + } + .mobile-block { + width: 100%; + } + .container .row > div { + padding: 0!important; + } + #lista-produtos li .produtos { + margin-left: 3px!important; + margin-right: 3px!important; + width: 135px; + } + aside, + article { + float: none!important; + width: 100%!important; + padding: 0!important; + } + aside::after, + article::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; + } + aside { + width: 90%!important; + margin: 0 auto 65px; + } + .widget { + width: 100%; + } + .btn-filtros { + width: 100%; + height: auto; + padding: 10px 0; + background: transparent; + color: #000; + text-align: center; + font-family: 'Open Sans', sans-serif; + font-weight: 400; + font-size: 18px; + color: #666666; + border: none; + cursor: pointer; + border: #666 solid 1px; + outline: none; + } + .mobile-box-filtro, + .mobile-box-ordenar { + width: 50%; + float: left; + } + .titulo-filtro-tipo { + border-bottom: solid 1px #000; + padding-bottom: 5px; + } + .widget .titulo { + font-size: 0.875em; + cursor: pointer; + } + .widget .titulo span { + float: right; + margin-top: -3px; + } + .widget ul { + display: none!important; + } + .widget ul.abre-lista { + display: block!important; + } + .widget ul li.blocos:nth-child(4n) { + margin-right: 10px; + } + .widget ul li { + font-size: 15px; + line-height: 34px; + padding-left: 12px; + } + .mobile-widget { + display: none; + } + .mobile-widget p { + font-size: 16px; + width: 100%; + display: block!important; + border-bottom: #000 solid 1px; + margin: 0 0 15px; + padding: 0 12px 8px; + cursor: pointer; + } + .mobile-widget p span { + float: right; + margin-top: -3px; + margin-right: 12px; + } + #lista-cores-mobile { + display: block!important; + } + .mobile-widget.open-filter { + display: block!important; + position: absolute; + background: #fff; + width: 100%; + left: 0; + top: 0; + z-index: 1; + } + .hide { + display: block!important; + } + #btnCores { + display: none; + } +} +@media (min-width: 424px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 20px!important; + margin-right: 0px!important; + width: 175px; + } +} +@media (min-width: 767px) and (max-width: 939px) { + #lista-produtos li .produtos { + margin-left: 50px!important; + margin-right: 0px!important; + width: 175px; + } +} + +.box-full { + width: 100%; + height: auto; + display: block; +} +.clear::after { + content: ""; + display: table; + height: 1px; + clear: both; + margin: 10px 0 0; +} + diff --git a/dist/js/main.js b/dist/js/main.js new file mode 100644 index 00000000..1c3b7c05 --- /dev/null +++ b/dist/js/main.js @@ -0,0 +1,575 @@ +var app = angular.module('profite', ['ngRoute']); + app.run(['$rootScope', function ($rootScope) { + $rootScope.listaCompra = []; + }]); + app.config(['$compileProvider', function ($compileProvider) { + $compileProvider.debugInfoEnabled(false); + }]); + app.constant('OrdenarOpt', [ + {id: 1, label: "Mais Recentes"}, + {id: 2, label: "Menor Preço"}, + {id: 3, label: "Maior Preço"} + ]); +angular + .module('profite') + .config(routeConfig); + + function routeConfig($routeProvider, $locationProvider){ + + $locationProvider.hashPrefix(""); + + $routeProvider.when('/home', { + templateUrl: 'view/home.html', + controller: 'homeCtrl' + }); + $routeProvider.when('/carrinho', { + templateUrl: 'view/carrinho.html', + controller: 'carrinhoCtrl' + }); + $routeProvider.otherwise({redirectTo: '/home'}); + } +angular.module('profite') + .controller('carrinhoCtrl', function ($scope, Carrinho) { + $scope.ListaCompras = Carrinho.get(); + + $scope.total = function () { + var t = 0; + for(var i in $scope.ListaCompras){ + t += $scope.ListaCompras[i].pagamento.por; + } + return "R$ " + t.toFixed(2).replace(".", ","); + }; + + $scope.remove = function (item) { + Carrinho.remove(item); + $scope.ListaCompras = Carrinho.get(); + $scope.$emit('ListaCompras'); + }; + }); +angular.module('profite') + .controller('homeCtrl', function ($http, $scope, $rootScope, OrdenarOpt, Model, $timeout, Util, config) { + var itensCollapseCor = 5; + $scope.ordernarList = OrdenarOpt; + $scope.ordenarTipo = null; + $scope.Cores = []; + $scope.Precos = []; + $scope.Tamanhos = []; + $scope.Produtos = []; + $scope.produtosCopia = []; + $scope.corSelecionada = { id: 0 }; + $scope.precoSelecionado = { id: 0 }; + $scope.tamanhoSelecionado = { id: 0 }; + $scope.pagina = 1; + $scope.txtExpandCores = "Ver todas as cores"; + $scope.coresOpen = false; + $scope.mensagemErro = null; + + Model.Cores.get().then(function (res) { + $scope.Cores = res.data; + Util.collapse(itensCollapseCor); + }, function (err) { + alert(err); + }); + + Model.Produtos.get($scope.pagina,$scope).then(function (value) { + $scope.Produtos = value.data; + }, function (reason) { + console.log(reason); + }); + + Model.Preco.get().then(function (res) { + $scope.Precos = res.data; + }, function (reason) { + console.log(reason); + }); + Model.Tamanhos.get().then(function (res) { + $scope.Tamanhos = res.data; + }, function (err) { + console.error(err); + }); + + $scope.selecionaCor = function (cor) { + $scope.corSelecionada = cor; + Util.filtro($scope); + }; + $scope.selecionaPreco = function (preco) { + $scope.precoSelecionado = preco; + Util.filtro($scope); + }; + $scope.selecionaTamanho = function (tamanho) { + $scope.tamanhoSelecionado = tamanho; + Util.filtro($scope); + }; + $scope.replaceValor = function (valor) { + return valor.toFixed(2).replace(".", ","); + }; + $scope.addCarrinho = function (item) { + item.produto.addCart = true; + Model.Carrinho.set(item); + }; + + $scope.troggleColapse = function () { + Util.troggleColapse($scope, Util); + }; + + $scope.carregarMaisProdutos = function () { + Model.Produtos.get(2,$scope).then(function (value) { + $scope.Produtos = value.data; + }, function (reason) { + console.log(reason); + }); + }; + + $scope.ordenarChange = function (id) { + $scope.ordenarTipo = id; + Util.filtro($scope); + }; + + $scope.expandOptions = function ($event) { + Util.abreFiltroMobile($event); + }; + + $scope.fecharFiltro = function ($event) { + Util.fechaFiltroMobile($event); + }; + + $scope.troggleCategoriaFiltroMobile = function ($event) { + Util.troggleCategoriaFiltroMobile($event); + }; + + // $scope.carregarMaisProdutos(); + + $rootScope.$on('$routeChangeStart', function (event, next, current) { + Util.carregaCarrinho(next, Util); + }); + + $scope.showLimpaFiltros = function () { + return Util.showBtnLimpa($scope); + }; + + $scope.limpaFiltros = function () { + Util.limpaFiltros($scope, Util); + }; + }); +angular.module('profite') + .controller('mainCtrl', function ($scope, $routeParams, Carrinho) { + $scope.qtdCompra = 0; + $scope.$on('ListaCompras', function () { + $scope.qtdCompra = Carrinho.count(); + }); + }); +angular.module('profite') + .factory('Carrinho', function ($rootScope) { + var ListaCompra = []; + + function set(item) { + ListaCompra.push(item); + $rootScope.$broadcast('ListaCompras'); + } + + function get() { + return ListaCompra; + } + + function count() { + return ListaCompra.length; + } + + function remove(item) { + ListaCompra.splice(ListaCompra.indexOf(item), 1); + } + + return { + set: set, + get: get, + count: count, + remove: remove + } + }); +angular.module('profite') + .factory('Cores', function ($q, $http, config) { + + function get() { + return $http.get(config.apiUrl + "/cores"); + } + // + // function find(busca) { + // listaCores.map(function (item) { + // return item.id === busca.id; + // }); + // } + // + // getService(); + + return { + get: get + // find: find + } + }); +angular.module('profite') + .factory('Model', function (Cores, Tamanhos, Preco, Produtos, Carrinho) { + return { + Cores: Cores, + Tamanhos: Tamanhos, + Preco: Preco, + Produtos: Produtos, + Carrinho: Carrinho + } + }); +angular.module('profite') + .factory('Preco', function ($http, config) { + + function get() { + return $http.get(config.apiUrl + '/faixa-preco'); + } + + // function find(busca) { + // listaPreco.map(function (item) { + // return item.id === busca.id; + // }); + // } + + return { + get: get + // find: find + } + + }); +angular.module('profite') + .factory('Produtos', function($http, config){ + + function get(skip, scope) { + if(skip === 1) { + return $http.get(config.apiUrl + "/produtos"); + scope.pagina++; + } else if(skip === 2) { + return $http.get(config.apiUrl + "/mais-produtos"); + } + + } + + return { + get: get + } + }); +angular.module('profite') + .factory('Tamanhos', function ($q, $http, config) { + + function get() { + return $http.get(config.apiUrl + '/tamanhos'); + } + + // function find(busca) { + // listaTamanhos.map(function (item) { + // return item.id === busca.id; + // }); + // } + + return { + get: get + // find: find + } + + }); +angular.module('profite') + .factory('Util', function(Filtro, Collapse, MobileSupport, CTRLSupport){ + return { + msgErro: function msgErro(msg, scope, time) { + CTRLSupport.msgErro(msg, scope, time); + }, + + trataClasseBtn: function trataClasseBtn(ocasiao) { + Collapse.trataClasseBtn(ocasiao); + }, + + collapse: function collapse(itensCollapseCor) { + Collapse.collapse(itensCollapseCor); + }, + + troggleColapse: function troggleColapse(scope, Util) { + Collapse.troggleColapse(scope, Util); + }, + + // getProdutos: function getProdutos(skip, scope, Util, Model, time) { + // CTRLSupport.getProdutos(skip, scope, Util, Model, time); + // }, + + filtro: function filtro(scope, id) { + CTRLSupport.filtro(scope, id); + }, + + abreFiltroMobile: function abreFiltroMobile($event) { + MobileSupport.abreFiltroMobile($event); + }, + + fechaFiltroMobile: function fechaFiltroMobile($event) { + MobileSupport.fechaFiltroMobile($event); + }, + + troggleCategoriaFiltroMobile: function troggleCategoriaFiltroMobile($event) { + MobileSupport.troggleCategoriaFiltroMobile($event); + }, + + existsCompras: function existsCompras(model, scope) { + CTRLSupport.existCompras(model, scope); + }, + + carregaCarrinho: function carregaCarrinho(next, Util) { + CTRLSupport.carregaCarrinho(next, Util); + }, + + limpaFiltros: function limpaFiltros(scope, util) { + CTRLSupport.limpaFiltros(scope, util); + }, + + showBtnLimpa: function showBtnLimpa(scope) { + return CTRLSupport.showBtnLimpa(scope); + } + + }; + }); +angular.module('profite') + .factory('Collapse', function () { + return { + trataClasseBtn: function trataClasseBtn(ocasiao) { + var bt = document.querySelector("#btnCores"); + var seta = bt.querySelector('.arrow'); + var classeSeta = seta.className; + if (!ocasiao) { + classeSeta = classeSeta.split(" "); + classeSeta.splice(1, classeSeta.indexOf('open')); + seta.className = classeSeta.join(" "); + } else { + seta.className += " open"; + } + }, + + collapse: function collapse(itensCollapseCor) { + var boxcores = document.querySelector("#lista-cores"); + angular.element(boxcores).ready(function () { + var listaCores = boxcores.querySelectorAll("li"); + if (listaCores.length) { + for (var i = 0; i < listaCores.length; i++) { + if (i > itensCollapseCor && listaCores[i].className.indexOf("corextra") < 0) { + angular.element(listaCores[i]).addClass('corextra hide'); + } + } + } + }); + }, + + troggleColapse: function troggleColapse(scope, Util) { + var lista = document.querySelectorAll(".corextra"); + for (var i = 0; i < lista.length; i++) { + if (lista[i].className.indexOf("hide") < 0) { + angular.element(lista[i]).addClass('hide'); + scope.txtExpandCores = "Ver todas as cores"; + } else if (lista[i].className.indexOf("hide") > 0) { + var el = angular.element(lista[i]); + el.removeClass('hide'); + scope.txtExpandCores = "Ocultar as cores"; + } + } + scope.coresOpen = !scope.coresOpen; + Util.trataClasseBtn(scope.coresOpen); + } + + }; + }); +angular.module('profite') + .factory('CTRLSupport', function (Filtro) { + var encontraProdutoAdicionado = function encontraProdutoAdicionado(atual, model) { + for (var j in model.Carrinho.get()) { + var item = model.Carrinho.get()[j]; + if (atual.produto.id === item.produto.id) { + atual.produto.addCart = true; + } + } + }; + return { + msgErro: function msgErro(msg, scope, time) { + scope.mensagemErro = msg; + time(function () { + scope.mensagemErro = null; + }, 4000); + }, + + // getProdutos: function getProdutos(skip, scope, Util, Model, time) { + // Model.Produtos.get(skip).then( function (res) { + // for (var i in res) { + // res[i].produto.addCart = false; + // scope.Produtos.push(res[i]); + // scope.produtosCopia.push(res[i]); + // } + // scope.pagina++; + // Util.filtro(scope); + // Util.existsCompras(Model, scope); + // }, function (err) { + // Util.msgErro("Não há mais produtos.", scope, time); + // console.error(err); + // }); + // }, + + filtro: function filtro(scope, id) { + Filtro.filtroCor(scope); + Filtro.filtroTamanho(scope); + Filtro.filtroPreco(scope); + Filtro.semResultado(scope); + Filtro.ordenar(scope, id); + Filtro.semFiltro(scope); + }, + + existCompras: function existCompras(model, scope) { + if (model.Carrinho.count() > 0) { + for (var i in scope.Produtos) { + encontraProdutoAdicionado(scope.Produtos[i], model); + } + } + }, + + carregaCarrinho: function carregaCarrinho(next, Util) { + if (next.$$route.originalPath !== undefined) { + var path = next.$$route.originalPath.replace("/", ""); + if (angular.equals(path, "home")) { + Util.existsCompras(Model, $scope); + } + } + }, + + limpaFiltros: function limpaFiltros(scope, util) { + scope.corSelecionada = { id: 0 }; + scope.precoSelecionado = { id: 0 }; + scope.tamanhoSelecionado = { id: 0 }; + scope.ordenarTipo = null; + util.filtro(scope); + }, + + showBtnLimpa: function showBtnLimpa(scope) { + return scope.corSelecionada.id > 0 || scope.precoSelecionado.id > 0 || scope.tamanhoSelecionado.id > 0 || scope.ordenarTipo > 0; + } + }; + }); +angular.module('profite') + .factory('Filtro', function ($interval) { + return { + filtroCor: function filtroCor(scope) { + if (scope.corSelecionada.id > 0) { + var temp = scope.precoSelecionado.id > 0 || scope.tamanhoSelecionado.id > 0 ? angular.copy(scope.Produtos) : angular.copy(scope.produtosCopia); + scope.Produtos = []; + for (var i in temp) { + for (var j in temp[i].caracteristicas.cores) { + if (temp[i].caracteristicas.cores[j] === scope.corSelecionada.id) { + scope.Produtos.push(temp[i]); + break; + } + } + } + } + }, + + filtroTamanho: function filtroTamanho(scope) { + if (scope.tamanhoSelecionado.id > 0) { + var temp = scope.precoSelecionado.id > 0 || scope.corSelecionada.id > 0 ? angular.copy(scope.Produtos) : angular.copy(scope.produtosCopia); + scope.Produtos = []; + for (var i in temp) { + for (var j in temp[i].caracteristicas.tamanho) { + if (temp[i].caracteristicas.tamanho[j] === scope.tamanhoSelecionado.id) { + scope.Produtos.push(temp[i]); + break; + } + } + } + } + }, + + filtroPreco: function filtroPreco(scope) { + if (scope.precoSelecionado.id > 0) { + var temp = scope.tamanhoSelecionado.id > 0 || scope.corSelecionada.id > 0 ? angular.copy(scope.Produtos) : angular.copy(scope.produtosCopia); + scope.Produtos = []; + for (var i in temp) { + if (scope.precoSelecionado.to !== null) { + + if (temp[i].pagamento.por >= scope.precoSelecionado.from && temp[i].pagamento.por <= scope.precoSelecionado.to) { + scope.Produtos.push(temp[i]); + } + } else if (scope.precoSelecionado.to === null) { + + if (temp[i].pagamento.por >= scope.precoSelecionado.from) { + scope.Produtos.push(temp[i]); + } + } + } + } + }, + + semFiltro: function semFiltro(scope) { + if (scope.precoSelecionado.id < 1 && scope.tamanhoSelecionado.id < 1 && scope.corSelecionada.id < 1) { + scope.Produtos = angular.copy(scope.produtosCopia); + } + }, + + ordenar: function ordenar(scopectrl) { + if (scopectrl.ordenarTipo > 0) { + switch (scopectrl.ordenarTipo) { + case 1: + scopectrl.Produtos.sort(function (a, b) { + return new Date(a.produto.data) < new Date(b.produto.data); + }); + scopectrl.$digest(); + break; + case 2: + scopectrl.Produtos.sort(function (a, b) { + return a.pagamento.por > b.pagamento.por; + }); + scopectrl.$digest(); + break; + case 3: + scopectrl.Produtos.sort(function (a, b) { + return a.pagamento.por < b.pagamento.por; + }); + scopectrl.$digest(); + break; + } + } + }, + + semResultado: function semResultado(scope) { + if (scope.Produtos !== null && !scope.Produtos.length) { + scope.Produtos = null; + } + } + + }; + }); +angular.module('profite') + .factory('MobileSupport', function () { + return { + + abreFiltroMobile: function abreFiltroMobile($event) { + var box = angular.element($event.target.nextElementSibling); + box.addClass('open-filter'); + }, + + fechaFiltroMobile: function fechaFiltroMobile($event) { + var box = angular.element($event.target.parentElement); + box.removeClass('open-filter'); + }, + + troggleCategoriaFiltroMobile: function troggleCategoriaFiltroMobile($event) { + var lista = angular.element($event.target.nextElementSibling); + var span = angular.element($event.target.children); + if (lista.hasClass('abre-lista')) { + lista.removeClass('abre-lista'); + span.text("+"); + } else { + lista.addClass('abre-lista'); + span.text("-"); + } + } + + }; + }); +angular.module('profite') + .value('config', { + apiUrl: "https://private-4586c-profite.apiary-mock.com" + }); \ No newline at end of file From 59dce1a46237647e6183ac030f27c8c25aa8bb2d Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Sun, 18 Mar 2018 02:17:08 -0300 Subject: [PATCH 20/22] =?UTF-8?q?cria=C3=A7=C3=A3o=20de=20tarefas=20para?= =?UTF-8?q?=20build=20angular=20e=20normalize=20e=20inclus=C3=A3o=20no=20i?= =?UTF-8?q?ndex.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/css/normalize.css | 341 + dist/js/lib/angular-route.js | 1224 ++ dist/js/lib/angular.js | 34359 +++++++++++++++++++++++++++++++++ gulpfile.js | 17 +- index.html | 6 +- 5 files changed, 35943 insertions(+), 4 deletions(-) create mode 100644 dist/css/normalize.css create mode 100644 dist/js/lib/angular-route.js create mode 100644 dist/js/lib/angular.js diff --git a/dist/css/normalize.css b/dist/css/normalize.css new file mode 100644 index 00000000..47b010e4 --- /dev/null +++ b/dist/css/normalize.css @@ -0,0 +1,341 @@ +/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/dist/js/lib/angular-route.js b/dist/js/lib/angular-route.js new file mode 100644 index 00000000..bb63ba10 --- /dev/null +++ b/dist/js/lib/angular-route.js @@ -0,0 +1,1224 @@ +/** + * @license AngularJS v1.6.9 + * (c) 2010-2018 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular) {'use strict'; + +/* global shallowCopy: true */ + +/** + * Creates a shallow copy of an object, an array or a primitive. + * + * Assumes that there are no proto properties for objects. + */ +function shallowCopy(src, dst) { + if (isArray(src)) { + dst = dst || []; + + for (var i = 0, ii = src.length; i < ii; i++) { + dst[i] = src[i]; + } + } else if (isObject(src)) { + dst = dst || {}; + + for (var key in src) { + if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { + dst[key] = src[key]; + } + } + } + + return dst || src; +} + +/* global shallowCopy: false */ + +// `isArray` and `isObject` are necessary for `shallowCopy()` (included via `src/shallowCopy.js`). +// They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available. +var isArray; +var isObject; +var isDefined; +var noop; + +/** + * @ngdoc module + * @name ngRoute + * @description + * + * The `ngRoute` module provides routing and deeplinking services and directives for AngularJS apps. + * + * ## Example + * See {@link ngRoute.$route#examples $route} for an example of configuring and using `ngRoute`. + * + */ +/* global -ngRouteModule */ +var ngRouteModule = angular. + module('ngRoute', []). + info({ angularVersion: '1.6.9' }). + provider('$route', $RouteProvider). + // Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess` + // event (unless explicitly disabled). This is necessary in case `ngView` is included in an + // asynchronously loaded template. + run(instantiateRoute); +var $routeMinErr = angular.$$minErr('ngRoute'); +var isEagerInstantiationEnabled; + + +/** + * @ngdoc provider + * @name $routeProvider + * @this + * + * @description + * + * Used for configuring routes. + * + * ## Example + * See {@link ngRoute.$route#examples $route} for an example of configuring and using `ngRoute`. + * + * ## Dependencies + * Requires the {@link ngRoute `ngRoute`} module to be installed. + */ +function $RouteProvider() { + isArray = angular.isArray; + isObject = angular.isObject; + isDefined = angular.isDefined; + noop = angular.noop; + + function inherit(parent, extra) { + return angular.extend(Object.create(parent), extra); + } + + var routes = {}; + + /** + * @ngdoc method + * @name $routeProvider#when + * + * @param {string} path Route path (matched against `$location.path`). If `$location.path` + * contains redundant trailing slash or is missing one, the route will still match and the + * `$location.path` will be updated to add or drop the trailing slash to exactly match the + * route definition. + * + * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up + * to the next slash are matched and stored in `$routeParams` under the given `name` + * when the route matches. + * * `path` can contain named groups starting with a colon and ending with a star: + * e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name` + * when the route matches. + * * `path` can contain optional named groups with a question mark: e.g.`:name?`. + * + * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match + * `/color/brown/largecode/code/with/slashes/edit` and extract: + * + * * `color: brown` + * * `largecode: code/with/slashes`. + * + * + * @param {Object} route Mapping information to be assigned to `$route.current` on route + * match. + * + * Object properties: + * + * - `controller` – `{(string|Function)=}` – Controller fn that should be associated with + * newly created scope or the name of a {@link angular.Module#controller registered + * controller} if passed as a string. + * - `controllerAs` – `{string=}` – An identifier name for a reference to the controller. + * If present, the controller will be published to scope under the `controllerAs` name. + * - `template` – `{(string|Function)=}` – html template as a string or a function that + * returns an html template as a string which should be used by {@link + * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives. + * This property takes precedence over `templateUrl`. + * + * If `template` is a function, it will be called with the following parameters: + * + * - `{Array.}` - route parameters extracted from the current + * `$location.path()` by applying the current route + * + * One of `template` or `templateUrl` is required. + * + * - `templateUrl` – `{(string|Function)=}` – path or function that returns a path to an html + * template that should be used by {@link ngRoute.directive:ngView ngView}. + * + * If `templateUrl` is a function, it will be called with the following parameters: + * + * - `{Array.}` - route parameters extracted from the current + * `$location.path()` by applying the current route + * + * One of `templateUrl` or `template` is required. + * + * - `resolve` - `{Object.=}` - An optional map of dependencies which should + * be injected into the controller. If any of these dependencies are promises, the router + * will wait for them all to be resolved or one to be rejected before the controller is + * instantiated. + * If all the promises are resolved successfully, the values of the resolved promises are + * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is + * fired. If any of the promises are rejected the + * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. + * For easier access to the resolved dependencies from the template, the `resolve` map will + * be available on the scope of the route, under `$resolve` (by default) or a custom name + * specified by the `resolveAs` property (see below). This can be particularly useful, when + * working with {@link angular.Module#component components} as route templates.
    + *
    + * **Note:** If your scope already contains a property with this name, it will be hidden + * or overwritten. Make sure, you specify an appropriate name for this property, that + * does not collide with other properties on the scope. + *
    + * The map object is: + * + * - `key` – `{string}`: a name of a dependency to be injected into the controller. + * - `factory` - `{string|Function}`: If `string` then it is an alias for a service. + * Otherwise if function, then it is {@link auto.$injector#invoke injected} + * and the return value is treated as the dependency. If the result is a promise, it is + * resolved before its value is injected into the controller. Be aware that + * `ngRoute.$routeParams` will still refer to the previous route within these resolve + * functions. Use `$route.current.params` to access the new route parameters, instead. + * + * - `resolveAs` - `{string=}` - The name under which the `resolve` map will be available on + * the scope of the route. If omitted, defaults to `$resolve`. + * + * - `redirectTo` – `{(string|Function)=}` – value to update + * {@link ng.$location $location} path with and trigger route redirection. + * + * If `redirectTo` is a function, it will be called with the following parameters: + * + * - `{Object.}` - route parameters extracted from the current + * `$location.path()` by applying the current route templateUrl. + * - `{string}` - current `$location.path()` + * - `{Object}` - current `$location.search()` + * + * The custom `redirectTo` function is expected to return a string which will be used + * to update `$location.url()`. If the function throws an error, no further processing will + * take place and the {@link ngRoute.$route#$routeChangeError $routeChangeError} event will + * be fired. + * + * Routes that specify `redirectTo` will not have their controllers, template functions + * or resolves called, the `$location` will be changed to the redirect url and route + * processing will stop. The exception to this is if the `redirectTo` is a function that + * returns `undefined`. In this case the route transition occurs as though there was no + * redirection. + * + * - `resolveRedirectTo` – `{Function=}` – a function that will (eventually) return the value + * to update {@link ng.$location $location} URL with and trigger route redirection. In + * contrast to `redirectTo`, dependencies can be injected into `resolveRedirectTo` and the + * return value can be either a string or a promise that will be resolved to a string. + * + * Similar to `redirectTo`, if the return value is `undefined` (or a promise that gets + * resolved to `undefined`), no redirection takes place and the route transition occurs as + * though there was no redirection. + * + * If the function throws an error or the returned promise gets rejected, no further + * processing will take place and the + * {@link ngRoute.$route#$routeChangeError $routeChangeError} event will be fired. + * + * `redirectTo` takes precedence over `resolveRedirectTo`, so specifying both on the same + * route definition, will cause the latter to be ignored. + * + * - `[reloadOnSearch=true]` - `{boolean=}` - reload route when only `$location.search()` + * or `$location.hash()` changes. + * + * If the option is set to `false` and url in the browser changes, then + * `$routeUpdate` event is broadcasted on the root scope. + * + * - `[caseInsensitiveMatch=false]` - `{boolean=}` - match routes without being case sensitive + * + * If the option is set to `true`, then the particular route can be matched without being + * case sensitive + * + * @returns {Object} self + * + * @description + * Adds a new route definition to the `$route` service. + */ + this.when = function(path, route) { + //copy original route object to preserve params inherited from proto chain + var routeCopy = shallowCopy(route); + if (angular.isUndefined(routeCopy.reloadOnSearch)) { + routeCopy.reloadOnSearch = true; + } + if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) { + routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch; + } + routes[path] = angular.extend( + routeCopy, + path && pathRegExp(path, routeCopy) + ); + + // create redirection for trailing slashes + if (path) { + var redirectPath = (path[path.length - 1] === '/') + ? path.substr(0, path.length - 1) + : path + '/'; + + routes[redirectPath] = angular.extend( + {redirectTo: path}, + pathRegExp(redirectPath, routeCopy) + ); + } + + return this; + }; + + /** + * @ngdoc property + * @name $routeProvider#caseInsensitiveMatch + * @description + * + * A boolean property indicating if routes defined + * using this provider should be matched using a case insensitive + * algorithm. Defaults to `false`. + */ + this.caseInsensitiveMatch = false; + + /** + * @param path {string} path + * @param opts {Object} options + * @return {?Object} + * + * @description + * Normalizes the given path, returning a regular expression + * and the original path. + * + * Inspired by pathRexp in visionmedia/express/lib/utils.js. + */ + function pathRegExp(path, opts) { + var insensitive = opts.caseInsensitiveMatch, + ret = { + originalPath: path, + regexp: path + }, + keys = ret.keys = []; + + path = path + .replace(/([().])/g, '\\$1') + .replace(/(\/)?:(\w+)(\*\?|[?*])?/g, function(_, slash, key, option) { + var optional = (option === '?' || option === '*?') ? '?' : null; + var star = (option === '*' || option === '*?') ? '*' : null; + keys.push({ name: key, optional: !!optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (star && '(.+?)' || '([^/]+)') + + (optional || '') + + ')' + + (optional || ''); + }) + .replace(/([/$*])/g, '\\$1'); + + ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : ''); + return ret; + } + + /** + * @ngdoc method + * @name $routeProvider#otherwise + * + * @description + * Sets route definition that will be used on route change when no other route definition + * is matched. + * + * @param {Object|string} params Mapping information to be assigned to `$route.current`. + * If called with a string, the value maps to `redirectTo`. + * @returns {Object} self + */ + this.otherwise = function(params) { + if (typeof params === 'string') { + params = {redirectTo: params}; + } + this.when(null, params); + return this; + }; + + /** + * @ngdoc method + * @name $routeProvider#eagerInstantiationEnabled + * @kind function + * + * @description + * Call this method as a setter to enable/disable eager instantiation of the + * {@link ngRoute.$route $route} service upon application bootstrap. You can also call it as a + * getter (i.e. without any arguments) to get the current value of the + * `eagerInstantiationEnabled` flag. + * + * Instantiating `$route` early is necessary for capturing the initial + * {@link ng.$location#$locationChangeStart $locationChangeStart} event and navigating to the + * appropriate route. Usually, `$route` is instantiated in time by the + * {@link ngRoute.ngView ngView} directive. Yet, in cases where `ngView` is included in an + * asynchronously loaded template (e.g. in another directive's template), the directive factory + * might not be called soon enough for `$route` to be instantiated _before_ the initial + * `$locationChangeSuccess` event is fired. Eager instantiation ensures that `$route` is always + * instantiated in time, regardless of when `ngView` will be loaded. + * + * The default value is true. + * + * **Note**:
    + * You may want to disable the default behavior when unit-testing modules that depend on + * `ngRoute`, in order to avoid an unexpected request for the default route's template. + * + * @param {boolean=} enabled - If provided, update the internal `eagerInstantiationEnabled` flag. + * + * @returns {*} The current value of the `eagerInstantiationEnabled` flag if used as a getter or + * itself (for chaining) if used as a setter. + */ + isEagerInstantiationEnabled = true; + this.eagerInstantiationEnabled = function eagerInstantiationEnabled(enabled) { + if (isDefined(enabled)) { + isEagerInstantiationEnabled = enabled; + return this; + } + + return isEagerInstantiationEnabled; + }; + + + this.$get = ['$rootScope', + '$location', + '$routeParams', + '$q', + '$injector', + '$templateRequest', + '$sce', + '$browser', + function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce, $browser) { + + /** + * @ngdoc service + * @name $route + * @requires $location + * @requires $routeParams + * + * @property {Object} current Reference to the current route definition. + * The route definition contains: + * + * - `controller`: The controller constructor as defined in the route definition. + * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for + * controller instantiation. The `locals` contain + * the resolved values of the `resolve` map. Additionally the `locals` also contain: + * + * - `$scope` - The current route scope. + * - `$template` - The current route template HTML. + * + * The `locals` will be assigned to the route scope's `$resolve` property. You can override + * the property name, using `resolveAs` in the route definition. See + * {@link ngRoute.$routeProvider $routeProvider} for more info. + * + * @property {Object} routes Object with all route configuration Objects as its properties. + * + * @description + * `$route` is used for deep-linking URLs to controllers and views (HTML partials). + * It watches `$location.url()` and tries to map the path to an existing route definition. + * + * Requires the {@link ngRoute `ngRoute`} module to be installed. + * + * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API. + * + * The `$route` service is typically used in conjunction with the + * {@link ngRoute.directive:ngView `ngView`} directive and the + * {@link ngRoute.$routeParams `$routeParams`} service. + * + * @example + * This example shows how changing the URL hash causes the `$route` to match a route against the + * URL, and the `ngView` pulls in the partial. + * + * + * + *
    + * Choose: + * Moby | + * Moby: Ch1 | + * Gatsby | + * Gatsby: Ch4 | + * Scarlet Letter
    + * + *
    + * + *
    + * + *
    $location.path() = {{$location.path()}}
    + *
    $route.current.templateUrl = {{$route.current.templateUrl}}
    + *
    $route.current.params = {{$route.current.params}}
    + *
    $route.current.scope.name = {{$route.current.scope.name}}
    + *
    $routeParams = {{$routeParams}}
    + *
    + *
    + * + * + * controller: {{name}}
    + * Book Id: {{params.bookId}}
    + *
    + * + * + * controller: {{name}}
    + * Book Id: {{params.bookId}}
    + * Chapter Id: {{params.chapterId}} + *
    + * + * + * angular.module('ngRouteExample', ['ngRoute']) + * + * .controller('MainController', function($scope, $route, $routeParams, $location) { + * $scope.$route = $route; + * $scope.$location = $location; + * $scope.$routeParams = $routeParams; + * }) + * + * .controller('BookController', function($scope, $routeParams) { + * $scope.name = 'BookController'; + * $scope.params = $routeParams; + * }) + * + * .controller('ChapterController', function($scope, $routeParams) { + * $scope.name = 'ChapterController'; + * $scope.params = $routeParams; + * }) + * + * .config(function($routeProvider, $locationProvider) { + * $routeProvider + * .when('/Book/:bookId', { + * templateUrl: 'book.html', + * controller: 'BookController', + * resolve: { + * // I will cause a 1 second delay + * delay: function($q, $timeout) { + * var delay = $q.defer(); + * $timeout(delay.resolve, 1000); + * return delay.promise; + * } + * } + * }) + * .when('/Book/:bookId/ch/:chapterId', { + * templateUrl: 'chapter.html', + * controller: 'ChapterController' + * }); + * + * // configure html5 to get links working on jsfiddle + * $locationProvider.html5Mode(true); + * }); + * + * + * + * + * it('should load and compile correct template', function() { + * element(by.linkText('Moby: Ch1')).click(); + * var content = element(by.css('[ng-view]')).getText(); + * expect(content).toMatch(/controller: ChapterController/); + * expect(content).toMatch(/Book Id: Moby/); + * expect(content).toMatch(/Chapter Id: 1/); + * + * element(by.partialLinkText('Scarlet')).click(); + * + * content = element(by.css('[ng-view]')).getText(); + * expect(content).toMatch(/controller: BookController/); + * expect(content).toMatch(/Book Id: Scarlet/); + * }); + * + *
    + */ + + /** + * @ngdoc event + * @name $route#$routeChangeStart + * @eventType broadcast on root scope + * @description + * Broadcasted before a route change. At this point the route services starts + * resolving all of the dependencies needed for the route change to occur. + * Typically this involves fetching the view template as well as any dependencies + * defined in `resolve` route property. Once all of the dependencies are resolved + * `$routeChangeSuccess` is fired. + * + * The route change (and the `$location` change that triggered it) can be prevented + * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} + * for more details about event object. + * + * @param {Object} angularEvent Synthetic event object. + * @param {Route} next Future route information. + * @param {Route} current Current route information. + */ + + /** + * @ngdoc event + * @name $route#$routeChangeSuccess + * @eventType broadcast on root scope + * @description + * Broadcasted after a route change has happened successfully. + * The `resolve` dependencies are now available in the `current.locals` property. + * + * {@link ngRoute.directive:ngView ngView} listens for the directive + * to instantiate the controller and render the view. + * + * @param {Object} angularEvent Synthetic event object. + * @param {Route} current Current route information. + * @param {Route|Undefined} previous Previous route information, or undefined if current is + * first route entered. + */ + + /** + * @ngdoc event + * @name $route#$routeChangeError + * @eventType broadcast on root scope + * @description + * Broadcasted if a redirection function fails or any redirection or resolve promises are + * rejected. + * + * @param {Object} angularEvent Synthetic event object + * @param {Route} current Current route information. + * @param {Route} previous Previous route information. + * @param {Route} rejection The thrown error or the rejection reason of the promise. Usually + * the rejection reason is the error that caused the promise to get rejected. + */ + + /** + * @ngdoc event + * @name $route#$routeUpdate + * @eventType broadcast on root scope + * @description + * The `reloadOnSearch` property has been set to false, and we are reusing the same + * instance of the Controller. + * + * @param {Object} angularEvent Synthetic event object + * @param {Route} current Current/previous route information. + */ + + var forceReload = false, + preparedRoute, + preparedRouteIsUpdateOnly, + $route = { + routes: routes, + + /** + * @ngdoc method + * @name $route#reload + * + * @description + * Causes `$route` service to reload the current route even if + * {@link ng.$location $location} hasn't changed. + * + * As a result of that, {@link ngRoute.directive:ngView ngView} + * creates new scope and reinstantiates the controller. + */ + reload: function() { + forceReload = true; + + var fakeLocationEvent = { + defaultPrevented: false, + preventDefault: function fakePreventDefault() { + this.defaultPrevented = true; + forceReload = false; + } + }; + + $rootScope.$evalAsync(function() { + prepareRoute(fakeLocationEvent); + if (!fakeLocationEvent.defaultPrevented) commitRoute(); + }); + }, + + /** + * @ngdoc method + * @name $route#updateParams + * + * @description + * Causes `$route` service to update the current URL, replacing + * current route parameters with those specified in `newParams`. + * Provided property names that match the route's path segment + * definitions will be interpolated into the location's path, while + * remaining properties will be treated as query params. + * + * @param {!Object} newParams mapping of URL parameter names to values + */ + updateParams: function(newParams) { + if (this.current && this.current.$$route) { + newParams = angular.extend({}, this.current.params, newParams); + $location.path(interpolate(this.current.$$route.originalPath, newParams)); + // interpolate modifies newParams, only query params are left + $location.search(newParams); + } else { + throw $routeMinErr('norout', 'Tried updating route when with no current route'); + } + } + }; + + $rootScope.$on('$locationChangeStart', prepareRoute); + $rootScope.$on('$locationChangeSuccess', commitRoute); + + return $route; + + ///////////////////////////////////////////////////// + + /** + * @param on {string} current url + * @param route {Object} route regexp to match the url against + * @return {?Object} + * + * @description + * Check if the route matches the current url. + * + * Inspired by match in + * visionmedia/express/lib/router/router.js. + */ + function switchRouteMatcher(on, route) { + var keys = route.keys, + params = {}; + + if (!route.regexp) return null; + + var m = route.regexp.exec(on); + if (!m) return null; + + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + + var val = m[i]; + + if (key && val) { + params[key.name] = val; + } + } + return params; + } + + function prepareRoute($locationEvent) { + var lastRoute = $route.current; + + preparedRoute = parseRoute(); + preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route + && angular.equals(preparedRoute.pathParams, lastRoute.pathParams) + && !preparedRoute.reloadOnSearch && !forceReload; + + if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) { + if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) { + if ($locationEvent) { + $locationEvent.preventDefault(); + } + } + } + } + + function commitRoute() { + var lastRoute = $route.current; + var nextRoute = preparedRoute; + + if (preparedRouteIsUpdateOnly) { + lastRoute.params = nextRoute.params; + angular.copy(lastRoute.params, $routeParams); + $rootScope.$broadcast('$routeUpdate', lastRoute); + } else if (nextRoute || lastRoute) { + forceReload = false; + $route.current = nextRoute; + + var nextRoutePromise = $q.resolve(nextRoute); + + $browser.$$incOutstandingRequestCount(); + + nextRoutePromise. + then(getRedirectionData). + then(handlePossibleRedirection). + then(function(keepProcessingRoute) { + return keepProcessingRoute && nextRoutePromise. + then(resolveLocals). + then(function(locals) { + // after route change + if (nextRoute === $route.current) { + if (nextRoute) { + nextRoute.locals = locals; + angular.copy(nextRoute.params, $routeParams); + } + $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute); + } + }); + }).catch(function(error) { + if (nextRoute === $route.current) { + $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error); + } + }).finally(function() { + // Because `commitRoute()` is called from a `$rootScope.$evalAsync` block (see + // `$locationWatch`), this `$$completeOutstandingRequest()` call will not cause + // `outstandingRequestCount` to hit zero. This is important in case we are redirecting + // to a new route which also requires some asynchronous work. + + $browser.$$completeOutstandingRequest(noop); + }); + } + } + + function getRedirectionData(route) { + var data = { + route: route, + hasRedirection: false + }; + + if (route) { + if (route.redirectTo) { + if (angular.isString(route.redirectTo)) { + data.path = interpolate(route.redirectTo, route.params); + data.search = route.params; + data.hasRedirection = true; + } else { + var oldPath = $location.path(); + var oldSearch = $location.search(); + var newUrl = route.redirectTo(route.pathParams, oldPath, oldSearch); + + if (angular.isDefined(newUrl)) { + data.url = newUrl; + data.hasRedirection = true; + } + } + } else if (route.resolveRedirectTo) { + return $q. + resolve($injector.invoke(route.resolveRedirectTo)). + then(function(newUrl) { + if (angular.isDefined(newUrl)) { + data.url = newUrl; + data.hasRedirection = true; + } + + return data; + }); + } + } + + return data; + } + + function handlePossibleRedirection(data) { + var keepProcessingRoute = true; + + if (data.route !== $route.current) { + keepProcessingRoute = false; + } else if (data.hasRedirection) { + var oldUrl = $location.url(); + var newUrl = data.url; + + if (newUrl) { + $location. + url(newUrl). + replace(); + } else { + newUrl = $location. + path(data.path). + search(data.search). + replace(). + url(); + } + + if (newUrl !== oldUrl) { + // Exit out and don't process current next value, + // wait for next location change from redirect + keepProcessingRoute = false; + } + } + + return keepProcessingRoute; + } + + function resolveLocals(route) { + if (route) { + var locals = angular.extend({}, route.resolve); + angular.forEach(locals, function(value, key) { + locals[key] = angular.isString(value) ? + $injector.get(value) : + $injector.invoke(value, null, null, key); + }); + var template = getTemplateFor(route); + if (angular.isDefined(template)) { + locals['$template'] = template; + } + return $q.all(locals); + } + } + + function getTemplateFor(route) { + var template, templateUrl; + if (angular.isDefined(template = route.template)) { + if (angular.isFunction(template)) { + template = template(route.params); + } + } else if (angular.isDefined(templateUrl = route.templateUrl)) { + if (angular.isFunction(templateUrl)) { + templateUrl = templateUrl(route.params); + } + if (angular.isDefined(templateUrl)) { + route.loadedTemplateUrl = $sce.valueOf(templateUrl); + template = $templateRequest(templateUrl); + } + } + return template; + } + + /** + * @returns {Object} the current active route, by matching it against the URL + */ + function parseRoute() { + // Match a route + var params, match; + angular.forEach(routes, function(route, path) { + if (!match && (params = switchRouteMatcher($location.path(), route))) { + match = inherit(route, { + params: angular.extend({}, $location.search(), params), + pathParams: params}); + match.$$route = route; + } + }); + // No route matched; fallback to "otherwise" route + return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); + } + + /** + * @returns {string} interpolation of the redirect path with the parameters + */ + function interpolate(string, params) { + var result = []; + angular.forEach((string || '').split(':'), function(segment, i) { + if (i === 0) { + result.push(segment); + } else { + var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/); + var key = segmentMatch[1]; + result.push(params[key]); + result.push(segmentMatch[2] || ''); + delete params[key]; + } + }); + return result.join(''); + } + }]; +} + +instantiateRoute.$inject = ['$injector']; +function instantiateRoute($injector) { + if (isEagerInstantiationEnabled) { + // Instantiate `$route` + $injector.get('$route'); + } +} + +ngRouteModule.provider('$routeParams', $RouteParamsProvider); + + +/** + * @ngdoc service + * @name $routeParams + * @requires $route + * @this + * + * @description + * The `$routeParams` service allows you to retrieve the current set of route parameters. + * + * Requires the {@link ngRoute `ngRoute`} module to be installed. + * + * The route parameters are a combination of {@link ng.$location `$location`}'s + * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}. + * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched. + * + * In case of parameter name collision, `path` params take precedence over `search` params. + * + * The service guarantees that the identity of the `$routeParams` object will remain unchanged + * (but its properties will likely change) even when a route change occurs. + * + * Note that the `$routeParams` are only updated *after* a route change completes successfully. + * This means that you cannot rely on `$routeParams` being correct in route resolve functions. + * Instead you can use `$route.current.params` to access the new route's parameters. + * + * @example + * ```js + * // Given: + * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby + * // Route: /Chapter/:chapterId/Section/:sectionId + * // + * // Then + * $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'} + * ``` + */ +function $RouteParamsProvider() { + this.$get = function() { return {}; }; +} + +ngRouteModule.directive('ngView', ngViewFactory); +ngRouteModule.directive('ngView', ngViewFillContentFactory); + + +/** + * @ngdoc directive + * @name ngView + * @restrict ECA + * + * @description + * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by + * including the rendered template of the current route into the main layout (`index.html`) file. + * Every time the current route changes, the included view changes with it according to the + * configuration of the `$route` service. + * + * Requires the {@link ngRoute `ngRoute`} module to be installed. + * + * @animations + * | Animation | Occurs | + * |----------------------------------|-------------------------------------| + * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM | + * | {@link ng.$animate#leave leave} | when the old element is removed from to the DOM | + * + * The enter and leave animation occur concurrently. + * + * @scope + * @priority 400 + * @param {string=} onload Expression to evaluate whenever the view updates. + * + * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll + * $anchorScroll} to scroll the viewport after the view is updated. + * + * - If the attribute is not set, disable scrolling. + * - If the attribute is set without value, enable scrolling. + * - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated + * as an expression yields a truthy value. + * @example + + +
    + Choose: + Moby | + Moby: Ch1 | + Gatsby | + Gatsby: Ch4 | + Scarlet Letter
    + +
    +
    +
    +
    + +
    $location.path() = {{main.$location.path()}}
    +
    $route.current.templateUrl = {{main.$route.current.templateUrl}}
    +
    $route.current.params = {{main.$route.current.params}}
    +
    $routeParams = {{main.$routeParams}}
    +
    +
    + + +
    + controller: {{book.name}}
    + Book Id: {{book.params.bookId}}
    +
    +
    + + +
    + controller: {{chapter.name}}
    + Book Id: {{chapter.params.bookId}}
    + Chapter Id: {{chapter.params.chapterId}} +
    +
    + + + .view-animate-container { + position:relative; + height:100px!important; + background:white; + border:1px solid black; + height:40px; + overflow:hidden; + } + + .view-animate { + padding:10px; + } + + .view-animate.ng-enter, .view-animate.ng-leave { + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s; + + display:block; + width:100%; + border-left:1px solid black; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + padding:10px; + } + + .view-animate.ng-enter { + left:100%; + } + .view-animate.ng-enter.ng-enter-active { + left:0; + } + .view-animate.ng-leave.ng-leave-active { + left:-100%; + } + + + + angular.module('ngViewExample', ['ngRoute', 'ngAnimate']) + .config(['$routeProvider', '$locationProvider', + function($routeProvider, $locationProvider) { + $routeProvider + .when('/Book/:bookId', { + templateUrl: 'book.html', + controller: 'BookCtrl', + controllerAs: 'book' + }) + .when('/Book/:bookId/ch/:chapterId', { + templateUrl: 'chapter.html', + controller: 'ChapterCtrl', + controllerAs: 'chapter' + }); + + $locationProvider.html5Mode(true); + }]) + .controller('MainCtrl', ['$route', '$routeParams', '$location', + function MainCtrl($route, $routeParams, $location) { + this.$route = $route; + this.$location = $location; + this.$routeParams = $routeParams; + }]) + .controller('BookCtrl', ['$routeParams', function BookCtrl($routeParams) { + this.name = 'BookCtrl'; + this.params = $routeParams; + }]) + .controller('ChapterCtrl', ['$routeParams', function ChapterCtrl($routeParams) { + this.name = 'ChapterCtrl'; + this.params = $routeParams; + }]); + + + + + it('should load and compile correct template', function() { + element(by.linkText('Moby: Ch1')).click(); + var content = element(by.css('[ng-view]')).getText(); + expect(content).toMatch(/controller: ChapterCtrl/); + expect(content).toMatch(/Book Id: Moby/); + expect(content).toMatch(/Chapter Id: 1/); + + element(by.partialLinkText('Scarlet')).click(); + + content = element(by.css('[ng-view]')).getText(); + expect(content).toMatch(/controller: BookCtrl/); + expect(content).toMatch(/Book Id: Scarlet/); + }); + +
    + */ + + +/** + * @ngdoc event + * @name ngView#$viewContentLoaded + * @eventType emit on the current ngView scope + * @description + * Emitted every time the ngView content is reloaded. + */ +ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; +function ngViewFactory($route, $anchorScroll, $animate) { + return { + restrict: 'ECA', + terminal: true, + priority: 400, + transclude: 'element', + link: function(scope, $element, attr, ctrl, $transclude) { + var currentScope, + currentElement, + previousLeaveAnimation, + autoScrollExp = attr.autoscroll, + onloadExp = attr.onload || ''; + + scope.$on('$routeChangeSuccess', update); + update(); + + function cleanupLastView() { + if (previousLeaveAnimation) { + $animate.cancel(previousLeaveAnimation); + previousLeaveAnimation = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + if (currentElement) { + previousLeaveAnimation = $animate.leave(currentElement); + previousLeaveAnimation.done(function(response) { + if (response !== false) previousLeaveAnimation = null; + }); + currentElement = null; + } + } + + function update() { + var locals = $route.current && $route.current.locals, + template = locals && locals.$template; + + if (angular.isDefined(template)) { + var newScope = scope.$new(); + var current = $route.current; + + // Note: This will also link all children of ng-view that were contained in the original + // html. If that content contains controllers, ... they could pollute/change the scope. + // However, using ng-view on an element with additional content does not make sense... + // Note: We can't remove them in the cloneAttchFn of $transclude as that + // function is called before linking the content, which would apply child + // directives to non existing elements. + var clone = $transclude(newScope, function(clone) { + $animate.enter(clone, null, currentElement || $element).done(function onNgViewEnter(response) { + if (response !== false && angular.isDefined(autoScrollExp) + && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + }); + cleanupLastView(); + }); + + currentElement = clone; + currentScope = current.scope = newScope; + currentScope.$emit('$viewContentLoaded'); + currentScope.$eval(onloadExp); + } else { + cleanupLastView(); + } + } + } + }; +} + +// This directive is called during the $transclude call of the first `ngView` directive. +// It will replace and compile the content of the element with the loaded template. +// We need this directive so that the element content is already filled when +// the link function of another directive on the same element as ngView +// is called. +ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route']; +function ngViewFillContentFactory($compile, $controller, $route) { + return { + restrict: 'ECA', + priority: -400, + link: function(scope, $element) { + var current = $route.current, + locals = current.locals; + + $element.html(locals.$template); + + var link = $compile($element.contents()); + + if (current.controller) { + locals.$scope = scope; + var controller = $controller(current.controller, locals); + if (current.controllerAs) { + scope[current.controllerAs] = controller; + } + $element.data('$ngControllerController', controller); + $element.children().data('$ngControllerController', controller); + } + scope[current.resolveAs || '$resolve'] = locals; + + link(scope); + } + }; +} + + +})(window, window.angular); diff --git a/dist/js/lib/angular.js b/dist/js/lib/angular.js new file mode 100644 index 00000000..b9d847e5 --- /dev/null +++ b/dist/js/lib/angular.js @@ -0,0 +1,34359 @@ +/** + * @license AngularJS v1.6.9 + * (c) 2010-2018 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window) {'use strict'; + +/* exported + minErrConfig, + errorHandlingConfig, + isValidObjectMaxDepth +*/ + +var minErrConfig = { + objectMaxDepth: 5 +}; + +/** + * @ngdoc function + * @name angular.errorHandlingConfig + * @module ng + * @kind function + * + * @description + * Configure several aspects of error handling in AngularJS if used as a setter or return the + * current configuration if used as a getter. The following options are supported: + * + * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages. + * + * Omitted or undefined options will leave the corresponding configuration values unchanged. + * + * @param {Object=} config - The configuration object. May only contain the options that need to be + * updated. Supported keys: + * + * * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a + * non-positive or non-numeric value, removes the max depth limit. + * Default: 5 + */ +function errorHandlingConfig(config) { + if (isObject(config)) { + if (isDefined(config.objectMaxDepth)) { + minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN; + } + } else { + return minErrConfig; + } +} + +/** + * @private + * @param {Number} maxDepth + * @return {boolean} + */ +function isValidObjectMaxDepth(maxDepth) { + return isNumber(maxDepth) && maxDepth > 0; +} + +/** + * @description + * + * This object provides a utility for producing rich Error messages within + * AngularJS. It can be called as follows: + * + * var exampleMinErr = minErr('example'); + * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); + * + * The above creates an instance of minErr in the example namespace. The + * resulting error will have a namespaced error code of example.one. The + * resulting error will replace {0} with the value of foo, and {1} with the + * value of bar. The object is not restricted in the number of arguments it can + * take. + * + * If fewer arguments are specified than necessary for interpolation, the extra + * interpolation markers will be preserved in the final string. + * + * Since data will be parsed statically during a build step, some restrictions + * are applied with respect to how minErr instances are created and called. + * Instances should have names of the form namespaceMinErr for a minErr created + * using minErr('namespace') . Error codes, namespaces and template strings + * should all be static strings, not variables or general expressions. + * + * @param {string} module The namespace to use for the new minErr instance. + * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning + * error from returned function, for cases when a particular type of error is useful. + * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance + */ + +function minErr(module, ErrorConstructor) { + ErrorConstructor = ErrorConstructor || Error; + return function() { + var code = arguments[0], + template = arguments[1], + message = '[' + (module ? module + ':' : '') + code + '] ', + templateArgs = sliceArgs(arguments, 2).map(function(arg) { + return toDebugString(arg, minErrConfig.objectMaxDepth); + }), + paramPrefix, i; + + message += template.replace(/\{\d+\}/g, function(match) { + var index = +match.slice(1, -1); + + if (index < templateArgs.length) { + return templateArgs[index]; + } + + return match; + }); + + message += '\nhttp://errors.angularjs.org/1.6.9/' + + (module ? module + '/' : '') + code; + + for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { + message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]); + } + + return new ErrorConstructor(message); + }; +} + +/* We need to tell ESLint what variables are being exported */ +/* exported + angular, + msie, + jqLite, + jQuery, + slice, + splice, + push, + toString, + minErrConfig, + errorHandlingConfig, + isValidObjectMaxDepth, + ngMinErr, + angularModule, + uid, + REGEX_STRING_REGEXP, + VALIDITY_STATE_PROPERTY, + + lowercase, + uppercase, + manualLowercase, + manualUppercase, + nodeName_, + isArrayLike, + forEach, + forEachSorted, + reverseParams, + nextUid, + setHashKey, + extend, + toInt, + inherit, + merge, + noop, + identity, + valueFn, + isUndefined, + isDefined, + isObject, + isBlankObject, + isString, + isNumber, + isNumberNaN, + isDate, + isError, + isArray, + isFunction, + isRegExp, + isWindow, + isScope, + isFile, + isFormData, + isBlob, + isBoolean, + isPromiseLike, + trim, + escapeForRegexp, + isElement, + makeMap, + includes, + arrayRemove, + copy, + simpleCompare, + equals, + csp, + jq, + concat, + sliceArgs, + bind, + toJsonReplacer, + toJson, + fromJson, + convertTimezoneToLocal, + timezoneToOffset, + startingTag, + tryDecodeURIComponent, + parseKeyValue, + toKeyValue, + encodeUriSegment, + encodeUriQuery, + angularInit, + bootstrap, + getTestability, + snake_case, + bindJQuery, + assertArg, + assertArgFn, + assertNotHasOwnProperty, + getter, + getBlockNodes, + hasOwnProperty, + createMap, + stringify, + + NODE_TYPE_ELEMENT, + NODE_TYPE_ATTRIBUTE, + NODE_TYPE_TEXT, + NODE_TYPE_COMMENT, + NODE_TYPE_DOCUMENT, + NODE_TYPE_DOCUMENT_FRAGMENT +*/ + +//////////////////////////////////// + +/** + * @ngdoc module + * @name ng + * @module ng + * @installation + * @description + * + * The ng module is loaded by default when an AngularJS application is started. The module itself + * contains the essential components for an AngularJS application to function. The table below + * lists a high level breakdown of each of the services/factories, filters, directives and testing + * components available within this core module. + * + */ + +var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; + +// The name of a form control's ValidityState property. +// This is used so that it's possible for internal tests to create mock ValidityStates. +var VALIDITY_STATE_PROPERTY = 'validity'; + + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * @ngdoc function + * @name angular.lowercase + * @module ng + * @kind function + * + * @deprecated + * sinceVersion="1.5.0" + * removeVersion="1.7.0" + * Use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) instead. + * + * @description Converts the specified string to lowercase. + * @param {string} string String to be converted to lowercase. + * @returns {string} Lowercased string. + */ +var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; + +/** + * @ngdoc function + * @name angular.uppercase + * @module ng + * @kind function + * + * @deprecated + * sinceVersion="1.5.0" + * removeVersion="1.7.0" + * Use [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) instead. + * + * @description Converts the specified string to uppercase. + * @param {string} string String to be converted to uppercase. + * @returns {string} Uppercased string. + */ +var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; + + +var manualLowercase = function(s) { + /* eslint-disable no-bitwise */ + return isString(s) + ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) + : s; + /* eslint-enable */ +}; +var manualUppercase = function(s) { + /* eslint-disable no-bitwise */ + return isString(s) + ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) + : s; + /* eslint-enable */ +}; + + +// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish +// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods +// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387 +if ('i' !== 'I'.toLowerCase()) { + lowercase = manualLowercase; + uppercase = manualUppercase; +} + + +var + msie, // holds major version number for IE, or NaN if UA is not IE. + jqLite, // delay binding since jQuery could be loaded after us. + jQuery, // delay binding + slice = [].slice, + splice = [].splice, + push = [].push, + toString = Object.prototype.toString, + getPrototypeOf = Object.getPrototypeOf, + ngMinErr = minErr('ng'), + + /** @name angular */ + angular = window.angular || (window.angular = {}), + angularModule, + uid = 0; + +// Support: IE 9-11 only +/** + * documentMode is an IE-only property + * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx + */ +msie = window.document.documentMode; + + +/** + * @private + * @param {*} obj + * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, + * String ...) + */ +function isArrayLike(obj) { + + // `null`, `undefined` and `window` are not array-like + if (obj == null || isWindow(obj)) return false; + + // arrays, strings and jQuery/jqLite objects are array like + // * jqLite is either the jQuery or jqLite constructor function + // * we have to check the existence of jqLite first as this method is called + // via the forEach method when constructing the jqLite object in the first place + if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true; + + // Support: iOS 8.2 (not reproducible in simulator) + // "length" in obj used to prevent JIT error (gh-11508) + var length = 'length' in Object(obj) && obj.length; + + // NodeList objects (with `item` method) and + // other objects with suitable length characteristics are array-like + return isNumber(length) && + (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function'); + +} + +/** + * @ngdoc function + * @name angular.forEach + * @module ng + * @kind function + * + * @description + * Invokes the `iterator` function once for each item in `obj` collection, which can be either an + * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` + * is the value of an object property or an array element, `key` is the object property key or + * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. + * + * It is worth noting that `.forEach` does not iterate over inherited properties because it filters + * using the `hasOwnProperty` method. + * + * Unlike ES262's + * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), + * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just + * return the value provided. + * + ```js + var values = {name: 'misko', gender: 'male'}; + var log = []; + angular.forEach(values, function(value, key) { + this.push(key + ': ' + value); + }, log); + expect(log).toEqual(['name: misko', 'gender: male']); + ``` + * + * @param {Object|Array} obj Object to iterate over. + * @param {Function} iterator Iterator function. + * @param {Object=} context Object to become context (`this`) for the iterator function. + * @returns {Object|Array} Reference to `obj`. + */ + +function forEach(obj, iterator, context) { + var key, length; + if (obj) { + if (isFunction(obj)) { + for (key in obj) { + if (key !== 'prototype' && key !== 'length' && key !== 'name' && obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key, obj); + } + } + } else if (isArray(obj) || isArrayLike(obj)) { + var isPrimitive = typeof obj !== 'object'; + for (key = 0, length = obj.length; key < length; key++) { + if (isPrimitive || key in obj) { + iterator.call(context, obj[key], key, obj); + } + } + } else if (obj.forEach && obj.forEach !== forEach) { + obj.forEach(iterator, context, obj); + } else if (isBlankObject(obj)) { + // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty + for (key in obj) { + iterator.call(context, obj[key], key, obj); + } + } else if (typeof obj.hasOwnProperty === 'function') { + // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key, obj); + } + } + } else { + // Slow path for objects which do not have a method `hasOwnProperty` + for (key in obj) { + if (hasOwnProperty.call(obj, key)) { + iterator.call(context, obj[key], key, obj); + } + } + } + } + return obj; +} + +function forEachSorted(obj, iterator, context) { + var keys = Object.keys(obj).sort(); + for (var i = 0; i < keys.length; i++) { + iterator.call(context, obj[keys[i]], keys[i]); + } + return keys; +} + + +/** + * when using forEach the params are value, key, but it is often useful to have key, value. + * @param {function(string, *)} iteratorFn + * @returns {function(*, string)} + */ +function reverseParams(iteratorFn) { + return function(value, key) {iteratorFn(key, value);}; +} + +/** + * A consistent way of creating unique IDs in angular. + * + * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before + * we hit number precision issues in JavaScript. + * + * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M + * + * @returns {number} an unique alpha-numeric string + */ +function nextUid() { + return ++uid; +} + + +/** + * Set or clear the hashkey for an object. + * @param obj object + * @param h the hashkey (!truthy to delete the hashkey) + */ +function setHashKey(obj, h) { + if (h) { + obj.$$hashKey = h; + } else { + delete obj.$$hashKey; + } +} + + +function baseExtend(dst, objs, deep) { + var h = dst.$$hashKey; + + for (var i = 0, ii = objs.length; i < ii; ++i) { + var obj = objs[i]; + if (!isObject(obj) && !isFunction(obj)) continue; + var keys = Object.keys(obj); + for (var j = 0, jj = keys.length; j < jj; j++) { + var key = keys[j]; + var src = obj[key]; + + if (deep && isObject(src)) { + if (isDate(src)) { + dst[key] = new Date(src.valueOf()); + } else if (isRegExp(src)) { + dst[key] = new RegExp(src); + } else if (src.nodeName) { + dst[key] = src.cloneNode(true); + } else if (isElement(src)) { + dst[key] = src.clone(); + } else { + if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; + baseExtend(dst[key], [src], true); + } + } else { + dst[key] = src; + } + } + } + + setHashKey(dst, h); + return dst; +} + +/** + * @ngdoc function + * @name angular.extend + * @module ng + * @kind function + * + * @description + * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) + * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so + * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. + * + * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use + * {@link angular.merge} for this. + * + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @returns {Object} Reference to `dst`. + */ +function extend(dst) { + return baseExtend(dst, slice.call(arguments, 1), false); +} + + +/** +* @ngdoc function +* @name angular.merge +* @module ng +* @kind function +* +* @description +* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s) +* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so +* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`. +* +* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source +* objects, performing a deep copy. +* +* @deprecated +* sinceVersion="1.6.5" +* This function is deprecated, but will not be removed in the 1.x lifecycle. +* There are edge cases (see {@link angular.merge#known-issues known issues}) that are not +* supported by this function. We suggest +* using [lodash's merge()](https://lodash.com/docs/4.17.4#merge) instead. +* +* @knownIssue +* This is a list of (known) object types that are not handled correctly by this function: +* - [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob) +* - [`MediaStream`](https://developer.mozilla.org/docs/Web/API/MediaStream) +* - [`CanvasGradient`](https://developer.mozilla.org/docs/Web/API/CanvasGradient) +* - AngularJS {@link $rootScope.Scope scopes}; +* +* @param {Object} dst Destination object. +* @param {...Object} src Source object(s). +* @returns {Object} Reference to `dst`. +*/ +function merge(dst) { + return baseExtend(dst, slice.call(arguments, 1), true); +} + + + +function toInt(str) { + return parseInt(str, 10); +} + +var isNumberNaN = Number.isNaN || function isNumberNaN(num) { + // eslint-disable-next-line no-self-compare + return num !== num; +}; + + +function inherit(parent, extra) { + return extend(Object.create(parent), extra); +} + +/** + * @ngdoc function + * @name angular.noop + * @module ng + * @kind function + * + * @description + * A function that performs no operations. This function can be useful when writing code in the + * functional style. + ```js + function foo(callback) { + var result = calculateResult(); + (callback || angular.noop)(result); + } + ``` + */ +function noop() {} +noop.$inject = []; + + +/** + * @ngdoc function + * @name angular.identity + * @module ng + * @kind function + * + * @description + * A function that returns its first argument. This function is useful when writing code in the + * functional style. + * + ```js + function transformer(transformationFn, value) { + return (transformationFn || angular.identity)(value); + }; + + // E.g. + function getResult(fn, input) { + return (fn || angular.identity)(input); + }; + + getResult(function(n) { return n * 2; }, 21); // returns 42 + getResult(null, 21); // returns 21 + getResult(undefined, 21); // returns 21 + ``` + * + * @param {*} value to be returned. + * @returns {*} the value passed in. + */ +function identity($) {return $;} +identity.$inject = []; + + +function valueFn(value) {return function valueRef() {return value;};} + +function hasCustomToString(obj) { + return isFunction(obj.toString) && obj.toString !== toString; +} + + +/** + * @ngdoc function + * @name angular.isUndefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is undefined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is undefined. + */ +function isUndefined(value) {return typeof value === 'undefined';} + + +/** + * @ngdoc function + * @name angular.isDefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is defined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is defined. + */ +function isDefined(value) {return typeof value !== 'undefined';} + + +/** + * @ngdoc function + * @name angular.isObject + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not + * considered to be objects. Note that JavaScript arrays are objects. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Object` but not `null`. + */ +function isObject(value) { + // http://jsperf.com/isobject4 + return value !== null && typeof value === 'object'; +} + + +/** + * Determine if a value is an object with a null prototype + * + * @returns {boolean} True if `value` is an `Object` with a null prototype + */ +function isBlankObject(value) { + return value !== null && typeof value === 'object' && !getPrototypeOf(value); +} + + +/** + * @ngdoc function + * @name angular.isString + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `String`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `String`. + */ +function isString(value) {return typeof value === 'string';} + + +/** + * @ngdoc function + * @name angular.isNumber + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Number`. + * + * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`. + * + * If you wish to exclude these then you can use the native + * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite) + * method. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Number`. + */ +function isNumber(value) {return typeof value === 'number';} + + +/** + * @ngdoc function + * @name angular.isDate + * @module ng + * @kind function + * + * @description + * Determines if a value is a date. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Date`. + */ +function isDate(value) { + return toString.call(value) === '[object Date]'; +} + + +/** + * @ngdoc function + * @name angular.isArray + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Array`. Alias of Array.isArray. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Array`. + */ +var isArray = Array.isArray; + +/** + * @description + * Determines if a reference is an `Error`. + * Loosely based on https://www.npmjs.com/package/iserror + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Error`. + */ +function isError(value) { + var tag = toString.call(value); + switch (tag) { + case '[object Error]': return true; + case '[object Exception]': return true; + case '[object DOMException]': return true; + default: return value instanceof Error; + } +} + +/** + * @ngdoc function + * @name angular.isFunction + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Function`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Function`. + */ +function isFunction(value) {return typeof value === 'function';} + + +/** + * Determines if a value is a regular expression object. + * + * @private + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `RegExp`. + */ +function isRegExp(value) { + return toString.call(value) === '[object RegExp]'; +} + + +/** + * Checks if `obj` is a window object. + * + * @private + * @param {*} obj Object to check + * @returns {boolean} True if `obj` is a window obj. + */ +function isWindow(obj) { + return obj && obj.window === obj; +} + + +function isScope(obj) { + return obj && obj.$evalAsync && obj.$watch; +} + + +function isFile(obj) { + return toString.call(obj) === '[object File]'; +} + + +function isFormData(obj) { + return toString.call(obj) === '[object FormData]'; +} + + +function isBlob(obj) { + return toString.call(obj) === '[object Blob]'; +} + + +function isBoolean(value) { + return typeof value === 'boolean'; +} + + +function isPromiseLike(obj) { + return obj && isFunction(obj.then); +} + + +var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/; +function isTypedArray(value) { + return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value)); +} + +function isArrayBuffer(obj) { + return toString.call(obj) === '[object ArrayBuffer]'; +} + + +var trim = function(value) { + return isString(value) ? value.trim() : value; +}; + +// Copied from: +// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 +// Prereq: s is a string. +var escapeForRegexp = function(s) { + return s + .replace(/([-()[\]{}+?*.$^|,:#= 0) { + array.splice(index, 1); + } + return index; +} + +/** + * @ngdoc function + * @name angular.copy + * @module ng + * @kind function + * + * @description + * Creates a deep copy of `source`, which should be an object or an array. + * + * * If no destination is supplied, a copy of the object or array is created. + * * If a destination is provided, all of its elements (for arrays) or properties (for objects) + * are deleted and then all elements/properties from the source are copied to it. + * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. + * * If `source` is identical to `destination` an exception will be thrown. + * + *
    + *
    + * Only enumerable properties are taken into account. Non-enumerable properties (both on `source` + * and on `destination`) will be ignored. + *
    + * + * @param {*} source The source that will be used to make a copy. + * Can be any type, including primitives, `null`, and `undefined`. + * @param {(Object|Array)=} destination Destination into which the source is copied. If + * provided, must be of the same type as `source`. + * @returns {*} The copy or updated `destination`, if `destination` was specified. + * + * @example + + +
    +
    +
    +
    + Gender: +
    + + +
    +
    form = {{user | json}}
    +
    leader = {{leader | json}}
    +
    +
    + + // Module: copyExample + angular. + module('copyExample', []). + controller('ExampleController', ['$scope', function($scope) { + $scope.leader = {}; + + $scope.reset = function() { + // Example with 1 argument + $scope.user = angular.copy($scope.leader); + }; + + $scope.update = function(user) { + // Example with 2 arguments + angular.copy(user, $scope.leader); + }; + + $scope.reset(); + }]); + +
    + */ +function copy(source, destination, maxDepth) { + var stackSource = []; + var stackDest = []; + maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN; + + if (destination) { + if (isTypedArray(destination) || isArrayBuffer(destination)) { + throw ngMinErr('cpta', 'Can\'t copy! TypedArray destination cannot be mutated.'); + } + if (source === destination) { + throw ngMinErr('cpi', 'Can\'t copy! Source and destination are identical.'); + } + + // Empty the destination object + if (isArray(destination)) { + destination.length = 0; + } else { + forEach(destination, function(value, key) { + if (key !== '$$hashKey') { + delete destination[key]; + } + }); + } + + stackSource.push(source); + stackDest.push(destination); + return copyRecurse(source, destination, maxDepth); + } + + return copyElement(source, maxDepth); + + function copyRecurse(source, destination, maxDepth) { + maxDepth--; + if (maxDepth < 0) { + return '...'; + } + var h = destination.$$hashKey; + var key; + if (isArray(source)) { + for (var i = 0, ii = source.length; i < ii; i++) { + destination.push(copyElement(source[i], maxDepth)); + } + } else if (isBlankObject(source)) { + // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty + for (key in source) { + destination[key] = copyElement(source[key], maxDepth); + } + } else if (source && typeof source.hasOwnProperty === 'function') { + // Slow path, which must rely on hasOwnProperty + for (key in source) { + if (source.hasOwnProperty(key)) { + destination[key] = copyElement(source[key], maxDepth); + } + } + } else { + // Slowest path --- hasOwnProperty can't be called as a method + for (key in source) { + if (hasOwnProperty.call(source, key)) { + destination[key] = copyElement(source[key], maxDepth); + } + } + } + setHashKey(destination, h); + return destination; + } + + function copyElement(source, maxDepth) { + // Simple values + if (!isObject(source)) { + return source; + } + + // Already copied values + var index = stackSource.indexOf(source); + if (index !== -1) { + return stackDest[index]; + } + + if (isWindow(source) || isScope(source)) { + throw ngMinErr('cpws', + 'Can\'t copy! Making copies of Window or Scope instances is not supported.'); + } + + var needsRecurse = false; + var destination = copyType(source); + + if (destination === undefined) { + destination = isArray(source) ? [] : Object.create(getPrototypeOf(source)); + needsRecurse = true; + } + + stackSource.push(source); + stackDest.push(destination); + + return needsRecurse + ? copyRecurse(source, destination, maxDepth) + : destination; + } + + function copyType(source) { + switch (toString.call(source)) { + case '[object Int8Array]': + case '[object Int16Array]': + case '[object Int32Array]': + case '[object Float32Array]': + case '[object Float64Array]': + case '[object Uint8Array]': + case '[object Uint8ClampedArray]': + case '[object Uint16Array]': + case '[object Uint32Array]': + return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length); + + case '[object ArrayBuffer]': + // Support: IE10 + if (!source.slice) { + // If we're in this case we know the environment supports ArrayBuffer + /* eslint-disable no-undef */ + var copied = new ArrayBuffer(source.byteLength); + new Uint8Array(copied).set(new Uint8Array(source)); + /* eslint-enable */ + return copied; + } + return source.slice(0); + + case '[object Boolean]': + case '[object Number]': + case '[object String]': + case '[object Date]': + return new source.constructor(source.valueOf()); + + case '[object RegExp]': + var re = new RegExp(source.source, source.toString().match(/[^/]*$/)[0]); + re.lastIndex = source.lastIndex; + return re; + + case '[object Blob]': + return new source.constructor([source], {type: source.type}); + } + + if (isFunction(source.cloneNode)) { + return source.cloneNode(true); + } + } +} + + +// eslint-disable-next-line no-self-compare +function simpleCompare(a, b) { return a === b || (a !== a && b !== b); } + + +/** + * @ngdoc function + * @name angular.equals + * @module ng + * @kind function + * + * @description + * Determines if two objects or two values are equivalent. Supports value types, regular + * expressions, arrays and objects. + * + * Two objects or values are considered equivalent if at least one of the following is true: + * + * * Both objects or values pass `===` comparison. + * * Both objects or values are of the same type and all of their properties are equal by + * comparing them with `angular.equals`. + * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) + * * Both values represent the same regular expression (In JavaScript, + * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual + * representation matches). + * + * During a property comparison, properties of `function` type and properties with names + * that begin with `$` are ignored. + * + * Scope and DOMWindow objects are being compared only by identify (`===`). + * + * @param {*} o1 Object or value to compare. + * @param {*} o2 Object or value to compare. + * @returns {boolean} True if arguments are equal. + * + * @example + + +
    +
    +

    User 1

    + Name: + Age: + +

    User 2

    + Name: + Age: + +
    +
    + +
    + User 1:
    {{user1 | json}}
    + User 2:
    {{user2 | json}}
    + Equal:
    {{result}}
    +
    +
    +
    + + angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) { + $scope.user1 = {}; + $scope.user2 = {}; + $scope.compare = function() { + $scope.result = angular.equals($scope.user1, $scope.user2); + }; + }]); + +
    + */ +function equals(o1, o2) { + if (o1 === o2) return true; + if (o1 === null || o2 === null) return false; + // eslint-disable-next-line no-self-compare + if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN + var t1 = typeof o1, t2 = typeof o2, length, key, keySet; + if (t1 === t2 && t1 === 'object') { + if (isArray(o1)) { + if (!isArray(o2)) return false; + if ((length = o1.length) === o2.length) { + for (key = 0; key < length; key++) { + if (!equals(o1[key], o2[key])) return false; + } + return true; + } + } else if (isDate(o1)) { + if (!isDate(o2)) return false; + return simpleCompare(o1.getTime(), o2.getTime()); + } else if (isRegExp(o1)) { + if (!isRegExp(o2)) return false; + return o1.toString() === o2.toString(); + } else { + if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || + isArray(o2) || isDate(o2) || isRegExp(o2)) return false; + keySet = createMap(); + for (key in o1) { + if (key.charAt(0) === '$' || isFunction(o1[key])) continue; + if (!equals(o1[key], o2[key])) return false; + keySet[key] = true; + } + for (key in o2) { + if (!(key in keySet) && + key.charAt(0) !== '$' && + isDefined(o2[key]) && + !isFunction(o2[key])) return false; + } + return true; + } + } + return false; +} + +var csp = function() { + if (!isDefined(csp.rules)) { + + + var ngCspElement = (window.document.querySelector('[ng-csp]') || + window.document.querySelector('[data-ng-csp]')); + + if (ngCspElement) { + var ngCspAttribute = ngCspElement.getAttribute('ng-csp') || + ngCspElement.getAttribute('data-ng-csp'); + csp.rules = { + noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1), + noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1) + }; + } else { + csp.rules = { + noUnsafeEval: noUnsafeEval(), + noInlineStyle: false + }; + } + } + + return csp.rules; + + function noUnsafeEval() { + try { + // eslint-disable-next-line no-new, no-new-func + new Function(''); + return false; + } catch (e) { + return true; + } + } +}; + +/** + * @ngdoc directive + * @module ng + * @name ngJq + * + * @element ANY + * @param {string=} ngJq the name of the library available under `window` + * to be used for angular.element + * @description + * Use this directive to force the angular.element library. This should be + * used to force either jqLite by leaving ng-jq blank or setting the name of + * the jquery variable under window (eg. jQuery). + * + * Since AngularJS looks for this directive when it is loaded (doesn't wait for the + * DOMContentLoaded event), it must be placed on an element that comes before the script + * which loads angular. Also, only the first instance of `ng-jq` will be used and all + * others ignored. + * + * @example + * This example shows how to force jqLite using the `ngJq` directive to the `html` tag. + ```html + + + ... + ... + + ``` + * @example + * This example shows how to use a jQuery based library of a different name. + * The library name must be available at the top most 'window'. + ```html + + + ... + ... + + ``` + */ +var jq = function() { + if (isDefined(jq.name_)) return jq.name_; + var el; + var i, ii = ngAttrPrefixes.length, prefix, name; + for (i = 0; i < ii; ++i) { + prefix = ngAttrPrefixes[i]; + el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]'); + if (el) { + name = el.getAttribute(prefix + 'jq'); + break; + } + } + + return (jq.name_ = name); +}; + +function concat(array1, array2, index) { + return array1.concat(slice.call(array2, index)); +} + +function sliceArgs(args, startIndex) { + return slice.call(args, startIndex || 0); +} + + +/** + * @ngdoc function + * @name angular.bind + * @module ng + * @kind function + * + * @description + * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for + * `fn`). You can supply optional `args` that are prebound to the function. This feature is also + * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as + * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). + * + * @param {Object} self Context which `fn` should be evaluated in. + * @param {function()} fn Function to be bound. + * @param {...*} args Optional arguments to be prebound to the `fn` function call. + * @returns {function()} Function that wraps the `fn` with all the specified bindings. + */ +function bind(self, fn) { + var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; + if (isFunction(fn) && !(fn instanceof RegExp)) { + return curryArgs.length + ? function() { + return arguments.length + ? fn.apply(self, concat(curryArgs, arguments, 0)) + : fn.apply(self, curryArgs); + } + : function() { + return arguments.length + ? fn.apply(self, arguments) + : fn.call(self); + }; + } else { + // In IE, native methods are not functions so they cannot be bound (note: they don't need to be). + return fn; + } +} + + +function toJsonReplacer(key, value) { + var val = value; + + if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { + val = undefined; + } else if (isWindow(value)) { + val = '$WINDOW'; + } else if (value && window.document === value) { + val = '$DOCUMENT'; + } else if (isScope(value)) { + val = '$SCOPE'; + } + + return val; +} + + +/** + * @ngdoc function + * @name angular.toJson + * @module ng + * @kind function + * + * @description + * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be + * stripped since AngularJS uses this notation internally. + * + * @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON. + * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. + * If set to an integer, the JSON output will contain that many spaces per indentation. + * @returns {string|undefined} JSON-ified string representing `obj`. + * @knownIssue + * + * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date` + * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the + * `Date.prototype.toJSON` method as follows: + * + * ``` + * var _DatetoJSON = Date.prototype.toJSON; + * Date.prototype.toJSON = function() { + * try { + * return _DatetoJSON.call(this); + * } catch(e) { + * if (e instanceof RangeError) { + * return null; + * } + * throw e; + * } + * }; + * ``` + * + * See https://github.com/angular/angular.js/pull/14221 for more information. + */ +function toJson(obj, pretty) { + if (isUndefined(obj)) return undefined; + if (!isNumber(pretty)) { + pretty = pretty ? 2 : null; + } + return JSON.stringify(obj, toJsonReplacer, pretty); +} + + +/** + * @ngdoc function + * @name angular.fromJson + * @module ng + * @kind function + * + * @description + * Deserializes a JSON string. + * + * @param {string} json JSON string to deserialize. + * @returns {Object|Array|string|number} Deserialized JSON string. + */ +function fromJson(json) { + return isString(json) + ? JSON.parse(json) + : json; +} + + +var ALL_COLONS = /:/g; +function timezoneToOffset(timezone, fallback) { + // Support: IE 9-11 only, Edge 13-15+ + // IE/Edge do not "understand" colon (`:`) in timezone + timezone = timezone.replace(ALL_COLONS, ''); + var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNumberNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; +} + + +function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; +} + + +function convertTimezoneToLocal(date, timezone, reverse) { + reverse = reverse ? -1 : 1; + var dateTimezoneOffset = date.getTimezoneOffset(); + var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); +} + + +/** + * @returns {string} Returns the string representation of the element. + */ +function startingTag(element) { + element = jqLite(element).clone().empty(); + var elemHtml = jqLite('
    ').append(element).html(); + try { + return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : + elemHtml. + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);}); + } catch (e) { + return lowercase(elemHtml); + } + +} + + +///////////////////////////////////////////////// + +/** + * Tries to decode the URI component without throwing an exception. + * + * @private + * @param str value potential URI component to check. + * @returns {boolean} True if `value` can be decoded + * with the decodeURIComponent function. + */ +function tryDecodeURIComponent(value) { + try { + return decodeURIComponent(value); + } catch (e) { + // Ignore any invalid uri component. + } +} + + +/** + * Parses an escaped url query string into key-value pairs. + * @returns {Object.} + */ +function parseKeyValue(/**string*/keyValue) { + var obj = {}; + forEach((keyValue || '').split('&'), function(keyValue) { + var splitPoint, key, val; + if (keyValue) { + key = keyValue = keyValue.replace(/\+/g,'%20'); + splitPoint = keyValue.indexOf('='); + if (splitPoint !== -1) { + key = keyValue.substring(0, splitPoint); + val = keyValue.substring(splitPoint + 1); + } + key = tryDecodeURIComponent(key); + if (isDefined(key)) { + val = isDefined(val) ? tryDecodeURIComponent(val) : true; + if (!hasOwnProperty.call(obj, key)) { + obj[key] = val; + } else if (isArray(obj[key])) { + obj[key].push(val); + } else { + obj[key] = [obj[key],val]; + } + } + } + }); + return obj; +} + +function toKeyValue(obj) { + var parts = []; + forEach(obj, function(value, key) { + if (isArray(value)) { + forEach(value, function(arrayValue) { + parts.push(encodeUriQuery(key, true) + + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); + }); + } else { + parts.push(encodeUriQuery(key, true) + + (value === true ? '' : '=' + encodeUriQuery(value, true))); + } + }); + return parts.length ? parts.join('&') : ''; +} + + +/** + * We need our custom method because encodeURIComponent is too aggressive and doesn't follow + * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path + * segments: + * segment = *pchar + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * pct-encoded = "%" HEXDIG HEXDIG + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriSegment(val) { + return encodeUriQuery(val, true). + replace(/%26/gi, '&'). + replace(/%3D/gi, '='). + replace(/%2B/gi, '+'); +} + + +/** + * This method is intended for encoding *key* or *value* parts of query component. We need a custom + * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be + * encoded per http://tools.ietf.org/html/rfc3986: + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +function encodeUriQuery(val, pctEncodeSpaces) { + return encodeURIComponent(val). + replace(/%40/gi, '@'). + replace(/%3A/gi, ':'). + replace(/%24/g, '$'). + replace(/%2C/gi, ','). + replace(/%3B/gi, ';'). + replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); +} + +var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; + +function getNgAttribute(element, ngAttr) { + var attr, i, ii = ngAttrPrefixes.length; + for (i = 0; i < ii; ++i) { + attr = ngAttrPrefixes[i] + ngAttr; + if (isString(attr = element.getAttribute(attr))) { + return attr; + } + } + return null; +} + +function allowAutoBootstrap(document) { + var script = document.currentScript; + + if (!script) { + // Support: IE 9-11 only + // IE does not have `document.currentScript` + return true; + } + + // If the `currentScript` property has been clobbered just return false, since this indicates a probable attack + if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) { + return false; + } + + var attributes = script.attributes; + var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')]; + + return srcs.every(function(src) { + if (!src) { + return true; + } + if (!src.value) { + return false; + } + + var link = document.createElement('a'); + link.href = src.value; + + if (document.location.origin === link.origin) { + // Same-origin resources are always allowed, even for non-whitelisted schemes. + return true; + } + // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web. + // This is to prevent angular.js bundled with browser extensions from being used to bypass the + // content security policy in web pages and other browser extensions. + switch (link.protocol) { + case 'http:': + case 'https:': + case 'ftp:': + case 'blob:': + case 'file:': + case 'data:': + return true; + default: + return false; + } + }); +} + +// Cached as it has to run during loading so that document.currentScript is available. +var isAutoBootstrapAllowed = allowAutoBootstrap(window.document); + +/** + * @ngdoc directive + * @name ngApp + * @module ng + * + * @element ANY + * @param {angular.Module} ngApp an optional application + * {@link angular.module module} name to load. + * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be + * created in "strict-di" mode. This means that the application will fail to invoke functions which + * do not use explicit function annotation (and are thus unsuitable for minification), as described + * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in + * tracking down the root of these bugs. + * + * @description + * + * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive + * designates the **root element** of the application and is typically placed near the root element + * of the page - e.g. on the `` or `` tags. + * + * There are a few things to keep in mind when using `ngApp`: + * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` + * found in the document will be used to define the root element to auto-bootstrap as an + * application. To run multiple applications in an HTML document you must manually bootstrap them using + * {@link angular.bootstrap} instead. + * - AngularJS applications cannot be nested within each other. + * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`. + * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and + * {@link ngRoute.ngView `ngView`}. + * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector}, + * causing animations to stop working and making the injector inaccessible from outside the app. + * + * You can specify an **AngularJS module** to be used as the root module for the application. This + * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It + * should contain the application code needed or have dependencies on other modules that will + * contain the code. See {@link angular.module} for more information. + * + * In the example below if the `ngApp` directive were not placed on the `html` element then the + * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` + * would not be resolved to `3`. + * + * @example + * + * ### Simple Usage + * + * `ngApp` is the easiest, and most common way to bootstrap an application. + * + + +
    + I can add: {{a}} + {{b}} = {{ a+b }} +
    +
    + + angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { + $scope.a = 1; + $scope.b = 2; + }); + +
    + * + * @example + * + * ### With `ngStrictDi` + * + * Using `ngStrictDi`, you would see something like this: + * + + +
    +
    + I can add: {{a}} + {{b}} = {{ a+b }} + +

    This renders because the controller does not fail to + instantiate, by using explicit annotation style (see + script.js for details) +

    +
    + +
    + Name:
    + Hello, {{name}}! + +

    This renders because the controller does not fail to + instantiate, by using explicit annotation style + (see script.js for details) +

    +
    + +
    + I can add: {{a}} + {{b}} = {{ a+b }} + +

    The controller could not be instantiated, due to relying + on automatic function annotations (which are disabled in + strict mode). As such, the content of this section is not + interpolated, and there should be an error in your web console. +

    +
    +
    +
    + + angular.module('ngAppStrictDemo', []) + // BadController will fail to instantiate, due to relying on automatic function annotation, + // rather than an explicit annotation + .controller('BadController', function($scope) { + $scope.a = 1; + $scope.b = 2; + }) + // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, + // due to using explicit annotations using the array style and $inject property, respectively. + .controller('GoodController1', ['$scope', function($scope) { + $scope.a = 1; + $scope.b = 2; + }]) + .controller('GoodController2', GoodController2); + function GoodController2($scope) { + $scope.name = 'World'; + } + GoodController2.$inject = ['$scope']; + + + div[ng-controller] { + margin-bottom: 1em; + -webkit-border-radius: 4px; + border-radius: 4px; + border: 1px solid; + padding: .5em; + } + div[ng-controller^=Good] { + border-color: #d6e9c6; + background-color: #dff0d8; + color: #3c763d; + } + div[ng-controller^=Bad] { + border-color: #ebccd1; + background-color: #f2dede; + color: #a94442; + margin-bottom: 0; + } + +
    + */ +function angularInit(element, bootstrap) { + var appElement, + module, + config = {}; + + // The element `element` has priority over any other element. + forEach(ngAttrPrefixes, function(prefix) { + var name = prefix + 'app'; + + if (!appElement && element.hasAttribute && element.hasAttribute(name)) { + appElement = element; + module = element.getAttribute(name); + } + }); + forEach(ngAttrPrefixes, function(prefix) { + var name = prefix + 'app'; + var candidate; + + if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) { + appElement = candidate; + module = candidate.getAttribute(name); + } + }); + if (appElement) { + if (!isAutoBootstrapAllowed) { + window.console.error('AngularJS: disabling automatic bootstrap. + * + * + * + * ``` + * + * @param {DOMElement} element DOM element which is the root of AngularJS application. + * @param {Array=} modules an array of modules to load into the application. + * Each item in the array should be the name of a predefined module or a (DI annotated) + * function that will be invoked by the injector as a `config` block. + * See: {@link angular.module modules} + * @param {Object=} config an object for defining configuration options for the application. The + * following keys are supported: + * + * * `strictDi` - disable automatic function annotation for the application. This is meant to + * assist in finding bugs which break minified code. Defaults to `false`. + * + * @returns {auto.$injector} Returns the newly created injector for this app. + */ +function bootstrap(element, modules, config) { + if (!isObject(config)) config = {}; + var defaultConfig = { + strictDi: false + }; + config = extend(defaultConfig, config); + var doBootstrap = function() { + element = jqLite(element); + + if (element.injector()) { + var tag = (element[0] === window.document) ? 'document' : startingTag(element); + // Encode angle brackets to prevent input from being sanitized to empty string #8683. + throw ngMinErr( + 'btstrpd', + 'App already bootstrapped with this element \'{0}\'', + tag.replace(//,'>')); + } + + modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); + + if (config.debugInfoEnabled) { + // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. + modules.push(['$compileProvider', function($compileProvider) { + $compileProvider.debugInfoEnabled(true); + }]); + } + + modules.unshift('ng'); + var injector = createInjector(modules, config.strictDi); + injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', + function bootstrapApply(scope, element, compile, injector) { + scope.$apply(function() { + element.data('$injector', injector); + compile(element)(scope); + }); + }] + ); + return injector; + }; + + var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/; + var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; + + if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) { + config.debugInfoEnabled = true; + window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, ''); + } + + if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { + return doBootstrap(); + } + + window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); + angular.resumeBootstrap = function(extraModules) { + forEach(extraModules, function(module) { + modules.push(module); + }); + return doBootstrap(); + }; + + if (isFunction(angular.resumeDeferredBootstrap)) { + angular.resumeDeferredBootstrap(); + } +} + +/** + * @ngdoc function + * @name angular.reloadWithDebugInfo + * @module ng + * @description + * Use this function to reload the current application with debug information turned on. + * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. + * + * See {@link ng.$compileProvider#debugInfoEnabled} for more. + */ +function reloadWithDebugInfo() { + window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name; + window.location.reload(); +} + +/** + * @name angular.getTestability + * @module ng + * @description + * Get the testability service for the instance of AngularJS on the given + * element. + * @param {DOMElement} element DOM element which is the root of AngularJS application. + */ +function getTestability(rootElement) { + var injector = angular.element(rootElement).injector(); + if (!injector) { + throw ngMinErr('test', + 'no injector found for element argument to getTestability'); + } + return injector.get('$$testability'); +} + +var SNAKE_CASE_REGEXP = /[A-Z]/g; +function snake_case(name, separator) { + separator = separator || '_'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); +} + +var bindJQueryFired = false; +function bindJQuery() { + var originalCleanData; + + if (bindJQueryFired) { + return; + } + + // bind to jQuery if present; + var jqName = jq(); + jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present) + !jqName ? undefined : // use jqLite + window[jqName]; // use jQuery specified by `ngJq` + + // Use jQuery if it exists with proper functionality, otherwise default to us. + // AngularJS 1.2+ requires jQuery 1.7+ for on()/off() support. + // AngularJS 1.3+ technically requires at least jQuery 2.1+ but it may work with older + // versions. It will not work for sure with jQuery <1.7, though. + if (jQuery && jQuery.fn.on) { + jqLite = jQuery; + extend(jQuery.fn, { + scope: JQLitePrototype.scope, + isolateScope: JQLitePrototype.isolateScope, + controller: /** @type {?} */ (JQLitePrototype).controller, + injector: JQLitePrototype.injector, + inheritedData: JQLitePrototype.inheritedData + }); + + // All nodes removed from the DOM via various jQuery APIs like .remove() + // are passed through jQuery.cleanData. Monkey-patch this method to fire + // the $destroy event on all removed nodes. + originalCleanData = jQuery.cleanData; + jQuery.cleanData = function(elems) { + var events; + for (var i = 0, elem; (elem = elems[i]) != null; i++) { + events = jQuery._data(elem, 'events'); + if (events && events.$destroy) { + jQuery(elem).triggerHandler('$destroy'); + } + } + originalCleanData(elems); + }; + } else { + jqLite = JQLite; + } + + angular.element = jqLite; + + // Prevent double-proxying. + bindJQueryFired = true; +} + +/** + * throw error if the argument is falsy. + */ +function assertArg(arg, name, reason) { + if (!arg) { + throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required')); + } + return arg; +} + +function assertArgFn(arg, name, acceptArrayAnnotation) { + if (acceptArrayAnnotation && isArray(arg)) { + arg = arg[arg.length - 1]; + } + + assertArg(isFunction(arg), name, 'not a function, got ' + + (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); + return arg; +} + +/** + * throw error if the name given is hasOwnProperty + * @param {String} name the name to test + * @param {String} context the context in which the name is used, such as module or directive + */ +function assertNotHasOwnProperty(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); + } +} + +/** + * Return the value accessible from the object by path. Any undefined traversals are ignored + * @param {Object} obj starting object + * @param {String} path path to traverse + * @param {boolean} [bindFnToScope=true] + * @returns {Object} value as accessible by path + */ +//TODO(misko): this function needs to be removed +function getter(obj, path, bindFnToScope) { + if (!path) return obj; + var keys = path.split('.'); + var key; + var lastInstance = obj; + var len = keys.length; + + for (var i = 0; i < len; i++) { + key = keys[i]; + if (obj) { + obj = (lastInstance = obj)[key]; + } + } + if (!bindFnToScope && isFunction(obj)) { + return bind(lastInstance, obj); + } + return obj; +} + +/** + * Return the DOM siblings between the first and last node in the given array. + * @param {Array} array like object + * @returns {Array} the inputted object or a jqLite collection containing the nodes + */ +function getBlockNodes(nodes) { + // TODO(perf): update `nodes` instead of creating a new object? + var node = nodes[0]; + var endNode = nodes[nodes.length - 1]; + var blockNodes; + + for (var i = 1; node !== endNode && (node = node.nextSibling); i++) { + if (blockNodes || nodes[i] !== node) { + if (!blockNodes) { + blockNodes = jqLite(slice.call(nodes, 0, i)); + } + blockNodes.push(node); + } + } + + return blockNodes || nodes; +} + + +/** + * Creates a new object without a prototype. This object is useful for lookup without having to + * guard against prototypically inherited properties via hasOwnProperty. + * + * Related micro-benchmarks: + * - http://jsperf.com/object-create2 + * - http://jsperf.com/proto-map-lookup/2 + * - http://jsperf.com/for-in-vs-object-keys2 + * + * @returns {Object} + */ +function createMap() { + return Object.create(null); +} + +function stringify(value) { + if (value == null) { // null || undefined + return ''; + } + switch (typeof value) { + case 'string': + break; + case 'number': + value = '' + value; + break; + default: + if (hasCustomToString(value) && !isArray(value) && !isDate(value)) { + value = value.toString(); + } else { + value = toJson(value); + } + } + + return value; +} + +var NODE_TYPE_ELEMENT = 1; +var NODE_TYPE_ATTRIBUTE = 2; +var NODE_TYPE_TEXT = 3; +var NODE_TYPE_COMMENT = 8; +var NODE_TYPE_DOCUMENT = 9; +var NODE_TYPE_DOCUMENT_FRAGMENT = 11; + +/** + * @ngdoc type + * @name angular.Module + * @module ng + * @description + * + * Interface for configuring AngularJS {@link angular.module modules}. + */ + +function setupModuleLoader(window) { + + var $injectorMinErr = minErr('$injector'); + var ngMinErr = minErr('ng'); + + function ensure(obj, name, factory) { + return obj[name] || (obj[name] = factory()); + } + + var angular = ensure(window, 'angular', Object); + + // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap + angular.$$minErr = angular.$$minErr || minErr; + + return ensure(angular, 'module', function() { + /** @type {Object.} */ + var modules = {}; + + /** + * @ngdoc function + * @name angular.module + * @module ng + * @description + * + * The `angular.module` is a global place for creating, registering and retrieving AngularJS + * modules. + * All modules (AngularJS core or 3rd party) that should be available to an application must be + * registered using this mechanism. + * + * Passing one argument retrieves an existing {@link angular.Module}, + * whereas passing more than one argument creates a new {@link angular.Module} + * + * + * # Module + * + * A module is a collection of services, directives, controllers, filters, and configuration information. + * `angular.module` is used to configure the {@link auto.$injector $injector}. + * + * ```js + * // Create a new module + * var myModule = angular.module('myModule', []); + * + * // register a new service + * myModule.value('appName', 'MyCoolApp'); + * + * // configure existing services inside initialization blocks. + * myModule.config(['$locationProvider', function($locationProvider) { + * // Configure existing providers + * $locationProvider.hashPrefix('!'); + * }]); + * ``` + * + * Then you can create an injector and load your modules like this: + * + * ```js + * var injector = angular.injector(['ng', 'myModule']) + * ``` + * + * However it's more likely that you'll just use + * {@link ng.directive:ngApp ngApp} or + * {@link angular.bootstrap} to simplify this process for you. + * + * @param {!string} name The name of the module to create or retrieve. + * @param {!Array.=} requires If specified then new module is being created. If + * unspecified then the module is being retrieved for further configuration. + * @param {Function=} configFn Optional configuration function for the module. Same as + * {@link angular.Module#config Module#config()}. + * @returns {angular.Module} new module with the {@link angular.Module} api. + */ + return function module(name, requires, configFn) { + + var info = {}; + + var assertNotHasOwnProperty = function(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); + } + }; + + assertNotHasOwnProperty(name, 'module'); + if (requires && modules.hasOwnProperty(name)) { + modules[name] = null; + } + return ensure(modules, name, function() { + if (!requires) { + throw $injectorMinErr('nomod', 'Module \'{0}\' is not available! You either misspelled ' + + 'the module name or forgot to load it. If registering a module ensure that you ' + + 'specify the dependencies as the second argument.', name); + } + + /** @type {!Array.>} */ + var invokeQueue = []; + + /** @type {!Array.} */ + var configBlocks = []; + + /** @type {!Array.} */ + var runBlocks = []; + + var config = invokeLater('$injector', 'invoke', 'push', configBlocks); + + /** @type {angular.Module} */ + var moduleInstance = { + // Private state + _invokeQueue: invokeQueue, + _configBlocks: configBlocks, + _runBlocks: runBlocks, + + /** + * @ngdoc method + * @name angular.Module#info + * @module ng + * + * @param {Object=} info Information about the module + * @returns {Object|Module} The current info object for this module if called as a getter, + * or `this` if called as a setter. + * + * @description + * Read and write custom information about this module. + * For example you could put the version of the module in here. + * + * ```js + * angular.module('myModule', []).info({ version: '1.0.0' }); + * ``` + * + * The version could then be read back out by accessing the module elsewhere: + * + * ``` + * var version = angular.module('myModule').info().version; + * ``` + * + * You can also retrieve this information during runtime via the + * {@link $injector#modules `$injector.modules`} property: + * + * ```js + * var version = $injector.modules['myModule'].info().version; + * ``` + */ + info: function(value) { + if (isDefined(value)) { + if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value'); + info = value; + return this; + } + return info; + }, + + /** + * @ngdoc property + * @name angular.Module#requires + * @module ng + * + * @description + * Holds the list of modules which the injector will load before the current module is + * loaded. + */ + requires: requires, + + /** + * @ngdoc property + * @name angular.Module#name + * @module ng + * + * @description + * Name of the module. + */ + name: name, + + + /** + * @ngdoc method + * @name angular.Module#provider + * @module ng + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the + * service. + * @description + * See {@link auto.$provide#provider $provide.provider()}. + */ + provider: invokeLaterAndSetModuleName('$provide', 'provider'), + + /** + * @ngdoc method + * @name angular.Module#factory + * @module ng + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. + * @description + * See {@link auto.$provide#factory $provide.factory()}. + */ + factory: invokeLaterAndSetModuleName('$provide', 'factory'), + + /** + * @ngdoc method + * @name angular.Module#service + * @module ng + * @param {string} name service name + * @param {Function} constructor A constructor function that will be instantiated. + * @description + * See {@link auto.$provide#service $provide.service()}. + */ + service: invokeLaterAndSetModuleName('$provide', 'service'), + + /** + * @ngdoc method + * @name angular.Module#value + * @module ng + * @param {string} name service name + * @param {*} object Service instance object. + * @description + * See {@link auto.$provide#value $provide.value()}. + */ + value: invokeLater('$provide', 'value'), + + /** + * @ngdoc method + * @name angular.Module#constant + * @module ng + * @param {string} name constant name + * @param {*} object Constant value. + * @description + * Because the constants are fixed, they get applied before other provide methods. + * See {@link auto.$provide#constant $provide.constant()}. + */ + constant: invokeLater('$provide', 'constant', 'unshift'), + + /** + * @ngdoc method + * @name angular.Module#decorator + * @module ng + * @param {string} name The name of the service to decorate. + * @param {Function} decorFn This function will be invoked when the service needs to be + * instantiated and should return the decorated service instance. + * @description + * See {@link auto.$provide#decorator $provide.decorator()}. + */ + decorator: invokeLaterAndSetModuleName('$provide', 'decorator', configBlocks), + + /** + * @ngdoc method + * @name angular.Module#animation + * @module ng + * @param {string} name animation name + * @param {Function} animationFactory Factory function for creating new instance of an + * animation. + * @description + * + * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. + * + * + * Defines an animation hook that can be later used with + * {@link $animate $animate} service and directives that use this service. + * + * ```js + * module.animation('.animation-name', function($inject1, $inject2) { + * return { + * eventName : function(element, done) { + * //code to run the animation + * //once complete, then run done() + * return function cancellationFunction(element) { + * //code to cancel the animation + * } + * } + * } + * }) + * ``` + * + * See {@link ng.$animateProvider#register $animateProvider.register()} and + * {@link ngAnimate ngAnimate module} for more information. + */ + animation: invokeLaterAndSetModuleName('$animateProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#filter + * @module ng + * @param {string} name Filter name - this must be a valid AngularJS expression identifier + * @param {Function} filterFactory Factory function for creating new instance of filter. + * @description + * See {@link ng.$filterProvider#register $filterProvider.register()}. + * + *
    + * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`. + * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace + * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores + * (`myapp_subsection_filterx`). + *
    + */ + filter: invokeLaterAndSetModuleName('$filterProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#controller + * @module ng + * @param {string|Object} name Controller name, or an object map of controllers where the + * keys are the names and the values are the constructors. + * @param {Function} constructor Controller constructor function. + * @description + * See {@link ng.$controllerProvider#register $controllerProvider.register()}. + */ + controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#directive + * @module ng + * @param {string|Object} name Directive name, or an object map of directives where the + * keys are the names and the values are the factories. + * @param {Function} directiveFactory Factory function for creating new instance of + * directives. + * @description + * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'), + + /** + * @ngdoc method + * @name angular.Module#component + * @module ng + * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp) + * @param {Object} options Component definition object (a simplified + * {@link ng.$compile#directive-definition-object directive definition object}) + * + * @description + * See {@link ng.$compileProvider#component $compileProvider.component()}. + */ + component: invokeLaterAndSetModuleName('$compileProvider', 'component'), + + /** + * @ngdoc method + * @name angular.Module#config + * @module ng + * @param {Function} configFn Execute this function on module load. Useful for service + * configuration. + * @description + * Use this method to configure services by injecting their + * {@link angular.Module#provider `providers`}, e.g. for adding routes to the + * {@link ngRoute.$routeProvider $routeProvider}. + * + * Note that you can only inject {@link angular.Module#provider `providers`} and + * {@link angular.Module#constant `constants`} into this function. + * + * For more about how to configure services, see + * {@link providers#provider-recipe Provider Recipe}. + */ + config: config, + + /** + * @ngdoc method + * @name angular.Module#run + * @module ng + * @param {Function} initializationFn Execute this function after injector creation. + * Useful for application initialization. + * @description + * Use this method to register work which should be performed when the injector is done + * loading all modules. + */ + run: function(block) { + runBlocks.push(block); + return this; + } + }; + + if (configFn) { + config(configFn); + } + + return moduleInstance; + + /** + * @param {string} provider + * @param {string} method + * @param {String=} insertMethod + * @returns {angular.Module} + */ + function invokeLater(provider, method, insertMethod, queue) { + if (!queue) queue = invokeQueue; + return function() { + queue[insertMethod || 'push']([provider, method, arguments]); + return moduleInstance; + }; + } + + /** + * @param {string} provider + * @param {string} method + * @returns {angular.Module} + */ + function invokeLaterAndSetModuleName(provider, method, queue) { + if (!queue) queue = invokeQueue; + return function(recipeName, factoryFunction) { + if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name; + queue.push([provider, method, arguments]); + return moduleInstance; + }; + } + }); + }; + }); + +} + +/* global shallowCopy: true */ + +/** + * Creates a shallow copy of an object, an array or a primitive. + * + * Assumes that there are no proto properties for objects. + */ +function shallowCopy(src, dst) { + if (isArray(src)) { + dst = dst || []; + + for (var i = 0, ii = src.length; i < ii; i++) { + dst[i] = src[i]; + } + } else if (isObject(src)) { + dst = dst || {}; + + for (var key in src) { + if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { + dst[key] = src[key]; + } + } + } + + return dst || src; +} + +/* exported toDebugString */ + +function serializeObject(obj, maxDepth) { + var seen = []; + + // There is no direct way to stringify object until reaching a specific depth + // and a very deep object can cause a performance issue, so we copy the object + // based on this specific depth and then stringify it. + if (isValidObjectMaxDepth(maxDepth)) { + // This file is also included in `angular-loader`, so `copy()` might not always be available in + // the closure. Therefore, it is lazily retrieved as `angular.copy()` when needed. + obj = angular.copy(obj, null, maxDepth); + } + return JSON.stringify(obj, function(key, val) { + val = toJsonReplacer(key, val); + if (isObject(val)) { + + if (seen.indexOf(val) >= 0) return '...'; + + seen.push(val); + } + return val; + }); +} + +function toDebugString(obj, maxDepth) { + if (typeof obj === 'function') { + return obj.toString().replace(/ \{[\s\S]*$/, ''); + } else if (isUndefined(obj)) { + return 'undefined'; + } else if (typeof obj !== 'string') { + return serializeObject(obj, maxDepth); + } + return obj; +} + +/* global angularModule: true, + version: true, + + $CompileProvider, + + htmlAnchorDirective, + inputDirective, + inputDirective, + formDirective, + scriptDirective, + selectDirective, + optionDirective, + ngBindDirective, + ngBindHtmlDirective, + ngBindTemplateDirective, + ngClassDirective, + ngClassEvenDirective, + ngClassOddDirective, + ngCloakDirective, + ngControllerDirective, + ngFormDirective, + ngHideDirective, + ngIfDirective, + ngIncludeDirective, + ngIncludeFillContentDirective, + ngInitDirective, + ngNonBindableDirective, + ngPluralizeDirective, + ngRepeatDirective, + ngShowDirective, + ngStyleDirective, + ngSwitchDirective, + ngSwitchWhenDirective, + ngSwitchDefaultDirective, + ngOptionsDirective, + ngTranscludeDirective, + ngModelDirective, + ngListDirective, + ngChangeDirective, + patternDirective, + patternDirective, + requiredDirective, + requiredDirective, + minlengthDirective, + minlengthDirective, + maxlengthDirective, + maxlengthDirective, + ngValueDirective, + ngModelOptionsDirective, + ngAttributeAliasDirectives, + ngEventDirectives, + + $AnchorScrollProvider, + $AnimateProvider, + $CoreAnimateCssProvider, + $$CoreAnimateJsProvider, + $$CoreAnimateQueueProvider, + $$AnimateRunnerFactoryProvider, + $$AnimateAsyncRunFactoryProvider, + $BrowserProvider, + $CacheFactoryProvider, + $ControllerProvider, + $DateProvider, + $DocumentProvider, + $$IsDocumentHiddenProvider, + $ExceptionHandlerProvider, + $FilterProvider, + $$ForceReflowProvider, + $InterpolateProvider, + $IntervalProvider, + $HttpProvider, + $HttpParamSerializerProvider, + $HttpParamSerializerJQLikeProvider, + $HttpBackendProvider, + $xhrFactoryProvider, + $jsonpCallbacksProvider, + $LocationProvider, + $LogProvider, + $$MapProvider, + $ParseProvider, + $RootScopeProvider, + $QProvider, + $$QProvider, + $$SanitizeUriProvider, + $SceProvider, + $SceDelegateProvider, + $SnifferProvider, + $TemplateCacheProvider, + $TemplateRequestProvider, + $$TestabilityProvider, + $TimeoutProvider, + $$RAFProvider, + $WindowProvider, + $$jqLiteProvider, + $$CookieReaderProvider +*/ + + +/** + * @ngdoc object + * @name angular.version + * @module ng + * @description + * An object that contains information about the current AngularJS version. + * + * This object has the following properties: + * + * - `full` – `{string}` – Full version string, such as "0.9.18". + * - `major` – `{number}` – Major version number, such as "0". + * - `minor` – `{number}` – Minor version number, such as "9". + * - `dot` – `{number}` – Dot version number, such as "18". + * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". + */ +var version = { + // These placeholder strings will be replaced by grunt's `build` task. + // They need to be double- or single-quoted. + full: '1.6.9', + major: 1, + minor: 6, + dot: 9, + codeName: 'fiery-basilisk' +}; + + +function publishExternalAPI(angular) { + extend(angular, { + 'errorHandlingConfig': errorHandlingConfig, + 'bootstrap': bootstrap, + 'copy': copy, + 'extend': extend, + 'merge': merge, + 'equals': equals, + 'element': jqLite, + 'forEach': forEach, + 'injector': createInjector, + 'noop': noop, + 'bind': bind, + 'toJson': toJson, + 'fromJson': fromJson, + 'identity': identity, + 'isUndefined': isUndefined, + 'isDefined': isDefined, + 'isString': isString, + 'isFunction': isFunction, + 'isObject': isObject, + 'isNumber': isNumber, + 'isElement': isElement, + 'isArray': isArray, + 'version': version, + 'isDate': isDate, + 'lowercase': lowercase, + 'uppercase': uppercase, + 'callbacks': {$$counter: 0}, + 'getTestability': getTestability, + 'reloadWithDebugInfo': reloadWithDebugInfo, + '$$minErr': minErr, + '$$csp': csp, + '$$encodeUriSegment': encodeUriSegment, + '$$encodeUriQuery': encodeUriQuery, + '$$stringify': stringify + }); + + angularModule = setupModuleLoader(window); + + angularModule('ng', ['ngLocale'], ['$provide', + function ngModule($provide) { + // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. + $provide.provider({ + $$sanitizeUri: $$SanitizeUriProvider + }); + $provide.provider('$compile', $CompileProvider). + directive({ + a: htmlAnchorDirective, + input: inputDirective, + textarea: inputDirective, + form: formDirective, + script: scriptDirective, + select: selectDirective, + option: optionDirective, + ngBind: ngBindDirective, + ngBindHtml: ngBindHtmlDirective, + ngBindTemplate: ngBindTemplateDirective, + ngClass: ngClassDirective, + ngClassEven: ngClassEvenDirective, + ngClassOdd: ngClassOddDirective, + ngCloak: ngCloakDirective, + ngController: ngControllerDirective, + ngForm: ngFormDirective, + ngHide: ngHideDirective, + ngIf: ngIfDirective, + ngInclude: ngIncludeDirective, + ngInit: ngInitDirective, + ngNonBindable: ngNonBindableDirective, + ngPluralize: ngPluralizeDirective, + ngRepeat: ngRepeatDirective, + ngShow: ngShowDirective, + ngStyle: ngStyleDirective, + ngSwitch: ngSwitchDirective, + ngSwitchWhen: ngSwitchWhenDirective, + ngSwitchDefault: ngSwitchDefaultDirective, + ngOptions: ngOptionsDirective, + ngTransclude: ngTranscludeDirective, + ngModel: ngModelDirective, + ngList: ngListDirective, + ngChange: ngChangeDirective, + pattern: patternDirective, + ngPattern: patternDirective, + required: requiredDirective, + ngRequired: requiredDirective, + minlength: minlengthDirective, + ngMinlength: minlengthDirective, + maxlength: maxlengthDirective, + ngMaxlength: maxlengthDirective, + ngValue: ngValueDirective, + ngModelOptions: ngModelOptionsDirective + }). + directive({ + ngInclude: ngIncludeFillContentDirective + }). + directive(ngAttributeAliasDirectives). + directive(ngEventDirectives); + $provide.provider({ + $anchorScroll: $AnchorScrollProvider, + $animate: $AnimateProvider, + $animateCss: $CoreAnimateCssProvider, + $$animateJs: $$CoreAnimateJsProvider, + $$animateQueue: $$CoreAnimateQueueProvider, + $$AnimateRunner: $$AnimateRunnerFactoryProvider, + $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider, + $browser: $BrowserProvider, + $cacheFactory: $CacheFactoryProvider, + $controller: $ControllerProvider, + $document: $DocumentProvider, + $$isDocumentHidden: $$IsDocumentHiddenProvider, + $exceptionHandler: $ExceptionHandlerProvider, + $filter: $FilterProvider, + $$forceReflow: $$ForceReflowProvider, + $interpolate: $InterpolateProvider, + $interval: $IntervalProvider, + $http: $HttpProvider, + $httpParamSerializer: $HttpParamSerializerProvider, + $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider, + $httpBackend: $HttpBackendProvider, + $xhrFactory: $xhrFactoryProvider, + $jsonpCallbacks: $jsonpCallbacksProvider, + $location: $LocationProvider, + $log: $LogProvider, + $parse: $ParseProvider, + $rootScope: $RootScopeProvider, + $q: $QProvider, + $$q: $$QProvider, + $sce: $SceProvider, + $sceDelegate: $SceDelegateProvider, + $sniffer: $SnifferProvider, + $templateCache: $TemplateCacheProvider, + $templateRequest: $TemplateRequestProvider, + $$testability: $$TestabilityProvider, + $timeout: $TimeoutProvider, + $window: $WindowProvider, + $$rAF: $$RAFProvider, + $$jqLite: $$jqLiteProvider, + $$Map: $$MapProvider, + $$cookieReader: $$CookieReaderProvider + }); + } + ]) + .info({ angularVersion: '1.6.9' }); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* global + JQLitePrototype: true, + BOOLEAN_ATTR: true, + ALIASED_ATTR: true +*/ + +////////////////////////////////// +//JQLite +////////////////////////////////// + +/** + * @ngdoc function + * @name angular.element + * @module ng + * @kind function + * + * @description + * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. + * + * If jQuery is available, `angular.element` is an alias for the + * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` + * delegates to AngularJS's built-in subset of jQuery, called "jQuery lite" or **jqLite**. + * + * jqLite is a tiny, API-compatible subset of jQuery that allows + * AngularJS to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most + * commonly needed functionality with the goal of having a very small footprint. + * + * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the + * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a + * specific version of jQuery if multiple versions exist on the page. + * + *
    **Note:** All element references in AngularJS are always wrapped with jQuery or + * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.
    + * + *
    **Note:** Keep in mind that this function will not find elements + * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)` + * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.
    + * + * ## AngularJS's jqLite + * jqLite provides only the following jQuery methods: + * + * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument + * - [`after()`](http://api.jquery.com/after/) + * - [`append()`](http://api.jquery.com/append/) + * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters + * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData + * - [`children()`](http://api.jquery.com/children/) - Does not support selectors + * - [`clone()`](http://api.jquery.com/clone/) + * - [`contents()`](http://api.jquery.com/contents/) + * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. + * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing. + * - [`data()`](http://api.jquery.com/data/) + * - [`detach()`](http://api.jquery.com/detach/) + * - [`empty()`](http://api.jquery.com/empty/) + * - [`eq()`](http://api.jquery.com/eq/) + * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name + * - [`hasClass()`](http://api.jquery.com/hasClass/) + * - [`html()`](http://api.jquery.com/html/) + * - [`next()`](http://api.jquery.com/next/) - Does not support selectors + * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData + * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter + * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors + * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors + * - [`prepend()`](http://api.jquery.com/prepend/) + * - [`prop()`](http://api.jquery.com/prop/) + * - [`ready()`](http://api.jquery.com/ready/) (_deprecated_, use `angular.element(callback)` instead of `angular.element(document).ready(callback)`) + * - [`remove()`](http://api.jquery.com/remove/) + * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes + * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument + * - [`removeData()`](http://api.jquery.com/removeData/) + * - [`replaceWith()`](http://api.jquery.com/replaceWith/) + * - [`text()`](http://api.jquery.com/text/) + * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument + * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers + * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter + * - [`val()`](http://api.jquery.com/val/) + * - [`wrap()`](http://api.jquery.com/wrap/) + * + * ## jQuery/jqLite Extras + * AngularJS also provides the following additional methods and events to both jQuery and jqLite: + * + * ### Events + * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event + * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM + * element before it is removed. + * + * ### Methods + * - `controller(name)` - retrieves the controller of the current element or its parent. By default + * retrieves controller associated with the `ngController` directive. If `name` is provided as + * camelCase directive name, then the controller for this directive will be retrieved (e.g. + * `'ngModel'`). + * - `injector()` - retrieves the injector of the current element or its parent. + * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current + * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to + * be enabled. + * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the + * current element. This getter should be used only on elements that contain a directive which starts a new isolate + * scope. Calling `scope()` on this element always returns the original non-isolate scope. + * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. + * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top + * parent element is reached. + * + * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See + * https://github.com/angular/angular.js/issues/14251 for more information. + * + * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. + * @returns {Object} jQuery object. + */ + +JQLite.expando = 'ng339'; + +var jqCache = JQLite.cache = {}, + jqId = 1; + +/* + * !!! This is an undocumented "private" function !!! + */ +JQLite._data = function(node) { + //jQuery always returns an object on cache miss + return this.cache[node[this.expando]] || {}; +}; + +function jqNextId() { return ++jqId; } + + +var DASH_LOWERCASE_REGEXP = /-([a-z])/g; +var MS_HACK_REGEXP = /^-ms-/; +var MOUSE_EVENT_MAP = { mouseleave: 'mouseout', mouseenter: 'mouseover' }; +var jqLiteMinErr = minErr('jqLite'); + +/** + * Converts kebab-case to camelCase. + * There is also a special case for the ms prefix starting with a lowercase letter. + * @param name Name to normalize + */ +function cssKebabToCamel(name) { + return kebabToCamel(name.replace(MS_HACK_REGEXP, 'ms-')); +} + +function fnCamelCaseReplace(all, letter) { + return letter.toUpperCase(); +} + +/** + * Converts kebab-case to camelCase. + * @param name Name to normalize + */ +function kebabToCamel(name) { + return name + .replace(DASH_LOWERCASE_REGEXP, fnCamelCaseReplace); +} + +var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/; +var HTML_REGEXP = /<|&#?\w+;/; +var TAG_NAME_REGEXP = /<([\w:-]+)/; +var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi; + +var wrapMap = { + 'option': [1, ''], + + 'thead': [1, '', '
    '], + 'col': [2, '', '
    '], + 'tr': [2, '', '
    '], + 'td': [3, '', '
    '], + '_default': [0, '', ''] +}; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function jqLiteIsTextNode(html) { + return !HTML_REGEXP.test(html); +} + +function jqLiteAcceptsData(node) { + // The window object can accept data but has no nodeType + // Otherwise we are only interested in elements (1) and documents (9) + var nodeType = node.nodeType; + return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT; +} + +function jqLiteHasData(node) { + for (var key in jqCache[node.ng339]) { + return true; + } + return false; +} + +function jqLiteBuildFragment(html, context) { + var tmp, tag, wrap, + fragment = context.createDocumentFragment(), + nodes = [], i; + + if (jqLiteIsTextNode(html)) { + // Convert non-html into a text node + nodes.push(context.createTextNode(html)); + } else { + // Convert html into DOM nodes + tmp = fragment.appendChild(context.createElement('div')); + tag = (TAG_NAME_REGEXP.exec(html) || ['', ''])[1].toLowerCase(); + wrap = wrapMap[tag] || wrapMap._default; + tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, '<$1>') + wrap[2]; + + // Descend through wrappers to the right content + i = wrap[0]; + while (i--) { + tmp = tmp.lastChild; + } + + nodes = concat(nodes, tmp.childNodes); + + tmp = fragment.firstChild; + tmp.textContent = ''; + } + + // Remove wrapper from fragment + fragment.textContent = ''; + fragment.innerHTML = ''; // Clear inner HTML + forEach(nodes, function(node) { + fragment.appendChild(node); + }); + + return fragment; +} + +function jqLiteParseHTML(html, context) { + context = context || window.document; + var parsed; + + if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { + return [context.createElement(parsed[1])]; + } + + if ((parsed = jqLiteBuildFragment(html, context))) { + return parsed.childNodes; + } + + return []; +} + +function jqLiteWrapNode(node, wrapper) { + var parent = node.parentNode; + + if (parent) { + parent.replaceChild(wrapper, node); + } + + wrapper.appendChild(node); +} + + +// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. +var jqLiteContains = window.Node.prototype.contains || /** @this */ function(arg) { + // eslint-disable-next-line no-bitwise + return !!(this.compareDocumentPosition(arg) & 16); +}; + +///////////////////////////////////////////// +function JQLite(element) { + if (element instanceof JQLite) { + return element; + } + + var argIsString; + + if (isString(element)) { + element = trim(element); + argIsString = true; + } + if (!(this instanceof JQLite)) { + if (argIsString && element.charAt(0) !== '<') { + throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); + } + return new JQLite(element); + } + + if (argIsString) { + jqLiteAddNodes(this, jqLiteParseHTML(element)); + } else if (isFunction(element)) { + jqLiteReady(element); + } else { + jqLiteAddNodes(this, element); + } +} + +function jqLiteClone(element) { + return element.cloneNode(true); +} + +function jqLiteDealoc(element, onlyDescendants) { + if (!onlyDescendants && jqLiteAcceptsData(element)) jqLite.cleanData([element]); + + if (element.querySelectorAll) { + jqLite.cleanData(element.querySelectorAll('*')); + } +} + +function jqLiteOff(element, type, fn, unsupported) { + if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); + + var expandoStore = jqLiteExpandoStore(element); + var events = expandoStore && expandoStore.events; + var handle = expandoStore && expandoStore.handle; + + if (!handle) return; //no listeners registered + + if (!type) { + for (type in events) { + if (type !== '$destroy') { + element.removeEventListener(type, handle); + } + delete events[type]; + } + } else { + + var removeHandler = function(type) { + var listenerFns = events[type]; + if (isDefined(fn)) { + arrayRemove(listenerFns || [], fn); + } + if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) { + element.removeEventListener(type, handle); + delete events[type]; + } + }; + + forEach(type.split(' '), function(type) { + removeHandler(type); + if (MOUSE_EVENT_MAP[type]) { + removeHandler(MOUSE_EVENT_MAP[type]); + } + }); + } +} + +function jqLiteRemoveData(element, name) { + var expandoId = element.ng339; + var expandoStore = expandoId && jqCache[expandoId]; + + if (expandoStore) { + if (name) { + delete expandoStore.data[name]; + return; + } + + if (expandoStore.handle) { + if (expandoStore.events.$destroy) { + expandoStore.handle({}, '$destroy'); + } + jqLiteOff(element); + } + delete jqCache[expandoId]; + element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it + } +} + + +function jqLiteExpandoStore(element, createIfNecessary) { + var expandoId = element.ng339, + expandoStore = expandoId && jqCache[expandoId]; + + if (createIfNecessary && !expandoStore) { + element.ng339 = expandoId = jqNextId(); + expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined}; + } + + return expandoStore; +} + + +function jqLiteData(element, key, value) { + if (jqLiteAcceptsData(element)) { + var prop; + + var isSimpleSetter = isDefined(value); + var isSimpleGetter = !isSimpleSetter && key && !isObject(key); + var massGetter = !key; + var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter); + var data = expandoStore && expandoStore.data; + + if (isSimpleSetter) { // data('key', value) + data[kebabToCamel(key)] = value; + } else { + if (massGetter) { // data() + return data; + } else { + if (isSimpleGetter) { // data('key') + // don't force creation of expandoStore if it doesn't exist yet + return data && data[kebabToCamel(key)]; + } else { // mass-setter: data({key1: val1, key2: val2}) + for (prop in key) { + data[kebabToCamel(prop)] = key[prop]; + } + } + } + } + } +} + +function jqLiteHasClass(element, selector) { + if (!element.getAttribute) return false; + return ((' ' + (element.getAttribute('class') || '') + ' ').replace(/[\n\t]/g, ' '). + indexOf(' ' + selector + ' ') > -1); +} + +function jqLiteRemoveClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') + .replace(/[\n\t]/g, ' '); + var newClasses = existingClasses; + + forEach(cssClasses.split(' '), function(cssClass) { + cssClass = trim(cssClass); + newClasses = newClasses.replace(' ' + cssClass + ' ', ' '); + }); + + if (newClasses !== existingClasses) { + element.setAttribute('class', trim(newClasses)); + } + } +} + +function jqLiteAddClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') + .replace(/[\n\t]/g, ' '); + var newClasses = existingClasses; + + forEach(cssClasses.split(' '), function(cssClass) { + cssClass = trim(cssClass); + if (newClasses.indexOf(' ' + cssClass + ' ') === -1) { + newClasses += cssClass + ' '; + } + }); + + if (newClasses !== existingClasses) { + element.setAttribute('class', trim(newClasses)); + } + } +} + + +function jqLiteAddNodes(root, elements) { + // THIS CODE IS VERY HOT. Don't make changes without benchmarking. + + if (elements) { + + // if a Node (the most common case) + if (elements.nodeType) { + root[root.length++] = elements; + } else { + var length = elements.length; + + // if an Array or NodeList and not a Window + if (typeof length === 'number' && elements.window !== elements) { + if (length) { + for (var i = 0; i < length; i++) { + root[root.length++] = elements[i]; + } + } + } else { + root[root.length++] = elements; + } + } + } +} + + +function jqLiteController(element, name) { + return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); +} + +function jqLiteInheritedData(element, name, value) { + // if element is the document object work with the html element instead + // this makes $(document).scope() possible + if (element.nodeType === NODE_TYPE_DOCUMENT) { + element = element.documentElement; + } + var names = isArray(name) ? name : [name]; + + while (element) { + for (var i = 0, ii = names.length; i < ii; i++) { + if (isDefined(value = jqLite.data(element, names[i]))) return value; + } + + // If dealing with a document fragment node with a host element, and no parent, use the host + // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM + // to lookup parent controllers. + element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host); + } +} + +function jqLiteEmpty(element) { + jqLiteDealoc(element, true); + while (element.firstChild) { + element.removeChild(element.firstChild); + } +} + +function jqLiteRemove(element, keepData) { + if (!keepData) jqLiteDealoc(element); + var parent = element.parentNode; + if (parent) parent.removeChild(element); +} + + +function jqLiteDocumentLoaded(action, win) { + win = win || window; + if (win.document.readyState === 'complete') { + // Force the action to be run async for consistent behavior + // from the action's point of view + // i.e. it will definitely not be in a $apply + win.setTimeout(action); + } else { + // No need to unbind this handler as load is only ever called once + jqLite(win).on('load', action); + } +} + +function jqLiteReady(fn) { + function trigger() { + window.document.removeEventListener('DOMContentLoaded', trigger); + window.removeEventListener('load', trigger); + fn(); + } + + // check if document is already loaded + if (window.document.readyState === 'complete') { + window.setTimeout(fn); + } else { + // We can not use jqLite since we are not done loading and jQuery could be loaded later. + + // Works for modern browsers and IE9 + window.document.addEventListener('DOMContentLoaded', trigger); + + // Fallback to window.onload for others + window.addEventListener('load', trigger); + } +} + +////////////////////////////////////////// +// Functions which are declared directly. +////////////////////////////////////////// +var JQLitePrototype = JQLite.prototype = { + ready: jqLiteReady, + toString: function() { + var value = []; + forEach(this, function(e) { value.push('' + e);}); + return '[' + value.join(', ') + ']'; + }, + + eq: function(index) { + return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); + }, + + length: 0, + push: push, + sort: [].sort, + splice: [].splice +}; + +////////////////////////////////////////// +// Functions iterating getter/setters. +// these functions return self on setter and +// value on get. +////////////////////////////////////////// +var BOOLEAN_ATTR = {}; +forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { + BOOLEAN_ATTR[lowercase(value)] = value; +}); +var BOOLEAN_ELEMENTS = {}; +forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { + BOOLEAN_ELEMENTS[value] = true; +}); +var ALIASED_ATTR = { + 'ngMinlength': 'minlength', + 'ngMaxlength': 'maxlength', + 'ngMin': 'min', + 'ngMax': 'max', + 'ngPattern': 'pattern', + 'ngStep': 'step' +}; + +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; +} + +function getAliasedAttrName(name) { + return ALIASED_ATTR[name]; +} + +forEach({ + data: jqLiteData, + removeData: jqLiteRemoveData, + hasData: jqLiteHasData, + cleanData: function jqLiteCleanData(nodes) { + for (var i = 0, ii = nodes.length; i < ii; i++) { + jqLiteRemoveData(nodes[i]); + } + } +}, function(fn, name) { + JQLite[name] = fn; +}); + +forEach({ + data: jqLiteData, + inheritedData: jqLiteInheritedData, + + scope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); + }, + + isolateScope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); + }, + + controller: jqLiteController, + + injector: function(element) { + return jqLiteInheritedData(element, '$injector'); + }, + + removeAttr: function(element, name) { + element.removeAttribute(name); + }, + + hasClass: jqLiteHasClass, + + css: function(element, name, value) { + name = cssKebabToCamel(name); + + if (isDefined(value)) { + element.style[name] = value; + } else { + return element.style[name]; + } + }, + + attr: function(element, name, value) { + var ret; + var nodeType = element.nodeType; + if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT || + !element.getAttribute) { + return; + } + + var lowercasedName = lowercase(name); + var isBooleanAttr = BOOLEAN_ATTR[lowercasedName]; + + if (isDefined(value)) { + // setter + + if (value === null || (value === false && isBooleanAttr)) { + element.removeAttribute(name); + } else { + element.setAttribute(name, isBooleanAttr ? lowercasedName : value); + } + } else { + // getter + + ret = element.getAttribute(name); + + if (isBooleanAttr && ret !== null) { + ret = lowercasedName; + } + // Normalize non-existing attributes to undefined (as jQuery). + return ret === null ? undefined : ret; + } + }, + + prop: function(element, name, value) { + if (isDefined(value)) { + element[name] = value; + } else { + return element[name]; + } + }, + + text: (function() { + getText.$dv = ''; + return getText; + + function getText(element, value) { + if (isUndefined(value)) { + var nodeType = element.nodeType; + return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : ''; + } + element.textContent = value; + } + })(), + + val: function(element, value) { + if (isUndefined(value)) { + if (element.multiple && nodeName_(element) === 'select') { + var result = []; + forEach(element.options, function(option) { + if (option.selected) { + result.push(option.value || option.text); + } + }); + return result; + } + return element.value; + } + element.value = value; + }, + + html: function(element, value) { + if (isUndefined(value)) { + return element.innerHTML; + } + jqLiteDealoc(element, true); + element.innerHTML = value; + }, + + empty: jqLiteEmpty +}, function(fn, name) { + /** + * Properties: writes return selection, reads return first value + */ + JQLite.prototype[name] = function(arg1, arg2) { + var i, key; + var nodeCount = this.length; + + // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it + // in a way that survives minification. + // jqLiteEmpty takes no arguments but is a setter. + if (fn !== jqLiteEmpty && + (isUndefined((fn.length === 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) { + if (isObject(arg1)) { + + // we are a write, but the object properties are the key/values + for (i = 0; i < nodeCount; i++) { + if (fn === jqLiteData) { + // data() takes the whole object in jQuery + fn(this[i], arg1); + } else { + for (key in arg1) { + fn(this[i], key, arg1[key]); + } + } + } + // return self for chaining + return this; + } else { + // we are a read, so read the first child. + // TODO: do we still need this? + var value = fn.$dv; + // Only if we have $dv do we iterate over all, otherwise it is just the first element. + var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount; + for (var j = 0; j < jj; j++) { + var nodeValue = fn(this[j], arg1, arg2); + value = value ? value + nodeValue : nodeValue; + } + return value; + } + } else { + // we are a write, so apply to all children + for (i = 0; i < nodeCount; i++) { + fn(this[i], arg1, arg2); + } + // return self for chaining + return this; + } + }; +}); + +function createEventHandler(element, events) { + var eventHandler = function(event, type) { + // jQuery specific api + event.isDefaultPrevented = function() { + return event.defaultPrevented; + }; + + var eventFns = events[type || event.type]; + var eventFnsLength = eventFns ? eventFns.length : 0; + + if (!eventFnsLength) return; + + if (isUndefined(event.immediatePropagationStopped)) { + var originalStopImmediatePropagation = event.stopImmediatePropagation; + event.stopImmediatePropagation = function() { + event.immediatePropagationStopped = true; + + if (event.stopPropagation) { + event.stopPropagation(); + } + + if (originalStopImmediatePropagation) { + originalStopImmediatePropagation.call(event); + } + }; + } + + event.isImmediatePropagationStopped = function() { + return event.immediatePropagationStopped === true; + }; + + // Some events have special handlers that wrap the real handler + var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper; + + // Copy event handlers in case event handlers array is modified during execution. + if ((eventFnsLength > 1)) { + eventFns = shallowCopy(eventFns); + } + + for (var i = 0; i < eventFnsLength; i++) { + if (!event.isImmediatePropagationStopped()) { + handlerWrapper(element, event, eventFns[i]); + } + } + }; + + // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all + // events on `element` + eventHandler.elem = element; + return eventHandler; +} + +function defaultHandlerWrapper(element, event, handler) { + handler.call(element, event); +} + +function specialMouseHandlerWrapper(target, event, handler) { + // Refer to jQuery's implementation of mouseenter & mouseleave + // Read about mouseenter and mouseleave: + // http://www.quirksmode.org/js/events_mouse.html#link8 + var related = event.relatedTarget; + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if (!related || (related !== target && !jqLiteContains.call(target, related))) { + handler.call(target, event); + } +} + +////////////////////////////////////////// +// Functions iterating traversal. +// These functions chain results into a single +// selector. +////////////////////////////////////////// +forEach({ + removeData: jqLiteRemoveData, + + on: function jqLiteOn(element, type, fn, unsupported) { + if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); + + // Do not add event handlers to non-elements because they will not be cleaned up. + if (!jqLiteAcceptsData(element)) { + return; + } + + var expandoStore = jqLiteExpandoStore(element, true); + var events = expandoStore.events; + var handle = expandoStore.handle; + + if (!handle) { + handle = expandoStore.handle = createEventHandler(element, events); + } + + // http://jsperf.com/string-indexof-vs-split + var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; + var i = types.length; + + var addHandler = function(type, specialHandlerWrapper, noEventListener) { + var eventFns = events[type]; + + if (!eventFns) { + eventFns = events[type] = []; + eventFns.specialHandlerWrapper = specialHandlerWrapper; + if (type !== '$destroy' && !noEventListener) { + element.addEventListener(type, handle); + } + } + + eventFns.push(fn); + }; + + while (i--) { + type = types[i]; + if (MOUSE_EVENT_MAP[type]) { + addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper); + addHandler(type, undefined, true); + } else { + addHandler(type); + } + } + }, + + off: jqLiteOff, + + one: function(element, type, fn) { + element = jqLite(element); + + //add the listener twice so that when it is called + //you can remove the original function and still be + //able to call element.off(ev, fn) normally + element.on(type, function onFn() { + element.off(type, fn); + element.off(type, onFn); + }); + element.on(type, fn); + }, + + replaceWith: function(element, replaceNode) { + var index, parent = element.parentNode; + jqLiteDealoc(element); + forEach(new JQLite(replaceNode), function(node) { + if (index) { + parent.insertBefore(node, index.nextSibling); + } else { + parent.replaceChild(node, element); + } + index = node; + }); + }, + + children: function(element) { + var children = []; + forEach(element.childNodes, function(element) { + if (element.nodeType === NODE_TYPE_ELEMENT) { + children.push(element); + } + }); + return children; + }, + + contents: function(element) { + return element.contentDocument || element.childNodes || []; + }, + + append: function(element, node) { + var nodeType = element.nodeType; + if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return; + + node = new JQLite(node); + + for (var i = 0, ii = node.length; i < ii; i++) { + var child = node[i]; + element.appendChild(child); + } + }, + + prepend: function(element, node) { + if (element.nodeType === NODE_TYPE_ELEMENT) { + var index = element.firstChild; + forEach(new JQLite(node), function(child) { + element.insertBefore(child, index); + }); + } + }, + + wrap: function(element, wrapNode) { + jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]); + }, + + remove: jqLiteRemove, + + detach: function(element) { + jqLiteRemove(element, true); + }, + + after: function(element, newElement) { + var index = element, parent = element.parentNode; + + if (parent) { + newElement = new JQLite(newElement); + + for (var i = 0, ii = newElement.length; i < ii; i++) { + var node = newElement[i]; + parent.insertBefore(node, index.nextSibling); + index = node; + } + } + }, + + addClass: jqLiteAddClass, + removeClass: jqLiteRemoveClass, + + toggleClass: function(element, selector, condition) { + if (selector) { + forEach(selector.split(' '), function(className) { + var classCondition = condition; + if (isUndefined(classCondition)) { + classCondition = !jqLiteHasClass(element, className); + } + (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); + }); + } + }, + + parent: function(element) { + var parent = element.parentNode; + return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null; + }, + + next: function(element) { + return element.nextElementSibling; + }, + + find: function(element, selector) { + if (element.getElementsByTagName) { + return element.getElementsByTagName(selector); + } else { + return []; + } + }, + + clone: jqLiteClone, + + triggerHandler: function(element, event, extraParameters) { + + var dummyEvent, eventFnsCopy, handlerArgs; + var eventName = event.type || event; + var expandoStore = jqLiteExpandoStore(element); + var events = expandoStore && expandoStore.events; + var eventFns = events && events[eventName]; + + if (eventFns) { + // Create a dummy event to pass to the handlers + dummyEvent = { + preventDefault: function() { this.defaultPrevented = true; }, + isDefaultPrevented: function() { return this.defaultPrevented === true; }, + stopImmediatePropagation: function() { this.immediatePropagationStopped = true; }, + isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; }, + stopPropagation: noop, + type: eventName, + target: element + }; + + // If a custom event was provided then extend our dummy event with it + if (event.type) { + dummyEvent = extend(dummyEvent, event); + } + + // Copy event handlers in case event handlers array is modified during execution. + eventFnsCopy = shallowCopy(eventFns); + handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; + + forEach(eventFnsCopy, function(fn) { + if (!dummyEvent.isImmediatePropagationStopped()) { + fn.apply(element, handlerArgs); + } + }); + } + } +}, function(fn, name) { + /** + * chaining functions + */ + JQLite.prototype[name] = function(arg1, arg2, arg3) { + var value; + + for (var i = 0, ii = this.length; i < ii; i++) { + if (isUndefined(value)) { + value = fn(this[i], arg1, arg2, arg3); + if (isDefined(value)) { + // any function which returns a value needs to be wrapped + value = jqLite(value); + } + } else { + jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); + } + } + return isDefined(value) ? value : this; + }; +}); + +// bind legacy bind/unbind to on/off +JQLite.prototype.bind = JQLite.prototype.on; +JQLite.prototype.unbind = JQLite.prototype.off; + + +// Provider for private $$jqLite service +/** @this */ +function $$jqLiteProvider() { + this.$get = function $$jqLite() { + return extend(JQLite, { + hasClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteHasClass(node, classes); + }, + addClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteAddClass(node, classes); + }, + removeClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteRemoveClass(node, classes); + } + }); + }; +} + +/** + * Computes a hash of an 'obj'. + * Hash of a: + * string is string + * number is number as string + * object is either result of calling $$hashKey function on the object or uniquely generated id, + * that is also assigned to the $$hashKey property of the object. + * + * @param obj + * @returns {string} hash string such that the same input will have the same hash string. + * The resulting string key is in 'type:hashKey' format. + */ +function hashKey(obj, nextUidFn) { + var key = obj && obj.$$hashKey; + + if (key) { + if (typeof key === 'function') { + key = obj.$$hashKey(); + } + return key; + } + + var objType = typeof obj; + if (objType === 'function' || (objType === 'object' && obj !== null)) { + key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)(); + } else { + key = objType + ':' + obj; + } + + return key; +} + +// A minimal ES2015 Map implementation. +// Should be bug/feature equivalent to the native implementations of supported browsers +// (for the features required in Angular). +// See https://kangax.github.io/compat-table/es6/#test-Map +var nanKey = Object.create(null); +function NgMapShim() { + this._keys = []; + this._values = []; + this._lastKey = NaN; + this._lastIndex = -1; +} +NgMapShim.prototype = { + _idx: function(key) { + if (key === this._lastKey) { + return this._lastIndex; + } + this._lastKey = key; + this._lastIndex = this._keys.indexOf(key); + return this._lastIndex; + }, + _transformKey: function(key) { + return isNumberNaN(key) ? nanKey : key; + }, + get: function(key) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx !== -1) { + return this._values[idx]; + } + }, + set: function(key, value) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + idx = this._lastIndex = this._keys.length; + } + this._keys[idx] = key; + this._values[idx] = value; + + // Support: IE11 + // Do not `return this` to simulate the partial IE11 implementation + }, + delete: function(key) { + key = this._transformKey(key); + var idx = this._idx(key); + if (idx === -1) { + return false; + } + this._keys.splice(idx, 1); + this._values.splice(idx, 1); + this._lastKey = NaN; + this._lastIndex = -1; + return true; + } +}; + +// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations +// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map` +// implementations get more stable, we can reconsider switching to `window.Map` (when available). +var NgMap = NgMapShim; + +var $$MapProvider = [/** @this */function() { + this.$get = [function() { + return NgMap; + }]; +}]; + +/** + * @ngdoc function + * @module ng + * @name angular.injector + * @kind function + * + * @description + * Creates an injector object that can be used for retrieving services as well as for + * dependency injection (see {@link guide/di dependency injection}). + * + * @param {Array.} modules A list of module functions or their aliases. See + * {@link angular.module}. The `ng` module must be explicitly added. + * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which + * disallows argument name annotation inference. + * @returns {injector} Injector object. See {@link auto.$injector $injector}. + * + * @example + * Typical usage + * ```js + * // create an injector + * var $injector = angular.injector(['ng']); + * + * // use the injector to kick off your application + * // use the type inference to auto inject arguments, or use implicit injection + * $injector.invoke(function($rootScope, $compile, $document) { + * $compile($document)($rootScope); + * $rootScope.$digest(); + * }); + * ``` + * + * Sometimes you want to get access to the injector of a currently running AngularJS app + * from outside AngularJS. Perhaps, you want to inject and compile some markup after the + * application has been bootstrapped. You can do this using the extra `injector()` added + * to JQuery/jqLite elements. See {@link angular.element}. + * + * *This is fairly rare but could be the case if a third party library is injecting the + * markup.* + * + * In the following example a new block of HTML containing a `ng-controller` + * directive is added to the end of the document body by JQuery. We then compile and link + * it into the current AngularJS scope. + * + * ```js + * var $div = $('
    {{content.label}}
    '); + * $(document.body).append($div); + * + * angular.element(document).injector().invoke(function($compile) { + * var scope = angular.element($div).scope(); + * $compile($div)(scope); + * }); + * ``` + */ + + +/** + * @ngdoc module + * @name auto + * @installation + * @description + * + * Implicit module which gets automatically added to each {@link auto.$injector $injector}. + */ + +var ARROW_ARG = /^([^(]+?)=>/; +var FN_ARGS = /^[^(]*\(\s*([^)]*)\)/m; +var FN_ARG_SPLIT = /,/; +var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; +var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; +var $injectorMinErr = minErr('$injector'); + +function stringifyFn(fn) { + return Function.prototype.toString.call(fn); +} + +function extractArgs(fn) { + var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''), + args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS); + return args; +} + +function anonFn(fn) { + // For anonymous functions, showing at the very least the function signature can help in + // debugging. + var args = extractArgs(fn); + if (args) { + return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; + } + return 'fn'; +} + +function annotate(fn, strictDi, name) { + var $inject, + argDecl, + last; + + if (typeof fn === 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + if (fn.length) { + if (strictDi) { + if (!isString(name) || !name) { + name = fn.name || anonFn(fn); + } + throw $injectorMinErr('strictdi', + '{0} is not using explicit annotation and cannot be invoked in strict mode', name); + } + argDecl = extractArgs(fn); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) { + arg.replace(FN_ARG, function(all, underscore, name) { + $inject.push(name); + }); + }); + } + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn'); + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); + } + return $inject; +} + +/////////////////////////////////////// + +/** + * @ngdoc service + * @name $injector + * + * @description + * + * `$injector` is used to retrieve object instances as defined by + * {@link auto.$provide provider}, instantiate types, invoke methods, + * and load modules. + * + * The following always holds true: + * + * ```js + * var $injector = angular.injector(); + * expect($injector.get('$injector')).toBe($injector); + * expect($injector.invoke(function($injector) { + * return $injector; + * })).toBe($injector); + * ``` + * + * ## Injection Function Annotation + * + * JavaScript does not have annotations, and annotations are needed for dependency injection. The + * following are all valid ways of annotating function with injection arguments and are equivalent. + * + * ```js + * // inferred (only works if code not minified/obfuscated) + * $injector.invoke(function(serviceA){}); + * + * // annotated + * function explicit(serviceA) {}; + * explicit.$inject = ['serviceA']; + * $injector.invoke(explicit); + * + * // inline + * $injector.invoke(['serviceA', function(serviceA){}]); + * ``` + * + * ### Inference + * + * In JavaScript calling `toString()` on a function returns the function definition. The definition + * can then be parsed and the function arguments can be extracted. This method of discovering + * annotations is disallowed when the injector is in strict mode. + * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the + * argument names. + * + * ### `$inject` Annotation + * By adding an `$inject` property onto a function the injection parameters can be specified. + * + * ### Inline + * As an array of injection names, where the last item in the array is the function to call. + */ + +/** + * @ngdoc property + * @name $injector#modules + * @type {Object} + * @description + * A hash containing all the modules that have been loaded into the + * $injector. + * + * You can use this property to find out information about a module via the + * {@link angular.Module#info `myModule.info(...)`} method. + * + * For example: + * + * ``` + * var info = $injector.modules['ngAnimate'].info(); + * ``` + * + * **Do not use this property to attempt to modify the modules after the application + * has been bootstrapped.** + */ + + +/** + * @ngdoc method + * @name $injector#get + * + * @description + * Return an instance of the service. + * + * @param {string} name The name of the instance to retrieve. + * @param {string=} caller An optional string to provide the origin of the function call for error messages. + * @return {*} The instance. + */ + +/** + * @ngdoc method + * @name $injector#invoke + * + * @description + * Invoke the method and supply the method arguments from the `$injector`. + * + * @param {Function|Array.} fn The injectable function to invoke. Function parameters are + * injected according to the {@link guide/di $inject Annotation} rules. + * @param {Object=} self The `this` for the invoked method. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {*} the value returned by the invoked `fn` function. + */ + +/** + * @ngdoc method + * @name $injector#has + * + * @description + * Allows the user to query if the particular service exists. + * + * @param {string} name Name of the service to query. + * @returns {boolean} `true` if injector has given service. + */ + +/** + * @ngdoc method + * @name $injector#instantiate + * @description + * Create a new instance of JS type. The method takes a constructor function, invokes the new + * operator, and supplies all of the arguments to the constructor function as specified by the + * constructor annotation. + * + * @param {Function} Type Annotated constructor function. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {Object} new instance of `Type`. + */ + +/** + * @ngdoc method + * @name $injector#annotate + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is + * used by the injector to determine which services need to be injected into the function when the + * function is invoked. There are three ways in which the function can be annotated with the needed + * dependencies. + * + * #### Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done + * by converting the function into a string using `toString()` method and extracting the argument + * names. + * ```js + * // Given + * function MyController($scope, $route) { + * // ... + * } + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * You can disallow this method by using strict injection mode. + * + * This method does not work with code minification / obfuscation. For this reason the following + * annotation strategies are supported. + * + * #### The `$inject` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings + * represent names of services to be injected into the function. + * ```js + * // Given + * var MyController = function(obfuscatedScope, obfuscatedRoute) { + * // ... + * } + * // Define function dependencies + * MyController['$inject'] = ['$scope', '$route']; + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * #### The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property + * is very inconvenient. In these situations using the array notation to specify the dependencies in + * a way that survives minification is a better choice: + * + * ```js + * // We wish to write this (not minification / obfuscation safe) + * injector.invoke(function($compile, $rootScope) { + * // ... + * }); + * + * // We are forced to write break inlining + * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { + * // ... + * }; + * tmpFn.$inject = ['$compile', '$rootScope']; + * injector.invoke(tmpFn); + * + * // To better support inline function the inline annotation is supported + * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { + * // ... + * }]); + * + * // Therefore + * expect(injector.annotate( + * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) + * ).toEqual(['$compile', '$rootScope']); + * ``` + * + * @param {Function|Array.} fn Function for which dependent service names need to + * be retrieved as described above. + * + * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. + * + * @returns {Array.} The names of the services which the function requires. + */ +/** + * @ngdoc method + * @name $injector#loadNewModules + * + * @description + * + * **This is a dangerous API, which you use at your own risk!** + * + * Add the specified modules to the current injector. + * + * This method will add each of the injectables to the injector and execute all of the config and run + * blocks for each module passed to the method. + * + * If a module has already been loaded into the injector then it will not be loaded again. + * + * * The application developer is responsible for loading the code containing the modules; and for + * ensuring that lazy scripts are not downloaded and executed more often that desired. + * * Previously compiled HTML will not be affected by newly loaded directives, filters and components. + * * Modules cannot be unloaded. + * + * You can use {@link $injector#modules `$injector.modules`} to check whether a module has been loaded + * into the injector, which may indicate whether the script has been executed already. + * + * @example + * Here is an example of loading a bundle of modules, with a utility method called `getScript`: + * + * ```javascript + * app.factory('loadModule', function($injector) { + * return function loadModule(moduleName, bundleUrl) { + * return getScript(bundleUrl).then(function() { $injector.loadNewModules([moduleName]); }); + * }; + * }) + * ``` + * + * @param {Array=} mods an array of modules to load into the application. + * Each item in the array should be the name of a predefined module or a (DI annotated) + * function that will be invoked by the injector as a `config` block. + * See: {@link angular.module modules} + */ + + +/** + * @ngdoc service + * @name $provide + * + * @description + * + * The {@link auto.$provide $provide} service has a number of methods for registering components + * with the {@link auto.$injector $injector}. Many of these functions are also exposed on + * {@link angular.Module}. + * + * An AngularJS **service** is a singleton object created by a **service factory**. These **service + * factories** are functions which, in turn, are created by a **service provider**. + * The **service providers** are constructor functions. When instantiated they must contain a + * property called `$get`, which holds the **service factory** function. + * + * When you request a service, the {@link auto.$injector $injector} is responsible for finding the + * correct **service provider**, instantiating it and then calling its `$get` **service factory** + * function to get the instance of the **service**. + * + * Often services have no configuration options and there is no need to add methods to the service + * provider. The provider will be no more than a constructor function with a `$get` property. For + * these cases the {@link auto.$provide $provide} service has additional helper methods to register + * services without specifying a provider. + * + * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the + * {@link auto.$injector $injector} + * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by + * providers and services. + * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by + * services, not providers. + * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function** + * that will be wrapped in a **service provider** object, whose `$get` property will contain the + * given factory function. + * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function** + * that will be wrapped in a **service provider** object, whose `$get` property will instantiate + * a new object using the given constructor function. + * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that + * will be able to modify or replace the implementation of another service. + * + * See the individual methods for more information and examples. + */ + +/** + * @ngdoc method + * @name $provide#provider + * @description + * + * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions + * are constructor functions, whose instances are responsible for "providing" a factory for a + * service. + * + * Service provider names start with the name of the service they provide followed by `Provider`. + * For example, the {@link ng.$log $log} service has a provider called + * {@link ng.$logProvider $logProvider}. + * + * Service provider objects can have additional methods which allow configuration of the provider + * and its service. Importantly, you can configure what kind of service is created by the `$get` + * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a + * method {@link ng.$logProvider#debugEnabled debugEnabled} + * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the + * console or not. + * + * It is possible to inject other providers into the provider function, + * but the injected provider must have been defined before the one that requires it. + * + * @param {string} name The name of the instance. NOTE: the provider will be available under `name + + 'Provider'` key. + * @param {(Object|function())} provider If the provider is: + * + * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using + * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. + * - `Constructor`: a new instance of the provider will be created using + * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. + * + * @returns {Object} registered provider instance + + * @example + * + * The following example shows how to create a simple event tracking service and register it using + * {@link auto.$provide#provider $provide.provider()}. + * + * ```js + * // Define the eventTracker provider + * function EventTrackerProvider() { + * var trackingUrl = '/track'; + * + * // A provider method for configuring where the tracked events should been saved + * this.setTrackingUrl = function(url) { + * trackingUrl = url; + * }; + * + * // The service factory function + * this.$get = ['$http', function($http) { + * var trackedEvents = {}; + * return { + * // Call this to track an event + * event: function(event) { + * var count = trackedEvents[event] || 0; + * count += 1; + * trackedEvents[event] = count; + * return count; + * }, + * // Call this to save the tracked events to the trackingUrl + * save: function() { + * $http.post(trackingUrl, trackedEvents); + * } + * }; + * }]; + * } + * + * describe('eventTracker', function() { + * var postSpy; + * + * beforeEach(module(function($provide) { + * // Register the eventTracker provider + * $provide.provider('eventTracker', EventTrackerProvider); + * })); + * + * beforeEach(module(function(eventTrackerProvider) { + * // Configure eventTracker provider + * eventTrackerProvider.setTrackingUrl('/custom-track'); + * })); + * + * it('tracks events', inject(function(eventTracker) { + * expect(eventTracker.event('login')).toEqual(1); + * expect(eventTracker.event('login')).toEqual(2); + * })); + * + * it('saves to the tracking url', inject(function(eventTracker, $http) { + * postSpy = spyOn($http, 'post'); + * eventTracker.event('login'); + * eventTracker.save(); + * expect(postSpy).toHaveBeenCalled(); + * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); + * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); + * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); + * })); + * }); + * ``` + */ + +/** + * @ngdoc method + * @name $provide#factory + * @description + * + * Register a **service factory**, which will be called to return the service instance. + * This is short for registering a service where its provider consists of only a `$get` property, + * which is the given service factory function. + * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to + * configure your service in a provider. + * + * @param {string} name The name of the instance. + * @param {Function|Array.} $getFn The injectable $getFn for the instance creation. + * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service + * ```js + * $provide.factory('ping', ['$http', function($http) { + * return function ping() { + * return $http.send('/ping'); + * }; + * }]); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#service + * @description + * + * Register a **service constructor**, which will be invoked with `new` to create the service + * instance. + * This is short for registering a service where its provider's `$get` property is a factory + * function that returns an instance instantiated by the injector from the service constructor + * function. + * + * Internally it looks a bit like this: + * + * ``` + * { + * $get: function() { + * return $injector.instantiate(constructor); + * } + * } + * ``` + * + * + * You should use {@link auto.$provide#service $provide.service(class)} if you define your service + * as a type/class. + * + * @param {string} name The name of the instance. + * @param {Function|Array.} constructor An injectable class (constructor function) + * that will be instantiated. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service using + * {@link auto.$provide#service $provide.service(class)}. + * ```js + * var Ping = function($http) { + * this.$http = $http; + * }; + * + * Ping.$inject = ['$http']; + * + * Ping.prototype.send = function() { + * return this.$http.get('/ping'); + * }; + * $provide.service('ping', Ping); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping.send(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#value + * @description + * + * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a + * number, an array, an object or a function. This is short for registering a service where its + * provider's `$get` property is a factory function that takes no arguments and returns the **value + * service**. That also means it is not possible to inject other services into a value service. + * + * Value services are similar to constant services, except that they cannot be injected into a + * module configuration function (see {@link angular.Module#config}) but they can be overridden by + * an AngularJS {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the instance. + * @param {*} value The value. + * @returns {Object} registered provider instance + * + * @example + * Here are some examples of creating value services. + * ```js + * $provide.value('ADMIN_USER', 'admin'); + * + * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); + * + * $provide.value('halfOf', function(value) { + * return value / 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#constant + * @description + * + * Register a **constant service** with the {@link auto.$injector $injector}, such as a string, + * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not + * possible to inject other services into a constant. + * + * But unlike {@link auto.$provide#value value}, a constant can be + * injected into a module configuration function (see {@link angular.Module#config}) and it cannot + * be overridden by an AngularJS {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the constant. + * @param {*} value The constant value. + * @returns {Object} registered instance + * + * @example + * Here a some examples of creating constants: + * ```js + * $provide.constant('SHARD_HEIGHT', 306); + * + * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); + * + * $provide.constant('double', function(value) { + * return value * 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#decorator + * @description + * + * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function + * intercepts the creation of a service, allowing it to override or modify the behavior of the + * service. The return value of the decorator function may be the original service, or a new service + * that replaces (or wraps and delegates to) the original service. + * + * You can find out more about using decorators in the {@link guide/decorators} guide. + * + * @param {string} name The name of the service to decorate. + * @param {Function|Array.} decorator This function will be invoked when the service needs to be + * provided and should return the decorated service instance. The function is called using + * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. + * Local injection arguments: + * + * * `$delegate` - The original service instance, which can be replaced, monkey patched, configured, + * decorated or delegated to. + * + * @example + * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting + * calls to {@link ng.$log#error $log.warn()}. + * ```js + * $provide.decorator('$log', ['$delegate', function($delegate) { + * $delegate.warn = $delegate.error; + * return $delegate; + * }]); + * ``` + */ + + +function createInjector(modulesToLoad, strictDi) { + strictDi = (strictDi === true); + var INSTANTIATING = {}, + providerSuffix = 'Provider', + path = [], + loadedModules = new NgMap(), + providerCache = { + $provide: { + provider: supportObject(provider), + factory: supportObject(factory), + service: supportObject(service), + value: supportObject(value), + constant: supportObject(constant), + decorator: decorator + } + }, + providerInjector = (providerCache.$injector = + createInternalInjector(providerCache, function(serviceName, caller) { + if (angular.isString(caller)) { + path.push(caller); + } + throw $injectorMinErr('unpr', 'Unknown provider: {0}', path.join(' <- ')); + })), + instanceCache = {}, + protoInstanceInjector = + createInternalInjector(instanceCache, function(serviceName, caller) { + var provider = providerInjector.get(serviceName + providerSuffix, caller); + return instanceInjector.invoke( + provider.$get, provider, undefined, serviceName); + }), + instanceInjector = protoInstanceInjector; + + providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) }; + instanceInjector.modules = providerInjector.modules = createMap(); + var runBlocks = loadModules(modulesToLoad); + instanceInjector = protoInstanceInjector.get('$injector'); + instanceInjector.strictDi = strictDi; + forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); }); + + instanceInjector.loadNewModules = function(mods) { + forEach(loadModules(mods), function(fn) { if (fn) instanceInjector.invoke(fn); }); + }; + + + return instanceInjector; + + //////////////////////////////////// + // $provider + //////////////////////////////////// + + function supportObject(delegate) { + return function(key, value) { + if (isObject(key)) { + forEach(key, reverseParams(delegate)); + } else { + return delegate(key, value); + } + }; + } + + function provider(name, provider_) { + assertNotHasOwnProperty(name, 'service'); + if (isFunction(provider_) || isArray(provider_)) { + provider_ = providerInjector.instantiate(provider_); + } + if (!provider_.$get) { + throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name); + } + return (providerCache[name + providerSuffix] = provider_); + } + + function enforceReturnValue(name, factory) { + return /** @this */ function enforcedReturnValue() { + var result = instanceInjector.invoke(factory, this); + if (isUndefined(result)) { + throw $injectorMinErr('undef', 'Provider \'{0}\' must return a value from $get factory method.', name); + } + return result; + }; + } + + function factory(name, factoryFn, enforce) { + return provider(name, { + $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn + }); + } + + function service(name, constructor) { + return factory(name, ['$injector', function($injector) { + return $injector.instantiate(constructor); + }]); + } + + function value(name, val) { return factory(name, valueFn(val), false); } + + function constant(name, value) { + assertNotHasOwnProperty(name, 'constant'); + providerCache[name] = value; + instanceCache[name] = value; + } + + function decorator(serviceName, decorFn) { + var origProvider = providerInjector.get(serviceName + providerSuffix), + orig$get = origProvider.$get; + + origProvider.$get = function() { + var origInstance = instanceInjector.invoke(orig$get, origProvider); + return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); + }; + } + + //////////////////////////////////// + // Module Loading + //////////////////////////////////// + function loadModules(modulesToLoad) { + assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array'); + var runBlocks = [], moduleFn; + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.set(module, true); + + function runInvokeQueue(queue) { + var i, ii; + for (i = 0, ii = queue.length; i < ii; i++) { + var invokeArgs = queue[i], + provider = providerInjector.get(invokeArgs[0]); + + provider[invokeArgs[1]].apply(provider, invokeArgs[2]); + } + } + + try { + if (isString(module)) { + moduleFn = angularModule(module); + instanceInjector.modules[module] = moduleFn; + runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); + runInvokeQueue(moduleFn._invokeQueue); + runInvokeQueue(moduleFn._configBlocks); + } else if (isFunction(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else if (isArray(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else { + assertArgFn(module, 'module'); + } + } catch (e) { + if (isArray(module)) { + module = module[module.length - 1]; + } + if (e.message && e.stack && e.stack.indexOf(e.message) === -1) { + // Safari & FF's stack traces don't contain error.message content + // unlike those of Chrome and IE + // So if stack doesn't contain message, we create a new string that contains both. + // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. + // eslint-disable-next-line no-ex-assign + e = e.message + '\n' + e.stack; + } + throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}', + module, e.stack || e.message || e); + } + }); + return runBlocks; + } + + //////////////////////////////////// + // internal Injector + //////////////////////////////////// + + function createInternalInjector(cache, factory) { + + function getService(serviceName, caller) { + if (cache.hasOwnProperty(serviceName)) { + if (cache[serviceName] === INSTANTIATING) { + throw $injectorMinErr('cdep', 'Circular dependency found: {0}', + serviceName + ' <- ' + path.join(' <- ')); + } + return cache[serviceName]; + } else { + try { + path.unshift(serviceName); + cache[serviceName] = INSTANTIATING; + cache[serviceName] = factory(serviceName, caller); + return cache[serviceName]; + } catch (err) { + if (cache[serviceName] === INSTANTIATING) { + delete cache[serviceName]; + } + throw err; + } finally { + path.shift(); + } + } + } + + + function injectionArgs(fn, locals, serviceName) { + var args = [], + $inject = createInjector.$$annotate(fn, strictDi, serviceName); + + for (var i = 0, length = $inject.length; i < length; i++) { + var key = $inject[i]; + if (typeof key !== 'string') { + throw $injectorMinErr('itkn', + 'Incorrect injection token! Expected service name as string, got {0}', key); + } + args.push(locals && locals.hasOwnProperty(key) ? locals[key] : + getService(key, serviceName)); + } + return args; + } + + function isClass(func) { + // Support: IE 9-11 only + // IE 9-11 do not support classes and IE9 leaks with the code below. + if (msie || typeof func !== 'function') { + return false; + } + var result = func.$$ngIsClass; + if (!isBoolean(result)) { + // Support: Edge 12-13 only + // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/ + result = func.$$ngIsClass = /^(?:class\b|constructor\()/.test(stringifyFn(func)); + } + return result; + } + + function invoke(fn, self, locals, serviceName) { + if (typeof locals === 'string') { + serviceName = locals; + locals = null; + } + + var args = injectionArgs(fn, locals, serviceName); + if (isArray(fn)) { + fn = fn[fn.length - 1]; + } + + if (!isClass(fn)) { + // http://jsperf.com/angularjs-invoke-apply-vs-switch + // #5388 + return fn.apply(self, args); + } else { + args.unshift(null); + return new (Function.prototype.bind.apply(fn, args))(); + } + } + + + function instantiate(Type, locals, serviceName) { + // Check if Type is annotated and use just the given function at n-1 as parameter + // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); + var ctor = (isArray(Type) ? Type[Type.length - 1] : Type); + var args = injectionArgs(Type, locals, serviceName); + // Empty object at position 0 is ignored for invocation with `new`, but required. + args.unshift(null); + return new (Function.prototype.bind.apply(ctor, args))(); + } + + + return { + invoke: invoke, + instantiate: instantiate, + get: getService, + annotate: createInjector.$$annotate, + has: function(name) { + return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); + } + }; + } +} + +createInjector.$$annotate = annotate; + +/** + * @ngdoc provider + * @name $anchorScrollProvider + * @this + * + * @description + * Use `$anchorScrollProvider` to disable automatic scrolling whenever + * {@link ng.$location#hash $location.hash()} changes. + */ +function $AnchorScrollProvider() { + + var autoScrollingEnabled = true; + + /** + * @ngdoc method + * @name $anchorScrollProvider#disableAutoScrolling + * + * @description + * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to + * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.
    + * Use this method to disable automatic scrolling. + * + * If automatic scrolling is disabled, one must explicitly call + * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the + * current hash. + */ + this.disableAutoScrolling = function() { + autoScrollingEnabled = false; + }; + + /** + * @ngdoc service + * @name $anchorScroll + * @kind function + * @requires $window + * @requires $location + * @requires $rootScope + * + * @description + * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the + * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified + * in the + * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document). + * + * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to + * match any anchor whenever it changes. This can be disabled by calling + * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. + * + * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a + * vertical scroll-offset (either fixed or dynamic). + * + * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of + * {@link ng.$location#hash $location.hash()} will be used. + * + * @property {(number|function|jqLite)} yOffset + * If set, specifies a vertical scroll-offset. This is often useful when there are fixed + * positioned elements at the top of the page, such as navbars, headers etc. + * + * `yOffset` can be specified in various ways: + * - **number**: A fixed number of pixels to be used as offset.

    + * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return + * a number representing the offset (in pixels).

    + * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from + * the top of the page to the element's bottom will be used as offset.
    + * **Note**: The element will be taken into account only as long as its `position` is set to + * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust + * their height and/or positioning according to the viewport's size. + * + *
    + *
    + * In order for `yOffset` to work properly, scrolling should take place on the document's root and + * not some child element. + *
    + * + * @example + + +
    + Go to bottom + You're at the bottom! +
    +
    + + angular.module('anchorScrollExample', []) + .controller('ScrollController', ['$scope', '$location', '$anchorScroll', + function($scope, $location, $anchorScroll) { + $scope.gotoBottom = function() { + // set the location.hash to the id of + // the element you wish to scroll to. + $location.hash('bottom'); + + // call $anchorScroll() + $anchorScroll(); + }; + }]); + + + #scrollArea { + height: 280px; + overflow: auto; + } + + #bottom { + display: block; + margin-top: 2000px; + } + +
    + * + *
    + * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). + * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. + * + * @example + + + +
    + Anchor {{x}} of 5 +
    +
    + + angular.module('anchorScrollOffsetExample', []) + .run(['$anchorScroll', function($anchorScroll) { + $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels + }]) + .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', + function($anchorScroll, $location, $scope) { + $scope.gotoAnchor = function(x) { + var newHash = 'anchor' + x; + if ($location.hash() !== newHash) { + // set the $location.hash to `newHash` and + // $anchorScroll will automatically scroll to it + $location.hash('anchor' + x); + } else { + // call $anchorScroll() explicitly, + // since $location.hash hasn't changed + $anchorScroll(); + } + }; + } + ]); + + + body { + padding-top: 50px; + } + + .anchor { + border: 2px dashed DarkOrchid; + padding: 10px 10px 200px 10px; + } + + .fixed-header { + background-color: rgba(0, 0, 0, 0.2); + height: 50px; + position: fixed; + top: 0; left: 0; right: 0; + } + + .fixed-header > a { + display: inline-block; + margin: 5px 15px; + } + +
    + */ + this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { + var document = $window.document; + + // Helper function to get first anchor from a NodeList + // (using `Array#some()` instead of `angular#forEach()` since it's more performant + // and working in all supported browsers.) + function getFirstAnchor(list) { + var result = null; + Array.prototype.some.call(list, function(element) { + if (nodeName_(element) === 'a') { + result = element; + return true; + } + }); + return result; + } + + function getYOffset() { + + var offset = scroll.yOffset; + + if (isFunction(offset)) { + offset = offset(); + } else if (isElement(offset)) { + var elem = offset[0]; + var style = $window.getComputedStyle(elem); + if (style.position !== 'fixed') { + offset = 0; + } else { + offset = elem.getBoundingClientRect().bottom; + } + } else if (!isNumber(offset)) { + offset = 0; + } + + return offset; + } + + function scrollTo(elem) { + if (elem) { + elem.scrollIntoView(); + + var offset = getYOffset(); + + if (offset) { + // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. + // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the + // top of the viewport. + // + // IF the number of pixels from the top of `elem` to the end of the page's content is less + // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some + // way down the page. + // + // This is often the case for elements near the bottom of the page. + // + // In such cases we do not need to scroll the whole `offset` up, just the difference between + // the top of the element and the offset, which is enough to align the top of `elem` at the + // desired position. + var elemTop = elem.getBoundingClientRect().top; + $window.scrollBy(0, elemTop - offset); + } + } else { + $window.scrollTo(0, 0); + } + } + + function scroll(hash) { + // Allow numeric hashes + hash = isString(hash) ? hash : isNumber(hash) ? hash.toString() : $location.hash(); + var elm; + + // empty hash, scroll to the top of the page + if (!hash) scrollTo(null); + + // element with given id + else if ((elm = document.getElementById(hash))) scrollTo(elm); + + // first anchor with given name :-D + else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); + + // no element and hash === 'top', scroll to the top of the page + else if (hash === 'top') scrollTo(null); + } + + // does not scroll when user clicks on anchor link that is currently on + // (no url change, no $location.hash() change), browser native does scroll + if (autoScrollingEnabled) { + $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, + function autoScrollWatchAction(newVal, oldVal) { + // skip the initial scroll if $location.hash is empty + if (newVal === oldVal && newVal === '') return; + + jqLiteDocumentLoaded(function() { + $rootScope.$evalAsync(scroll); + }); + }); + } + + return scroll; + }]; +} + +var $animateMinErr = minErr('$animate'); +var ELEMENT_NODE = 1; +var NG_ANIMATE_CLASSNAME = 'ng-animate'; + +function mergeClasses(a,b) { + if (!a && !b) return ''; + if (!a) return b; + if (!b) return a; + if (isArray(a)) a = a.join(' '); + if (isArray(b)) b = b.join(' '); + return a + ' ' + b; +} + +function extractElementNode(element) { + for (var i = 0; i < element.length; i++) { + var elm = element[i]; + if (elm.nodeType === ELEMENT_NODE) { + return elm; + } + } +} + +function splitClasses(classes) { + if (isString(classes)) { + classes = classes.split(' '); + } + + // Use createMap() to prevent class assumptions involving property names in + // Object.prototype + var obj = createMap(); + forEach(classes, function(klass) { + // sometimes the split leaves empty string values + // incase extra spaces were applied to the options + if (klass.length) { + obj[klass] = true; + } + }); + return obj; +} + +// if any other type of options value besides an Object value is +// passed into the $animate.method() animation then this helper code +// will be run which will ignore it. While this patch is not the +// greatest solution to this, a lot of existing plugins depend on +// $animate to either call the callback (< 1.2) or return a promise +// that can be changed. This helper function ensures that the options +// are wiped clean incase a callback function is provided. +function prepareAnimateOptions(options) { + return isObject(options) + ? options + : {}; +} + +var $$CoreAnimateJsProvider = /** @this */ function() { + this.$get = noop; +}; + +// this is prefixed with Core since it conflicts with +// the animateQueueProvider defined in ngAnimate/animateQueue.js +var $$CoreAnimateQueueProvider = /** @this */ function() { + var postDigestQueue = new NgMap(); + var postDigestElements = []; + + this.$get = ['$$AnimateRunner', '$rootScope', + function($$AnimateRunner, $rootScope) { + return { + enabled: noop, + on: noop, + off: noop, + pin: noop, + + push: function(element, event, options, domOperation) { + if (domOperation) { + domOperation(); + } + + options = options || {}; + if (options.from) { + element.css(options.from); + } + if (options.to) { + element.css(options.to); + } + + if (options.addClass || options.removeClass) { + addRemoveClassesPostDigest(element, options.addClass, options.removeClass); + } + + var runner = new $$AnimateRunner(); + + // since there are no animations to run the runner needs to be + // notified that the animation call is complete. + runner.complete(); + return runner; + } + }; + + + function updateData(data, classes, value) { + var changed = false; + if (classes) { + classes = isString(classes) ? classes.split(' ') : + isArray(classes) ? classes : []; + forEach(classes, function(className) { + if (className) { + changed = true; + data[className] = value; + } + }); + } + return changed; + } + + function handleCSSClassChanges() { + forEach(postDigestElements, function(element) { + var data = postDigestQueue.get(element); + if (data) { + var existing = splitClasses(element.attr('class')); + var toAdd = ''; + var toRemove = ''; + forEach(data, function(status, className) { + var hasClass = !!existing[className]; + if (status !== hasClass) { + if (status) { + toAdd += (toAdd.length ? ' ' : '') + className; + } else { + toRemove += (toRemove.length ? ' ' : '') + className; + } + } + }); + + forEach(element, function(elm) { + if (toAdd) { + jqLiteAddClass(elm, toAdd); + } + if (toRemove) { + jqLiteRemoveClass(elm, toRemove); + } + }); + postDigestQueue.delete(element); + } + }); + postDigestElements.length = 0; + } + + + function addRemoveClassesPostDigest(element, add, remove) { + var data = postDigestQueue.get(element) || {}; + + var classesAdded = updateData(data, add, true); + var classesRemoved = updateData(data, remove, false); + + if (classesAdded || classesRemoved) { + + postDigestQueue.set(element, data); + postDigestElements.push(element); + + if (postDigestElements.length === 1) { + $rootScope.$$postDigest(handleCSSClassChanges); + } + } + } + }]; +}; + +/** + * @ngdoc provider + * @name $animateProvider + * + * @description + * Default implementation of $animate that doesn't perform any animations, instead just + * synchronously performs DOM updates and resolves the returned runner promise. + * + * In order to enable animations the `ngAnimate` module has to be loaded. + * + * To see the functional implementation check out `src/ngAnimate/animate.js`. + */ +var $AnimateProvider = ['$provide', /** @this */ function($provide) { + var provider = this; + var classNameFilter = null; + var customFilter = null; + + this.$$registeredAnimations = Object.create(null); + + /** + * @ngdoc method + * @name $animateProvider#register + * + * @description + * Registers a new injectable animation factory function. The factory function produces the + * animation object which contains callback functions for each event that is expected to be + * animated. + * + * * `eventFn`: `function(element, ... , doneFunction, options)` + * The element to animate, the `doneFunction` and the options fed into the animation. Depending + * on the type of animation additional arguments will be injected into the animation function. The + * list below explains the function signatures for the different animation methods: + * + * - setClass: function(element, addedClasses, removedClasses, doneFunction, options) + * - addClass: function(element, addedClasses, doneFunction, options) + * - removeClass: function(element, removedClasses, doneFunction, options) + * - enter, leave, move: function(element, doneFunction, options) + * - animate: function(element, fromStyles, toStyles, doneFunction, options) + * + * Make sure to trigger the `doneFunction` once the animation is fully complete. + * + * ```js + * return { + * //enter, leave, move signature + * eventFn : function(element, done, options) { + * //code to run the animation + * //once complete, then run done() + * return function endFunction(wasCancelled) { + * //code to cancel the animation + * } + * } + * } + * ``` + * + * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to). + * @param {Function} factory The factory function that will be executed to return the animation + * object. + */ + this.register = function(name, factory) { + if (name && name.charAt(0) !== '.') { + throw $animateMinErr('notcsel', 'Expecting class selector starting with \'.\' got \'{0}\'.', name); + } + + var key = name + '-animation'; + provider.$$registeredAnimations[name.substr(1)] = key; + $provide.factory(key, factory); + }; + + /** + * @ngdoc method + * @name $animateProvider#customFilter + * + * @description + * Sets and/or returns the custom filter function that is used to "filter" animations, i.e. + * determine if an animation is allowed or not. When no filter is specified (the default), no + * animation will be blocked. Setting the `customFilter` value will only allow animations for + * which the filter function's return value is truthy. + * + * This allows to easily create arbitrarily complex rules for filtering animations, such as + * allowing specific events only, or enabling animations on specific subtrees of the DOM, etc. + * Filtering animations can also boost performance for low-powered devices, as well as + * applications containing a lot of structural operations. + * + *
    + * **Best Practice:** + * Keep the filtering function as lean as possible, because it will be called for each DOM + * action (e.g. insertion, removal, class change) performed by "animation-aware" directives. + * See {@link guide/animations#which-directives-support-animations- here} for a list of built-in + * directives that support animations. + * Performing computationally expensive or time-consuming operations on each call of the + * filtering function can make your animations sluggish. + *
    + * + * **Note:** If present, `customFilter` will be checked before + * {@link $animateProvider#classNameFilter classNameFilter}. + * + * @param {Function=} filterFn - The filter function which will be used to filter all animations. + * If a falsy value is returned, no animation will be performed. The function will be called + * with the following arguments: + * - **node** `{DOMElement}` - The DOM element to be animated. + * - **event** `{String}` - The name of the animation event (e.g. `enter`, `leave`, `addClass` + * etc). + * - **options** `{Object}` - A collection of options/styles used for the animation. + * @return {Function} The current filter function or `null` if there is none set. + */ + this.customFilter = function(filterFn) { + if (arguments.length === 1) { + customFilter = isFunction(filterFn) ? filterFn : null; + } + + return customFilter; + }; + + /** + * @ngdoc method + * @name $animateProvider#classNameFilter + * + * @description + * Sets and/or returns the CSS class regular expression that is checked when performing + * an animation. Upon bootstrap the classNameFilter value is not set at all and will + * therefore enable $animate to attempt to perform an animation on any element that is triggered. + * When setting the `classNameFilter` value, animations will only be performed on elements + * that successfully match the filter expression. This in turn can boost performance + * for low-powered devices as well as applications containing a lot of structural operations. + * + * **Note:** If present, `classNameFilter` will be checked after + * {@link $animateProvider#customFilter customFilter}. If `customFilter` is present and returns + * false, `classNameFilter` will not be checked. + * + * @param {RegExp=} expression The className expression which will be checked against all animations + * @return {RegExp} The current CSS className expression value. If null then there is no expression value + */ + this.classNameFilter = function(expression) { + if (arguments.length === 1) { + classNameFilter = (expression instanceof RegExp) ? expression : null; + if (classNameFilter) { + var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]'); + if (reservedRegex.test(classNameFilter.toString())) { + classNameFilter = null; + throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); + } + } + } + return classNameFilter; + }; + + this.$get = ['$$animateQueue', function($$animateQueue) { + function domInsert(element, parentElement, afterElement) { + // if for some reason the previous element was removed + // from the dom sometime before this code runs then let's + // just stick to using the parent element as the anchor + if (afterElement) { + var afterNode = extractElementNode(afterElement); + if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) { + afterElement = null; + } + } + if (afterElement) { + afterElement.after(element); + } else { + parentElement.prepend(element); + } + } + + /** + * @ngdoc service + * @name $animate + * @description The $animate service exposes a series of DOM utility methods that provide support + * for animation hooks. The default behavior is the application of DOM operations, however, + * when an animation is detected (and animations are enabled), $animate will do the heavy lifting + * to ensure that animation runs with the triggered DOM operation. + * + * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't + * included and only when it is active then the animation hooks that `$animate` triggers will be + * functional. Once active then all structural `ng-` directives will trigger animations as they perform + * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`, + * `ngShow`, `ngHide` and `ngMessages` also provide support for animations. + * + * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives. + * + * To learn more about enabling animation support, click here to visit the + * {@link ngAnimate ngAnimate module page}. + */ + return { + // we don't call it directly since non-existant arguments may + // be interpreted as null within the sub enabled function + + /** + * + * @ngdoc method + * @name $animate#on + * @kind function + * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...) + * has fired on the given element or among any of its children. Once the listener is fired, the provided callback + * is fired with the following params: + * + * ```js + * $animate.on('enter', container, + * function callback(element, phase) { + * // cool we detected an enter animation within the container + * } + * ); + * ``` + * + * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...) + * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself + * as well as among its children + * @param {Function} callback the callback function that will be fired when the listener is triggered + * + * The arguments present in the callback function are: + * * `element` - The captured DOM element that the animation was fired on. + * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends). + */ + on: $$animateQueue.on, + + /** + * + * @ngdoc method + * @name $animate#off + * @kind function + * @description Deregisters an event listener based on the event which has been associated with the provided element. This method + * can be used in three different ways depending on the arguments: + * + * ```js + * // remove all the animation event listeners listening for `enter` + * $animate.off('enter'); + * + * // remove listeners for all animation events from the container element + * $animate.off(container); + * + * // remove all the animation event listeners listening for `enter` on the given element and its children + * $animate.off('enter', container); + * + * // remove the event listener function provided by `callback` that is set + * // to listen for `enter` on the given `container` as well as its children + * $animate.off('enter', container, callback); + * ``` + * + * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move, + * addClass, removeClass, etc...), or the container element. If it is the element, all other + * arguments are ignored. + * @param {DOMElement=} container the container element the event listener was placed on + * @param {Function=} callback the callback function that was registered as the listener + */ + off: $$animateQueue.off, + + /** + * @ngdoc method + * @name $animate#pin + * @kind function + * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists + * outside of the DOM structure of the AngularJS application. By doing so, any animation triggered via `$animate` can be issued on the + * element despite being outside the realm of the application or within another application. Say for example if the application + * was bootstrapped on an element that is somewhere inside of the `` tag, but we wanted to allow for an element to be situated + * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind + * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association. + * + * Note that this feature is only active when the `ngAnimate` module is used. + * + * @param {DOMElement} element the external element that will be pinned + * @param {DOMElement} parentElement the host parent element that will be associated with the external element + */ + pin: $$animateQueue.pin, + + /** + * + * @ngdoc method + * @name $animate#enabled + * @kind function + * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This + * function can be called in four ways: + * + * ```js + * // returns true or false + * $animate.enabled(); + * + * // changes the enabled state for all animations + * $animate.enabled(false); + * $animate.enabled(true); + * + * // returns true or false if animations are enabled for an element + * $animate.enabled(element); + * + * // changes the enabled state for an element and its children + * $animate.enabled(element, true); + * $animate.enabled(element, false); + * ``` + * + * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state + * @param {boolean=} enabled whether or not the animations will be enabled for the element + * + * @return {boolean} whether or not animations are enabled + */ + enabled: $$animateQueue.enabled, + + /** + * @ngdoc method + * @name $animate#cancel + * @kind function + * @description Cancels the provided animation. + * + * @param {Promise} animationPromise The animation promise that is returned when an animation is started. + */ + cancel: function(runner) { + if (runner.end) { + runner.end(); + } + }, + + /** + * + * @ngdoc method + * @name $animate#enter + * @kind function + * @description Inserts the element into the DOM either after the `after` element (if provided) or + * as the first child within the `parent` element and then triggers an animation. + * A promise is returned that will be resolved during the next digest once the animation + * has completed. + * + * @param {DOMElement} element the element which will be inserted into the DOM + * @param {DOMElement} parent the parent element which will append the element as + * a child (so long as the after element is not present) + * @param {DOMElement=} after the sibling element after which the element will be appended + * @param {object=} options an optional collection of options/styles that will be applied to the element. + * The object can have the following properties: + * + * - **addClass** - `{string}` - space-separated CSS classes to add to element + * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` + * - **removeClass** - `{string}` - space-separated CSS classes to remove from element + * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` + * + * @return {Promise} the animation callback promise + */ + enter: function(element, parent, after, options) { + parent = parent && jqLite(parent); + after = after && jqLite(after); + parent = parent || after.parent(); + domInsert(element, parent, after); + return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options)); + }, + + /** + * + * @ngdoc method + * @name $animate#move + * @kind function + * @description Inserts (moves) the element into its new position in the DOM either after + * the `after` element (if provided) or as the first child within the `parent` element + * and then triggers an animation. A promise is returned that will be resolved + * during the next digest once the animation has completed. + * + * @param {DOMElement} element the element which will be moved into the new DOM position + * @param {DOMElement} parent the parent element which will append the element as + * a child (so long as the after element is not present) + * @param {DOMElement=} after the sibling element after which the element will be appended + * @param {object=} options an optional collection of options/styles that will be applied to the element. + * The object can have the following properties: + * + * - **addClass** - `{string}` - space-separated CSS classes to add to element + * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` + * - **removeClass** - `{string}` - space-separated CSS classes to remove from element + * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` + * + * @return {Promise} the animation callback promise + */ + move: function(element, parent, after, options) { + parent = parent && jqLite(parent); + after = after && jqLite(after); + parent = parent || after.parent(); + domInsert(element, parent, after); + return $$animateQueue.push(element, 'move', prepareAnimateOptions(options)); + }, + + /** + * @ngdoc method + * @name $animate#leave + * @kind function + * @description Triggers an animation and then removes the element from the DOM. + * When the function is called a promise is returned that will be resolved during the next + * digest once the animation has completed. + * + * @param {DOMElement} element the element which will be removed from the DOM + * @param {object=} options an optional collection of options/styles that will be applied to the element. + * The object can have the following properties: + * + * - **addClass** - `{string}` - space-separated CSS classes to add to element + * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` + * - **removeClass** - `{string}` - space-separated CSS classes to remove from element + * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` + * + * @return {Promise} the animation callback promise + */ + leave: function(element, options) { + return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() { + element.remove(); + }); + }, + + /** + * @ngdoc method + * @name $animate#addClass + * @kind function + * + * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon + * execution, the addClass operation will only be handled after the next digest and it will not trigger an + * animation if element already contains the CSS class or if the class is removed at a later step. + * Note that class-based animations are treated differently compared to structural animations + * (like enter, move and leave) since the CSS classes may be added/removed at different points + * depending if CSS or JavaScript animations are used. + * + * @param {DOMElement} element the element which the CSS classes will be applied to + * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces) + * @param {object=} options an optional collection of options/styles that will be applied to the element. + * The object can have the following properties: + * + * - **addClass** - `{string}` - space-separated CSS classes to add to element + * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` + * - **removeClass** - `{string}` - space-separated CSS classes to remove from element + * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` + * + * @return {Promise} the animation callback promise + */ + addClass: function(element, className, options) { + options = prepareAnimateOptions(options); + options.addClass = mergeClasses(options.addclass, className); + return $$animateQueue.push(element, 'addClass', options); + }, + + /** + * @ngdoc method + * @name $animate#removeClass + * @kind function + * + * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon + * execution, the removeClass operation will only be handled after the next digest and it will not trigger an + * animation if element does not contain the CSS class or if the class is added at a later step. + * Note that class-based animations are treated differently compared to structural animations + * (like enter, move and leave) since the CSS classes may be added/removed at different points + * depending if CSS or JavaScript animations are used. + * + * @param {DOMElement} element the element which the CSS classes will be applied to + * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces) + * @param {object=} options an optional collection of options/styles that will be applied to the element. + * The object can have the following properties: + * + * - **addClass** - `{string}` - space-separated CSS classes to add to element + * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` + * - **removeClass** - `{string}` - space-separated CSS classes to remove from element + * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` + * + * @return {Promise} the animation callback promise + */ + removeClass: function(element, className, options) { + options = prepareAnimateOptions(options); + options.removeClass = mergeClasses(options.removeClass, className); + return $$animateQueue.push(element, 'removeClass', options); + }, + + /** + * @ngdoc method + * @name $animate#setClass + * @kind function + * + * @description Performs both the addition and removal of a CSS classes on an element and (during the process) + * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and + * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has + * passed. Note that class-based animations are treated differently compared to structural animations + * (like enter, move and leave) since the CSS classes may be added/removed at different points + * depending if CSS or JavaScript animations are used. + * + * @param {DOMElement} element the element which the CSS classes will be applied to + * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces) + * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces) + * @param {object=} options an optional collection of options/styles that will be applied to the element. + * The object can have the following properties: + * + * - **addClass** - `{string}` - space-separated CSS classes to add to element + * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` + * - **removeClass** - `{string}` - space-separated CSS classes to remove from element + * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` + * + * @return {Promise} the animation callback promise + */ + setClass: function(element, add, remove, options) { + options = prepareAnimateOptions(options); + options.addClass = mergeClasses(options.addClass, add); + options.removeClass = mergeClasses(options.removeClass, remove); + return $$animateQueue.push(element, 'setClass', options); + }, + + /** + * @ngdoc method + * @name $animate#animate + * @kind function + * + * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element. + * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take + * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and + * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding + * style in `to`, the style in `from` is applied immediately, and no animation is run. + * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate` + * method (or as part of the `options` parameter): + * + * ```js + * ngModule.animation('.my-inline-animation', function() { + * return { + * animate : function(element, from, to, done, options) { + * //animation + * done(); + * } + * } + * }); + * ``` + * + * @param {DOMElement} element the element which the CSS styles will be applied to + * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation. + * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation. + * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If + * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element. + * (Note that if no animation is detected then this value will not be applied to the element.) + * @param {object=} options an optional collection of options/styles that will be applied to the element. + * The object can have the following properties: + * + * - **addClass** - `{string}` - space-separated CSS classes to add to element + * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to` + * - **removeClass** - `{string}` - space-separated CSS classes to remove from element + * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from` + * + * @return {Promise} the animation callback promise + */ + animate: function(element, from, to, className, options) { + options = prepareAnimateOptions(options); + options.from = options.from ? extend(options.from, from) : from; + options.to = options.to ? extend(options.to, to) : to; + + className = className || 'ng-inline-animate'; + options.tempClasses = mergeClasses(options.tempClasses, className); + return $$animateQueue.push(element, 'animate', options); + } + }; + }]; +}]; + +var $$AnimateAsyncRunFactoryProvider = /** @this */ function() { + this.$get = ['$$rAF', function($$rAF) { + var waitQueue = []; + + function waitForTick(fn) { + waitQueue.push(fn); + if (waitQueue.length > 1) return; + $$rAF(function() { + for (var i = 0; i < waitQueue.length; i++) { + waitQueue[i](); + } + waitQueue = []; + }); + } + + return function() { + var passed = false; + waitForTick(function() { + passed = true; + }); + return function(callback) { + if (passed) { + callback(); + } else { + waitForTick(callback); + } + }; + }; + }]; +}; + +var $$AnimateRunnerFactoryProvider = /** @this */ function() { + this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$$isDocumentHidden', '$timeout', + function($q, $sniffer, $$animateAsyncRun, $$isDocumentHidden, $timeout) { + + var INITIAL_STATE = 0; + var DONE_PENDING_STATE = 1; + var DONE_COMPLETE_STATE = 2; + + AnimateRunner.chain = function(chain, callback) { + var index = 0; + + next(); + function next() { + if (index === chain.length) { + callback(true); + return; + } + + chain[index](function(response) { + if (response === false) { + callback(false); + return; + } + index++; + next(); + }); + } + }; + + AnimateRunner.all = function(runners, callback) { + var count = 0; + var status = true; + forEach(runners, function(runner) { + runner.done(onProgress); + }); + + function onProgress(response) { + status = status && response; + if (++count === runners.length) { + callback(status); + } + } + }; + + function AnimateRunner(host) { + this.setHost(host); + + var rafTick = $$animateAsyncRun(); + var timeoutTick = function(fn) { + $timeout(fn, 0, false); + }; + + this._doneCallbacks = []; + this._tick = function(fn) { + if ($$isDocumentHidden()) { + timeoutTick(fn); + } else { + rafTick(fn); + } + }; + this._state = 0; + } + + AnimateRunner.prototype = { + setHost: function(host) { + this.host = host || {}; + }, + + done: function(fn) { + if (this._state === DONE_COMPLETE_STATE) { + fn(); + } else { + this._doneCallbacks.push(fn); + } + }, + + progress: noop, + + getPromise: function() { + if (!this.promise) { + var self = this; + this.promise = $q(function(resolve, reject) { + self.done(function(status) { + if (status === false) { + reject(); + } else { + resolve(); + } + }); + }); + } + return this.promise; + }, + + then: function(resolveHandler, rejectHandler) { + return this.getPromise().then(resolveHandler, rejectHandler); + }, + + 'catch': function(handler) { + return this.getPromise()['catch'](handler); + }, + + 'finally': function(handler) { + return this.getPromise()['finally'](handler); + }, + + pause: function() { + if (this.host.pause) { + this.host.pause(); + } + }, + + resume: function() { + if (this.host.resume) { + this.host.resume(); + } + }, + + end: function() { + if (this.host.end) { + this.host.end(); + } + this._resolve(true); + }, + + cancel: function() { + if (this.host.cancel) { + this.host.cancel(); + } + this._resolve(false); + }, + + complete: function(response) { + var self = this; + if (self._state === INITIAL_STATE) { + self._state = DONE_PENDING_STATE; + self._tick(function() { + self._resolve(response); + }); + } + }, + + _resolve: function(response) { + if (this._state !== DONE_COMPLETE_STATE) { + forEach(this._doneCallbacks, function(fn) { + fn(response); + }); + this._doneCallbacks.length = 0; + this._state = DONE_COMPLETE_STATE; + } + } + }; + + return AnimateRunner; + }]; +}; + +/* exported $CoreAnimateCssProvider */ + +/** + * @ngdoc service + * @name $animateCss + * @kind object + * @this + * + * @description + * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included, + * then the `$animateCss` service will actually perform animations. + * + * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}. + */ +var $CoreAnimateCssProvider = function() { + this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) { + + return function(element, initialOptions) { + // all of the animation functions should create + // a copy of the options data, however, if a + // parent service has already created a copy then + // we should stick to using that + var options = initialOptions || {}; + if (!options.$$prepared) { + options = copy(options); + } + + // there is no point in applying the styles since + // there is no animation that goes on at all in + // this version of $animateCss. + if (options.cleanupStyles) { + options.from = options.to = null; + } + + if (options.from) { + element.css(options.from); + options.from = null; + } + + var closed, runner = new $$AnimateRunner(); + return { + start: run, + end: run + }; + + function run() { + $$rAF(function() { + applyAnimationContents(); + if (!closed) { + runner.complete(); + } + closed = true; + }); + return runner; + } + + function applyAnimationContents() { + if (options.addClass) { + element.addClass(options.addClass); + options.addClass = null; + } + if (options.removeClass) { + element.removeClass(options.removeClass); + options.removeClass = null; + } + if (options.to) { + element.css(options.to); + options.to = null; + } + } + }; + }]; +}; + +/* global stripHash: true */ + +/** + * ! This is a private undocumented service ! + * + * @name $browser + * @requires $log + * @description + * This object has two goals: + * + * - hide all the global state in the browser caused by the window object + * - abstract away all the browser specific features and inconsistencies + * + * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` + * service, which can be used for convenient testing of the application without the interaction with + * the real browser apis. + */ +/** + * @param {object} window The global window object. + * @param {object} document jQuery wrapped document. + * @param {object} $log window.console or an object with the same interface. + * @param {object} $sniffer $sniffer service + */ +function Browser(window, document, $log, $sniffer) { + var self = this, + location = window.location, + history = window.history, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + pendingDeferIds = {}; + + self.isMock = false; + + var outstandingRequestCount = 0; + var outstandingRequestCallbacks = []; + + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = completeOutstandingRequest; + self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; + + /** + * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` + * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. + */ + function completeOutstandingRequest(fn) { + try { + fn.apply(null, sliceArgs(arguments, 1)); + } finally { + outstandingRequestCount--; + if (outstandingRequestCount === 0) { + while (outstandingRequestCallbacks.length) { + try { + outstandingRequestCallbacks.pop()(); + } catch (e) { + $log.error(e); + } + } + } + } + } + + function getHash(url) { + var index = url.indexOf('#'); + return index === -1 ? '' : url.substr(index); + } + + /** + * @private + * TODO(vojta): prefix this method with $$ ? + * @param {function()} callback Function that will be called when no outstanding request + */ + self.notifyWhenNoOutstandingRequests = function(callback) { + if (outstandingRequestCount === 0) { + callback(); + } else { + outstandingRequestCallbacks.push(callback); + } + }; + + ////////////////////////////////////////////////////////////// + // URL API + ////////////////////////////////////////////////////////////// + + var cachedState, lastHistoryState, + lastBrowserUrl = location.href, + baseElement = document.find('base'), + pendingLocation = null, + getCurrentState = !$sniffer.history ? noop : function getCurrentState() { + try { + return history.state; + } catch (e) { + // MSIE can reportedly throw when there is no state (UNCONFIRMED). + } + }; + + cacheState(); + + /** + * @name $browser#url + * + * @description + * GETTER: + * Without any argument, this method just returns current value of location.href. + * + * SETTER: + * With at least one argument, this method sets url to new value. + * If html5 history api supported, pushState/replaceState is used, otherwise + * location.href/location.replace is used. + * Returns its own instance to allow chaining + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to change url. + * + * @param {string} url New url (when used as setter) + * @param {boolean=} replace Should new url replace current history record? + * @param {object=} state object to use with pushState/replaceState + */ + self.url = function(url, replace, state) { + // In modern browsers `history.state` is `null` by default; treating it separately + // from `undefined` would cause `$browser.url('/foo')` to change `history.state` + // to undefined via `pushState`. Instead, let's change `undefined` to `null` here. + if (isUndefined(state)) { + state = null; + } + + // Android Browser BFCache causes location, history reference to become stale. + if (location !== window.location) location = window.location; + if (history !== window.history) history = window.history; + + // setter + if (url) { + var sameState = lastHistoryState === state; + + // Don't change anything if previous and current URLs and states match. This also prevents + // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. + // See https://github.com/angular/angular.js/commit/ffb2701 + if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { + return self; + } + var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); + lastBrowserUrl = url; + lastHistoryState = state; + // Don't use history API if only the hash changed + // due to a bug in IE10/IE11 which leads + // to not firing a `hashchange` nor `popstate` event + // in some cases (see #9143). + if ($sniffer.history && (!sameBase || !sameState)) { + history[replace ? 'replaceState' : 'pushState'](state, '', url); + cacheState(); + } else { + if (!sameBase) { + pendingLocation = url; + } + if (replace) { + location.replace(url); + } else if (!sameBase) { + location.href = url; + } else { + location.hash = getHash(url); + } + if (location.href !== url) { + pendingLocation = url; + } + } + if (pendingLocation) { + pendingLocation = url; + } + return self; + // getter + } else { + // - pendingLocation is needed as browsers don't allow to read out + // the new location.href if a reload happened or if there is a bug like in iOS 9 (see + // https://openradar.appspot.com/22186109). + // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 + return pendingLocation || location.href.replace(/%27/g,'\''); + } + }; + + /** + * @name $browser#state + * + * @description + * This method is a getter. + * + * Return history.state or null if history.state is undefined. + * + * @returns {object} state + */ + self.state = function() { + return cachedState; + }; + + var urlChangeListeners = [], + urlChangeInit = false; + + function cacheStateAndFireUrlChange() { + pendingLocation = null; + fireStateOrUrlChange(); + } + + // This variable should be used *only* inside the cacheState function. + var lastCachedState = null; + function cacheState() { + // This should be the only place in $browser where `history.state` is read. + cachedState = getCurrentState(); + cachedState = isUndefined(cachedState) ? null : cachedState; + + // Prevent callbacks fo fire twice if both hashchange & popstate were fired. + if (equals(cachedState, lastCachedState)) { + cachedState = lastCachedState; + } + + lastCachedState = cachedState; + lastHistoryState = cachedState; + } + + function fireStateOrUrlChange() { + var prevLastHistoryState = lastHistoryState; + cacheState(); + + if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) { + return; + } + + lastBrowserUrl = self.url(); + lastHistoryState = cachedState; + forEach(urlChangeListeners, function(listener) { + listener(self.url(), cachedState); + }); + } + + /** + * @name $browser#onUrlChange + * + * @description + * Register callback function that will be called, when url changes. + * + * It's only called when the url is changed from outside of AngularJS: + * - user types different url into address bar + * - user clicks on history (forward/back) button + * - user clicks on a link + * + * It's not called when url is changed by $browser.url() method + * + * The listener gets called with new url as parameter. + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to monitor url changes in AngularJS apps. + * + * @param {function(string)} listener Listener function to be called when url changes. + * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. + */ + self.onUrlChange = function(callback) { + // TODO(vojta): refactor to use node's syntax for events + if (!urlChangeInit) { + // We listen on both (hashchange/popstate) when available, as some browsers don't + // fire popstate when user changes the address bar and don't fire hashchange when url + // changed by push/replaceState + + // html5 history api - popstate event + if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange); + // hashchange event + jqLite(window).on('hashchange', cacheStateAndFireUrlChange); + + urlChangeInit = true; + } + + urlChangeListeners.push(callback); + return callback; + }; + + /** + * @private + * Remove popstate and hashchange handler from window. + * + * NOTE: this api is intended for use only by $rootScope. + */ + self.$$applicationDestroyed = function() { + jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange); + }; + + /** + * Checks whether the url has changed outside of AngularJS. + * Needs to be exported to be able to check for changes that have been done in sync, + * as hashchange/popstate events fire in async. + */ + self.$$checkUrlChange = fireStateOrUrlChange; + + ////////////////////////////////////////////////////////////// + // Misc API + ////////////////////////////////////////////////////////////// + + /** + * @name $browser#baseHref + * + * @description + * Returns current + * (always relative - without domain) + * + * @returns {string} The current base href + */ + self.baseHref = function() { + var href = baseElement.attr('href'); + return href ? href.replace(/^(https?:)?\/\/[^/]*/, '') : ''; + }; + + /** + * @name $browser#defer + * @param {function()} fn A function, who's execution should be deferred. + * @param {number=} [delay=0] of milliseconds to defer the function execution. + * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. + * + * @description + * Executes a fn asynchronously via `setTimeout(fn, delay)`. + * + * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using + * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed + * via `$browser.defer.flush()`. + * + */ + self.defer = function(fn, delay) { + var timeoutId; + outstandingRequestCount++; + timeoutId = setTimeout(function() { + delete pendingDeferIds[timeoutId]; + completeOutstandingRequest(fn); + }, delay || 0); + pendingDeferIds[timeoutId] = true; + return timeoutId; + }; + + + /** + * @name $browser#defer.cancel + * + * @description + * Cancels a deferred task identified with `deferId`. + * + * @param {*} deferId Token returned by the `$browser.defer` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + self.defer.cancel = function(deferId) { + if (pendingDeferIds[deferId]) { + delete pendingDeferIds[deferId]; + clearTimeout(deferId); + completeOutstandingRequest(noop); + return true; + } + return false; + }; + +} + +/** @this */ +function $BrowserProvider() { + this.$get = ['$window', '$log', '$sniffer', '$document', + function($window, $log, $sniffer, $document) { + return new Browser($window, $document, $log, $sniffer); + }]; +} + +/** + * @ngdoc service + * @name $cacheFactory + * @this + * + * @description + * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to + * them. + * + * ```js + * + * var cache = $cacheFactory('cacheId'); + * expect($cacheFactory.get('cacheId')).toBe(cache); + * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); + * + * cache.put("key", "value"); + * cache.put("another key", "another value"); + * + * // We've specified no options on creation + * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); + * + * ``` + * + * + * @param {string} cacheId Name or id of the newly created cache. + * @param {object=} options Options object that specifies the cache behavior. Properties: + * + * - `{number=}` `capacity` — turns the cache into LRU cache. + * + * @returns {object} Newly created cache object with the following set of methods: + * + * - `{object}` `info()` — Returns id, size, and options of cache. + * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns + * it. + * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. + * - `{void}` `removeAll()` — Removes all cached values. + * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. + * + * @example + + +
    + + + + +

    Cached Values

    +
    + + : + +
    + +

    Cache Info

    +
    + + : + +
    +
    +
    + + angular.module('cacheExampleApp', []). + controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { + $scope.keys = []; + $scope.cache = $cacheFactory('cacheId'); + $scope.put = function(key, value) { + if (angular.isUndefined($scope.cache.get(key))) { + $scope.keys.push(key); + } + $scope.cache.put(key, angular.isUndefined(value) ? null : value); + }; + }]); + + + p { + margin: 10px 0 3px; + } + +
    + */ +function $CacheFactoryProvider() { + + this.$get = function() { + var caches = {}; + + function cacheFactory(cacheId, options) { + if (cacheId in caches) { + throw minErr('$cacheFactory')('iid', 'CacheId \'{0}\' is already taken!', cacheId); + } + + var size = 0, + stats = extend({}, options, {id: cacheId}), + data = createMap(), + capacity = (options && options.capacity) || Number.MAX_VALUE, + lruHash = createMap(), + freshEnd = null, + staleEnd = null; + + /** + * @ngdoc type + * @name $cacheFactory.Cache + * + * @description + * A cache object used to store and retrieve data, primarily used by + * {@link $templateRequest $templateRequest} and the {@link ng.directive:script script} + * directive to cache templates and other data. + * + * ```js + * angular.module('superCache') + * .factory('superCache', ['$cacheFactory', function($cacheFactory) { + * return $cacheFactory('super-cache'); + * }]); + * ``` + * + * Example test: + * + * ```js + * it('should behave like a cache', inject(function(superCache) { + * superCache.put('key', 'value'); + * superCache.put('another key', 'another value'); + * + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 2 + * }); + * + * superCache.remove('another key'); + * expect(superCache.get('another key')).toBeUndefined(); + * + * superCache.removeAll(); + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 0 + * }); + * })); + * ``` + */ + return (caches[cacheId] = { + + /** + * @ngdoc method + * @name $cacheFactory.Cache#put + * @kind function + * + * @description + * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be + * retrieved later, and incrementing the size of the cache if the key was not already + * present in the cache. If behaving like an LRU cache, it will also remove stale + * entries from the set. + * + * It will not insert undefined values into the cache. + * + * @param {string} key the key under which the cached data is stored. + * @param {*} value the value to store alongside the key. If it is undefined, the key + * will not be stored. + * @returns {*} the value stored. + */ + put: function(key, value) { + if (isUndefined(value)) return; + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); + + refresh(lruEntry); + } + + if (!(key in data)) size++; + data[key] = value; + + if (size > capacity) { + this.remove(staleEnd.key); + } + + return value; + }, + + /** + * @ngdoc method + * @name $cacheFactory.Cache#get + * @kind function + * + * @description + * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the data to be retrieved + * @returns {*} the value stored. + */ + get: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + refresh(lruEntry); + } + + return data[key]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#remove + * @kind function + * + * @description + * Removes an entry from the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the entry to be removed + */ + remove: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + if (lruEntry === freshEnd) freshEnd = lruEntry.p; + if (lruEntry === staleEnd) staleEnd = lruEntry.n; + link(lruEntry.n,lruEntry.p); + + delete lruHash[key]; + } + + if (!(key in data)) return; + + delete data[key]; + size--; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#removeAll + * @kind function + * + * @description + * Clears the cache object of any entries. + */ + removeAll: function() { + data = createMap(); + size = 0; + lruHash = createMap(); + freshEnd = staleEnd = null; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#destroy + * @kind function + * + * @description + * Destroys the {@link $cacheFactory.Cache Cache} object entirely, + * removing it from the {@link $cacheFactory $cacheFactory} set. + */ + destroy: function() { + data = null; + stats = null; + lruHash = null; + delete caches[cacheId]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#info + * @kind function + * + * @description + * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. + * + * @returns {object} an object with the following properties: + *
      + *
    • **id**: the id of the cache instance
    • + *
    • **size**: the number of entries kept in the cache instance
    • + *
    • **...**: any additional properties from the options object when creating the + * cache.
    • + *
    + */ + info: function() { + return extend({}, stats, {size: size}); + } + }); + + + /** + * makes the `entry` the freshEnd of the LRU linked list + */ + function refresh(entry) { + if (entry !== freshEnd) { + if (!staleEnd) { + staleEnd = entry; + } else if (staleEnd === entry) { + staleEnd = entry.n; + } + + link(entry.n, entry.p); + link(entry, freshEnd); + freshEnd = entry; + freshEnd.n = null; + } + } + + + /** + * bidirectionally links two entries of the LRU linked list + */ + function link(nextEntry, prevEntry) { + if (nextEntry !== prevEntry) { + if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify + if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify + } + } + } + + + /** + * @ngdoc method + * @name $cacheFactory#info + * + * @description + * Get information about all the caches that have been created + * + * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` + */ + cacheFactory.info = function() { + var info = {}; + forEach(caches, function(cache, cacheId) { + info[cacheId] = cache.info(); + }); + return info; + }; + + + /** + * @ngdoc method + * @name $cacheFactory#get + * + * @description + * Get access to a cache object by the `cacheId` used when it was created. + * + * @param {string} cacheId Name or id of a cache to access. + * @returns {object} Cache object identified by the cacheId or undefined if no such cache. + */ + cacheFactory.get = function(cacheId) { + return caches[cacheId]; + }; + + + return cacheFactory; + }; +} + +/** + * @ngdoc service + * @name $templateCache + * @this + * + * @description + * `$templateCache` is a {@link $cacheFactory.Cache Cache object} created by the + * {@link ng.$cacheFactory $cacheFactory}. + * + * The first time a template is used, it is loaded in the template cache for quick retrieval. You + * can load templates directly into the cache in a `script` tag, by using {@link $templateRequest}, + * or by consuming the `$templateCache` service directly. + * + * Adding via the `script` tag: + * + * ```html + * + * ``` + * + * **Note:** the `script` tag containing the template does not need to be included in the `head` of + * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (e.g. + * element with {@link ngApp} attribute), otherwise the template will be ignored. + * + * Adding via the `$templateCache` service: + * + * ```js + * var myApp = angular.module('myApp', []); + * myApp.run(function($templateCache) { + * $templateCache.put('templateId.html', 'This is the content of the template'); + * }); + * ``` + * + * To retrieve the template later, simply use it in your component: + * ```js + * myApp.component('myComponent', { + * templateUrl: 'templateId.html' + * }); + * ``` + * + * or get it via the `$templateCache` service: + * ```js + * $templateCache.get('templateId.html') + * ``` + * + */ +function $TemplateCacheProvider() { + this.$get = ['$cacheFactory', function($cacheFactory) { + return $cacheFactory('templates'); + }]; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables like document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! + * + * DOM-related variables: + * + * - "node" - DOM Node + * - "element" - DOM Element or Node + * - "$node" or "$element" - jqLite-wrapped node or element + * + * + * Compiler related stuff: + * + * - "linkFn" - linking fn of a single directive + * - "nodeLinkFn" - function that aggregates all linking fns for a particular node + * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node + * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) + */ + + +/** + * @ngdoc service + * @name $compile + * @kind function + * + * @description + * Compiles an HTML string or DOM into a template and produces a template function, which + * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. + * + * The compilation is a process of walking the DOM tree and matching DOM elements to + * {@link ng.$compileProvider#directive directives}. + * + *
    + * **Note:** This document is an in-depth reference of all directive options. + * For a gentle introduction to directives with examples of common use cases, + * see the {@link guide/directive directive guide}. + *
    + * + * ## Comprehensive Directive API + * + * There are many different options for a directive. + * + * The difference resides in the return value of the factory function. + * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)} + * that defines the directive properties, or just the `postLink` function (all other properties will have + * the default values). + * + *
    + * **Best Practice:** It's recommended to use the "directive definition object" form. + *
    + * + * Here's an example directive declared with a Directive Definition Object: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * {@link $compile#-priority- priority}: 0, + * {@link $compile#-template- template}: '
    ', // or // function(tElement, tAttrs) { ... }, + * // or + * // {@link $compile#-templateurl- templateUrl}: 'directive.html', // or // function(tElement, tAttrs) { ... }, + * {@link $compile#-transclude- transclude}: false, + * {@link $compile#-restrict- restrict}: 'A', + * {@link $compile#-templatenamespace- templateNamespace}: 'html', + * {@link $compile#-scope- scope}: false, + * {@link $compile#-controller- controller}: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, + * {@link $compile#-controlleras- controllerAs}: 'stringIdentifier', + * {@link $compile#-bindtocontroller- bindToController}: false, + * {@link $compile#-require- require}: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], + * {@link $compile#-multielement- multiElement}: false, + * {@link $compile#-compile- compile}: function compile(tElement, tAttrs, transclude) { + * return { + * {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... }, + * {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... } + * } + * // or + * // return function postLink( ... ) { ... } + * }, + * // or + * // {@link $compile#-link- link}: { + * // {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... }, + * // {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... } + * // } + * // or + * // {@link $compile#-link- link}: function postLink( ... ) { ... } + * }; + * return directiveDefinitionObject; + * }); + * ``` + * + *
    + * **Note:** Any unspecified options will use the default value. You can see the default values below. + *
    + * + * Therefore the above can be simplified as: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * link: function postLink(scope, iElement, iAttrs) { ... } + * }; + * return directiveDefinitionObject; + * // or + * // return function postLink(scope, iElement, iAttrs) { ... } + * }); + * ``` + * + * ### Life-cycle hooks + * Directive controllers can provide the following methods that are called by AngularJS at points in the life-cycle of the + * directive: + * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and + * had their bindings initialized (and before the pre & post linking functions for the directives on + * this element). This is a good place to put initialization code for your controller. + * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The + * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an + * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a + * component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will + * also be called when your bindings are initialized. + * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on + * changes. Any actions that you wish to take in response to the changes that you detect must be + * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook + * could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not + * be detected by AngularJS's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments; + * if detecting changes, you must store the previous value(s) for comparison to the current values. + * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing + * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in + * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent + * components will have their `$onDestroy()` hook called before child components. + * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link + * function this hook can be used to set up DOM event handlers and do direct DOM manipulation. + * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since + * they are waiting for their template to load asynchronously and their own compilation and linking has been + * suspended until that occurs. + * + * #### Comparison with life-cycle hooks in the new Angular + * The new Angular also uses life-cycle hooks for its components. While the AngularJS life-cycle hooks are similar there are + * some differences that you should be aware of, especially when it comes to moving your code from AngularJS to Angular: + * + * * AngularJS hooks are prefixed with `$`, such as `$onInit`. Angular hooks are prefixed with `ng`, such as `ngOnInit`. + * * AngularJS hooks can be defined on the controller prototype or added to the controller inside its constructor. + * In Angular you can only define hooks on the prototype of the Component class. + * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in AngularJS than you would to + * `ngDoCheck` in Angular. + * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be + * propagated throughout the application. + * Angular does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an + * error or do nothing depending upon the state of `enableProdMode()`. + * + * #### Life-cycle hook examples + * + * This example shows how you can check for mutations to a Date object even though the identity of the object + * has not changed. + * + * + * + * angular.module('do-check-module', []) + * .component('app', { + * template: + * 'Month: ' + + * 'Date: {{ $ctrl.date }}' + + * '', + * controller: function() { + * this.date = new Date(); + * this.month = this.date.getMonth(); + * this.updateDate = function() { + * this.date.setMonth(this.month); + * }; + * } + * }) + * .component('test', { + * bindings: { date: '<' }, + * template: + * '
    {{ $ctrl.log | json }}
    ', + * controller: function() { + * var previousValue; + * this.log = []; + * this.$doCheck = function() { + * var currentValue = this.date && this.date.valueOf(); + * if (previousValue !== currentValue) { + * this.log.push('doCheck: date mutated: ' + this.date); + * previousValue = currentValue; + * } + * }; + * } + * }); + *
    + * + * + * + *
    + * + * This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the + * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large + * arrays or objects can have a negative impact on your application performance) + * + * + * + *
    + * + * + *
    {{ items }}
    + * + *
    + *
    + * + * angular.module('do-check-module', []) + * .component('test', { + * bindings: { items: '<' }, + * template: + * '
    {{ $ctrl.log | json }}
    ', + * controller: function() { + * this.log = []; + * + * this.$doCheck = function() { + * if (this.items_ref !== this.items) { + * this.log.push('doCheck: items changed'); + * this.items_ref = this.items; + * } + * if (!angular.equals(this.items_clone, this.items)) { + * this.log.push('doCheck: items mutated'); + * this.items_clone = angular.copy(this.items); + * } + * }; + * } + * }); + *
    + *
    + * + * + * ### Directive Definition Object + * + * The directive definition object provides instructions to the {@link ng.$compile + * compiler}. The attributes are: + * + * #### `multiElement` + * When this property is set to true (default is `false`), the HTML compiler will collect DOM nodes between + * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them + * together as the directive elements. It is recommended that this feature be used on directives + * which are not strictly behavioral (such as {@link ngClick}), and which + * do not manipulate or replace child nodes (such as {@link ngInclude}). + * + * #### `priority` + * When there are multiple directives defined on a single DOM element, sometimes it + * is necessary to specify the order in which the directives are applied. The `priority` is used + * to sort the directives before their `compile` functions get called. Priority is defined as a + * number. Directives with greater numerical `priority` are compiled first. Pre-link functions + * are also run in priority order, but post-link functions are run in reverse order. The order + * of directives with the same priority is undefined. The default priority is `0`. + * + * #### `terminal` + * If set to true then the current `priority` will be the last set of directives + * which will execute (any directives at the current priority will still execute + * as the order of execution on same `priority` is undefined). Note that expressions + * and other directives used in the directive's template will also be excluded from execution. + * + * #### `scope` + * The scope property can be `false`, `true`, or an object: + * + * * **`false` (default):** No scope will be created for the directive. The directive will use its + * parent's scope. + * + * * **`true`:** A new child scope that prototypically inherits from its parent will be created for + * the directive's element. If multiple directives on the same element request a new scope, + * only one new scope is created. + * + * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template. + * The 'isolate' scope differs from normal scope in that it does not prototypically + * inherit from its parent scope. This is useful when creating reusable components, which should not + * accidentally read or modify data in the parent scope. Note that an isolate scope + * directive without a `template` or `templateUrl` will not apply the isolate scope + * to its children elements. + * + * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the + * directive's element. These local properties are useful for aliasing values for templates. The keys in + * the object hash map to the name of the property on the isolate scope; the values define how the property + * is bound to the parent scope, via matching attributes on the directive's element: + * + * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is + * always a string since DOM attributes are strings. If no `attr` name is specified then the + * attribute name is assumed to be the same as the local name. Given `` and the isolate scope definition `scope: { localName:'@myAttr' }`, + * the directive's scope property `localName` will reflect the interpolated value of `hello + * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's + * scope. The `name` is read from the parent scope (not the directive's scope). + * + * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression + * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope. + * If no `attr` name is specified then the attribute name is assumed to be the same as the local + * name. Given `` and the isolate scope definition `scope: { + * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the + * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in + * `localModel` and vice versa. Optional attributes should be marked as such with a question mark: + * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't + * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`}) + * will be thrown upon discovering changes to the local value, since it will be impossible to sync + * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`} + * method is used for tracking changes, and the equality check is based on object identity. + * However, if an object literal or an array literal is passed as the binding expression, the + * equality check is done by value (using the {@link angular.equals} function). It's also possible + * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection + * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional). + * + * * `<` or `` and directive definition of + * `scope: { localModel:'` and the isolate scope definition `scope: { + * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for + * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope + * via an expression to the parent scope. This can be done by passing a map of local variable names + * and values into the expression wrapper fn. For example, if the expression is `increment(amount)` + * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`. + * + * In general it's possible to apply more than one directive to one element, but there might be limitations + * depending on the type of scope required by the directives. The following points will help explain these limitations. + * For simplicity only two directives are taken into account, but it is also applicable for several directives: + * + * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope + * * **child scope** + **no scope** => Both directives will share one single child scope + * * **child scope** + **child scope** => Both directives will share one single child scope + * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use + * its parent's scope + * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot + * be applied to the same element. + * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives + * cannot be applied to the same element. + * + * + * #### `bindToController` + * This property is used to bind scope properties directly to the controller. It can be either + * `true` or an object hash with the same format as the `scope` property. + * + * When an isolate scope is used for a directive (see above), `bindToController: true` will + * allow a component to have its properties bound to the controller, rather than to scope. + * + * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller + * properties. You can access these bindings once they have been initialized by providing a controller method called + * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings + * initialized. + * + *
    + * **Deprecation warning:** if `$compileProcvider.preAssignBindingsEnabled(true)` was called, bindings for non-ES6 class + * controllers are bound to `this` before the controller constructor is called but this use is now deprecated. Please + * place initialization code that relies upon bindings inside a `$onInit` method on the controller, instead. + *
    + * + * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property. + * This will set up the scope bindings to the controller directly. Note that `scope` can still be used + * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate + * scope (useful for component directives). + * + * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`. + * + * + * #### `controller` + * Controller constructor function. The controller is instantiated before the + * pre-linking phase and can be accessed by other directives (see + * `require` attribute). This allows the directives to communicate with each other and augment + * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: + * + * * `$scope` - Current scope associated with the element + * * `$element` - Current element + * * `$attrs` - Current attributes object for the element + * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: + * `function([scope], cloneLinkingFn, futureParentElement, slotName)`: + * * `scope`: (optional) override the scope. + * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content. + * * `futureParentElement` (optional): + * * defines the parent to which the `cloneLinkingFn` will add the cloned elements. + * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`. + * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements) + * and when the `cloneLinkingFn` is passed, + * as those elements need to created and cloned in a special way when they are defined outside their + * usual containers (e.g. like ``). + * * See also the `directive.templateNamespace` property. + * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`) + * then the default transclusion is provided. + * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns + * `true` if the specified slot contains content (i.e. one or more DOM nodes). + * + * #### `require` + * Require another directive and inject its controller as the fourth argument to the linking function. The + * `require` property can be a string, an array or an object: + * * a **string** containing the name of the directive to pass to the linking function + * * an **array** containing the names of directives to pass to the linking function. The argument passed to the + * linking function will be an array of controllers in the same order as the names in the `require` property + * * an **object** whose property values are the names of the directives to pass to the linking function. The argument + * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding + * controllers. + * + * If the `require` property is an object and `bindToController` is truthy, then the required controllers are + * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers + * have been constructed but before `$onInit` is called. + * If the name of the required controller is the same as the local name (the key), the name can be + * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`. + * See the {@link $compileProvider#component} helper for an example of how this can be used. + * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is + * raised (unless no link function is specified and the required controllers are not being bound to the directive + * controller, in which case error checking is skipped). The name can be prefixed with: + * + * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. + * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. + * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. + * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found. + * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass + * `null` to the `link` fn if not found. + * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass + * `null` to the `link` fn if not found. + * + * + * #### `controllerAs` + * Identifier name for a reference to the controller in the directive's scope. + * This allows the controller to be referenced from the directive template. This is especially + * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible + * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the + * `controllerAs` reference might overwrite a property that already exists on the parent scope. + * + * + * #### `restrict` + * String of subset of `EACM` which restricts the directive to a specific directive + * declaration style. If omitted, the defaults (elements and attributes) are used. + * + * * `E` - Element name (default): `` + * * `A` - Attribute (default): `
    ` + * * `C` - Class: `
    ` + * * `M` - Comment: `` + * + * + * #### `templateNamespace` + * String representing the document type used by the markup in the template. + * AngularJS needs this information as those elements need to be created and cloned + * in a special way when they are defined outside their usual containers like `` and ``. + * + * * `html` - All root nodes in the template are HTML. Root nodes may also be + * top-level elements such as `` or ``. + * * `svg` - The root nodes in the template are SVG elements (excluding ``). + * * `math` - The root nodes in the template are MathML elements (excluding ``). + * + * If no `templateNamespace` is specified, then the namespace is considered to be `html`. + * + * #### `template` + * HTML markup that may: + * * Replace the contents of the directive's element (default). + * * Replace the directive's element itself (if `replace` is true - DEPRECATED). + * * Wrap the contents of the directive's element (if `transclude` is true). + * + * Value may be: + * + * * A string. For example `
    {{delete_str}}
    `. + * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` + * function api below) and returns a string value. + * + * + * #### `templateUrl` + * This is similar to `template` but the template is loaded from the specified URL, asynchronously. + * + * Because template loading is asynchronous the compiler will suspend compilation of directives on that element + * for later when the template has been resolved. In the meantime it will continue to compile and link + * sibling and parent elements as though this element had not contained any directives. + * + * The compiler does not suspend the entire compilation to wait for templates to be loaded because this + * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the + * case when only one deeply nested directive has `templateUrl`. + * + * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} + * + * You can specify `templateUrl` as a string representing the URL or as a function which takes two + * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns + * a string value representing the url. In either case, the template URL is passed through {@link + * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. + * + * + * #### `replace` (*DEPRECATED*) + * + * `replace` will be removed in next major release - i.e. v2.0). + * + * Specifies what the template should replace. Defaults to `false`. + * + * * `true` - the template will replace the directive's element. + * * `false` - the template will replace the contents of the directive's element. + * + * The replacement process migrates all of the attributes / classes from the old element to the new + * one. See the {@link guide/directive#template-expanding-directive + * Directives Guide} for an example. + * + * There are very few scenarios where element replacement is required for the application function, + * the main one being reusable custom components that are used within SVG contexts + * (because SVG doesn't work with custom elements in the DOM tree). + * + * #### `transclude` + * Extract the contents of the element where the directive appears and make it available to the directive. + * The contents are compiled and provided to the directive as a **transclusion function**. See the + * {@link $compile#transclusion Transclusion} section below. + * + * + * #### `compile` + * + * ```js + * function compile(tElement, tAttrs, transclude) { ... } + * ``` + * + * The compile function deals with transforming the template DOM. Since most directives do not do + * template transformation, it is not used often. The compile function takes the following arguments: + * + * * `tElement` - template element - The element where the directive has been declared. It is + * safe to do template transformation on the element and child elements only. + * + * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared + * between all directive compile functions. + * + * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` + * + *
    + * **Note:** The template instance and the link instance may be different objects if the template has + * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that + * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration + * should be done in a linking function rather than in a compile function. + *
    + + *
    + * **Note:** The compile function cannot handle directives that recursively use themselves in their + * own templates or compile functions. Compiling these directives results in an infinite loop and + * stack overflow errors. + * + * This can be avoided by manually using $compile in the postLink function to imperatively compile + * a directive's template instead of relying on automatic template compilation via `template` or + * `templateUrl` declaration or manual compilation inside the compile function. + *
    + * + *
    + * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it + * e.g. does not know about the right outer scope. Please use the transclude function that is passed + * to the link function instead. + *
    + + * A compile function can have a return value which can be either a function or an object. + * + * * returning a (post-link) function - is equivalent to registering the linking function via the + * `link` property of the config object when the compile function is empty. + * + * * returning an object with function(s) registered via `pre` and `post` properties - allows you to + * control when a linking function should be called during the linking phase. See info about + * pre-linking and post-linking functions below. + * + * + * #### `link` + * This property is used only if the `compile` property is not defined. + * + * ```js + * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } + * ``` + * + * The link function is responsible for registering DOM listeners as well as updating the DOM. It is + * executed after the template has been cloned. This is where most of the directive logic will be + * put. + * + * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the + * directive for registering {@link ng.$rootScope.Scope#$watch watches}. + * + * * `iElement` - instance element - The element where the directive is to be used. It is safe to + * manipulate the children of the element only in `postLink` function since the children have + * already been linked. + * + * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared + * between all directive linking functions. + * + * * `controller` - the directive's required controller instance(s) - Instances are shared + * among all directives, which allows the directives to use the controllers as a communication + * channel. The exact value depends on the directive's `require` property: + * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one + * * `string`: the controller instance + * * `array`: array of controller instances + * + * If a required controller cannot be found, and it is optional, the instance is `null`, + * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown. + * + * Note that you can also require the directive's own controller - it will be made available like + * any other controller. + * + * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. + * This is the same as the `$transclude` parameter of directive controllers, + * see {@link ng.$compile#-controller- the controller section for details}. + * `function([scope], cloneLinkingFn, futureParentElement)`. + * + * #### Pre-linking function + * + * Executed before the child elements are linked. Not safe to do DOM transformation since the + * compiler linking function will fail to locate the correct elements for linking. + * + * #### Post-linking function + * + * Executed after the child elements are linked. + * + * Note that child elements that contain `templateUrl` directives will not have been compiled + * and linked since they are waiting for their template to load asynchronously and their own + * compilation and linking has been suspended until that occurs. + * + * It is safe to do DOM transformation in the post-linking function on elements that are not waiting + * for their async templates to be resolved. + * + * + * ### Transclusion + * + * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and + * copying them to another part of the DOM, while maintaining their connection to the original AngularJS + * scope from where they were taken. + * + * Transclusion is used (often with {@link ngTransclude}) to insert the + * original contents of a directive's element into a specified place in the template of the directive. + * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded + * content has access to the properties on the scope from which it was taken, even if the directive + * has isolated scope. + * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}. + * + * This makes it possible for the widget to have private state for its template, while the transcluded + * content has access to its originating scope. + * + *
    + * **Note:** When testing an element transclude directive you must not place the directive at the root of the + * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives + * Testing Transclusion Directives}. + *
    + * + * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the + * directive's element, the entire element or multiple parts of the element contents: + * + * * `true` - transclude the content (i.e. the child nodes) of the directive's element. + * * `'element'` - transclude the whole of the directive's element including any directives on this + * element that defined at a lower priority than this directive. When used, the `template` + * property is ignored. + * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template. + * + * **Mult-slot transclusion** is declared by providing an object for the `transclude` property. + * + * This object is a map where the keys are the name of the slot to fill and the value is an element selector + * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`) + * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc). + * + * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} + * + * If the element selector is prefixed with a `?` then that slot is optional. + * + * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `` elements to + * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive. + * + * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements + * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call + * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and + * injectable into the directive's controller. + * + * + * #### Transclusion Functions + * + * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion + * function** to the directive's `link` function and `controller`. This transclusion function is a special + * **linking function** that will return the compiled contents linked to a new transclusion scope. + * + *
    + * If you are just using {@link ngTransclude} then you don't need to worry about this function, since + * ngTransclude will deal with it for us. + *
    + * + * If you want to manually control the insertion and removal of the transcluded content in your directive + * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery + * object that contains the compiled DOM, which is linked to the correct transclusion scope. + * + * When you call a transclusion function you can pass in a **clone attach function**. This function accepts + * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded + * content and the `scope` is the newly created transclusion scope, which the clone will be linked to. + * + *
    + * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function + * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. + *
    + * + * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone + * attach function**: + * + * ```js + * var transcludedContent, transclusionScope; + * + * $transclude(function(clone, scope) { + * element.append(clone); + * transcludedContent = clone; + * transclusionScope = scope; + * }); + * ``` + * + * Later, if you want to remove the transcluded content from your DOM then you should also destroy the + * associated transclusion scope: + * + * ```js + * transcludedContent.remove(); + * transclusionScope.$destroy(); + * ``` + * + *
    + * **Best Practice**: if you intend to add and remove transcluded content manually in your directive + * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it), + * then you are also responsible for calling `$destroy` on the transclusion scope. + *
    + * + * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat} + * automatically destroy their transcluded clones as necessary so you do not need to worry about this if + * you are simply using {@link ngTransclude} to inject the transclusion into your directive. + * + * + * #### Transclusion Scopes + * + * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion + * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed + * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it + * was taken. + * + * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look + * like this: + * + * ```html + *
    + *
    + *
    + *
    + *
    + *
    + * ``` + * + * The `$parent` scope hierarchy will look like this: + * + ``` + - $rootScope + - isolate + - transclusion + ``` + * + * but the scopes will inherit prototypically from different scopes to their `$parent`. + * + ``` + - $rootScope + - transclusion + - isolate + ``` + * + * + * ### Attributes + * + * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the + * `link()` or `compile()` functions. It has a variety of uses. + * + * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways: + * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access + * to the attributes. + * + * * *Directive inter-communication:* All directives share the same instance of the attributes + * object which allows the directives to use the attributes object as inter directive + * communication. + * + * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object + * allowing other directives to read the interpolated value. + * + * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes + * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also + * the only way to easily get the actual value because during the linking phase the interpolation + * hasn't been evaluated yet and so the value is at this time set to `undefined`. + * + * ```js + * function linkingFn(scope, elm, attrs, ctrl) { + * // get the attribute value + * console.log(attrs.ngModel); + * + * // change the attribute + * attrs.$set('ngModel', 'new value'); + * + * // observe changes to interpolated attribute + * attrs.$observe('ngModel', function(value) { + * console.log('ngModel has changed value to ' + value); + * }); + * } + * ``` + * + * ## Example + * + *
    + * **Note**: Typically directives are registered with `module.directive`. The example below is + * to illustrate how `$compile` works. + *
    + * + + + +
    +
    +
    +
    +
    +
    + + it('should auto compile', function() { + var textarea = $('textarea'); + var output = $('div[compile]'); + // The initial state reads 'Hello AngularJS'. + expect(output.getText()).toBe('Hello AngularJS'); + textarea.clear(); + textarea.sendKeys('{{name}}!'); + expect(output.getText()).toBe('AngularJS!'); + }); + +
    + + * + * + * @param {string|DOMElement} element Element or HTML string to compile into a template function. + * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. + * + *
    + * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it + * e.g. will not use the right outer scope. Please pass the transclude function as a + * `parentBoundTranscludeFn` to the link function instead. + *
    + * + * @param {number} maxPriority only apply directives lower than given priority (Only effects the + * root element(s), not their children) + * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template + * (a DOM element/tree) to a scope. Where: + * + * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. + * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the + * `template` and call the `cloneAttachFn` function allowing the caller to attach the + * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is + * called as:
    `cloneAttachFn(clonedElement, scope)` where: + * + * * `clonedElement` - is a clone of the original `element` passed into the compiler. + * * `scope` - is the current scope with which the linking function is working with. + * + * * `options` - An optional object hash with linking options. If `options` is provided, then the following + * keys may be used to control linking behavior: + * + * * `parentBoundTranscludeFn` - the transclude function made available to + * directives; if given, it will be passed through to the link functions of + * directives found in `element` during compilation. + * * `transcludeControllers` - an object hash with keys that map controller names + * to a hash with the key `instance`, which maps to the controller instance; + * if given, it will make the controllers available to directives on the compileNode: + * ``` + * { + * parent: { + * instance: parentControllerInstance + * } + * } + * ``` + * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add + * the cloned elements; only needed for transcludes that are allowed to contain non html + * elements (e.g. SVG elements). See also the directive.controller property. + * + * Calling the linking function returns the element of the template. It is either the original + * element passed in, or the clone of the element if the `cloneAttachFn` is provided. + * + * After linking the view is not updated until after a call to $digest which typically is done by + * AngularJS automatically. + * + * If you need access to the bound view, there are two ways to do it: + * + * - If you are not asking the linking function to clone the template, create the DOM element(s) + * before you send them to the compiler and keep this reference around. + * ```js + * var element = $compile('

    {{total}}

    ')(scope); + * ``` + * + * - if on the other hand, you need the element to be cloned, the view reference from the original + * example would not point to the clone, but rather to the original template that was cloned. In + * this case, you can access the clone via the cloneAttachFn: + * ```js + * var templateElement = angular.element('

    {{total}}

    '), + * scope = ....; + * + * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { + * //attach the clone to DOM document at the right place + * }); + * + * //now we have reference to the cloned DOM via `clonedElement` + * ``` + * + * + * For information on how the compiler works, see the + * {@link guide/compiler AngularJS HTML Compiler} section of the Developer Guide. + * + * @knownIssue + * + * ### Double Compilation + * + Double compilation occurs when an already compiled part of the DOM gets + compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues, + and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it + section on double compilation} for an in-depth explanation and ways to avoid it. + * + */ + +var $compileMinErr = minErr('$compile'); + +function UNINITIALIZED_VALUE() {} +var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE(); + +/** + * @ngdoc provider + * @name $compileProvider + * + * @description + */ +$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; +/** @this */ +function $CompileProvider($provide, $$sanitizeUriProvider) { + var hasDirectives = {}, + Suffix = 'Directive', + COMMENT_DIRECTIVE_REGEXP = /^\s*directive:\s*([\w-]+)\s+(.*)$/, + CLASS_DIRECTIVE_REGEXP = /(([\w-]+)(?::([^;]+))?;?)/, + ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), + REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; + + // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes + // The assumption is that future DOM event attribute names will begin with + // 'on' and be composed of only English letters. + var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; + var bindingCache = createMap(); + + function parseIsolateBindings(scope, directiveName, isController) { + var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/; + + var bindings = createMap(); + + forEach(scope, function(definition, scopeName) { + if (definition in bindingCache) { + bindings[scopeName] = bindingCache[definition]; + return; + } + var match = definition.match(LOCAL_REGEXP); + + if (!match) { + throw $compileMinErr('iscp', + 'Invalid {3} for directive \'{0}\'.' + + ' Definition: {... {1}: \'{2}\' ...}', + directiveName, scopeName, definition, + (isController ? 'controller bindings definition' : + 'isolate scope definition')); + } + + bindings[scopeName] = { + mode: match[1][0], + collection: match[2] === '*', + optional: match[3] === '?', + attrName: match[4] || scopeName + }; + if (match[4]) { + bindingCache[definition] = bindings[scopeName]; + } + }); + + return bindings; + } + + function parseDirectiveBindings(directive, directiveName) { + var bindings = { + isolateScope: null, + bindToController: null + }; + if (isObject(directive.scope)) { + if (directive.bindToController === true) { + bindings.bindToController = parseIsolateBindings(directive.scope, + directiveName, true); + bindings.isolateScope = {}; + } else { + bindings.isolateScope = parseIsolateBindings(directive.scope, + directiveName, false); + } + } + if (isObject(directive.bindToController)) { + bindings.bindToController = + parseIsolateBindings(directive.bindToController, directiveName, true); + } + if (bindings.bindToController && !directive.controller) { + // There is no controller + throw $compileMinErr('noctrl', + 'Cannot bind to controller without directive \'{0}\'s controller.', + directiveName); + } + return bindings; + } + + function assertValidDirectiveName(name) { + var letter = name.charAt(0); + if (!letter || letter !== lowercase(letter)) { + throw $compileMinErr('baddir', 'Directive/Component name \'{0}\' is invalid. The first character must be a lowercase letter', name); + } + if (name !== name.trim()) { + throw $compileMinErr('baddir', + 'Directive/Component name \'{0}\' is invalid. The name should not contain leading or trailing whitespaces', + name); + } + } + + function getDirectiveRequire(directive) { + var require = directive.require || (directive.controller && directive.name); + + if (!isArray(require) && isObject(require)) { + forEach(require, function(value, key) { + var match = value.match(REQUIRE_PREFIX_REGEXP); + var name = value.substring(match[0].length); + if (!name) require[key] = match[0] + key; + }); + } + + return require; + } + + function getDirectiveRestrict(restrict, name) { + if (restrict && !(isString(restrict) && /[EACM]/.test(restrict))) { + throw $compileMinErr('badrestrict', + 'Restrict property \'{0}\' of directive \'{1}\' is invalid', + restrict, + name); + } + + return restrict || 'EA'; + } + + /** + * @ngdoc method + * @name $compileProvider#directive + * @kind function + * + * @description + * Register a new directive with the compiler. + * + * @param {string|Object} name Name of the directive in camel-case (i.e. ngBind which + * will match as ng-bind), or an object map of directives where the keys are the + * names and the values are the factories. + * @param {Function|Array} directiveFactory An injectable directive factory function. See the + * {@link guide/directive directive guide} and the {@link $compile compile API} for more info. + * @returns {ng.$compileProvider} Self for chaining. + */ + this.directive = function registerDirective(name, directiveFactory) { + assertArg(name, 'name'); + assertNotHasOwnProperty(name, 'directive'); + if (isString(name)) { + assertValidDirectiveName(name); + assertArg(directiveFactory, 'directiveFactory'); + if (!hasDirectives.hasOwnProperty(name)) { + hasDirectives[name] = []; + $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', + function($injector, $exceptionHandler) { + var directives = []; + forEach(hasDirectives[name], function(directiveFactory, index) { + try { + var directive = $injector.invoke(directiveFactory); + if (isFunction(directive)) { + directive = { compile: valueFn(directive) }; + } else if (!directive.compile && directive.link) { + directive.compile = valueFn(directive.link); + } + directive.priority = directive.priority || 0; + directive.index = index; + directive.name = directive.name || name; + directive.require = getDirectiveRequire(directive); + directive.restrict = getDirectiveRestrict(directive.restrict, name); + directive.$$moduleName = directiveFactory.$$moduleName; + directives.push(directive); + } catch (e) { + $exceptionHandler(e); + } + }); + return directives; + }]); + } + hasDirectives[name].push(directiveFactory); + } else { + forEach(name, reverseParams(registerDirective)); + } + return this; + }; + + /** + * @ngdoc method + * @name $compileProvider#component + * @module ng + * @param {string|Object} name Name of the component in camelCase (i.e. `myComp` which will match ``), + * or an object map of components where the keys are the names and the values are the component definition objects. + * @param {Object} options Component definition object (a simplified + * {@link ng.$compile#directive-definition-object directive definition object}), + * with the following properties (all optional): + * + * - `controller` – `{(string|function()=}` – controller constructor function that should be + * associated with newly created scope or the name of a {@link ng.$compile#-controller- + * registered controller} if passed as a string. An empty `noop` function by default. + * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope. + * If present, the controller will be published to scope under the `controllerAs` name. + * If not present, this will default to be `$ctrl`. + * - `template` – `{string=|function()=}` – html template as a string or a function that + * returns an html template as a string which should be used as the contents of this component. + * Empty string by default. + * + * If `template` is a function, then it is {@link auto.$injector#invoke injected} with + * the following locals: + * + * - `$element` - Current element + * - `$attrs` - Current attributes object for the element + * + * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html + * template that should be used as the contents of this component. + * + * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with + * the following locals: + * + * - `$element` - Current element + * - `$attrs` - Current attributes object for the element + * + * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties. + * Component properties are always bound to the component controller and not to the scope. + * See {@link ng.$compile#-bindtocontroller- `bindToController`}. + * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled. + * Disabled by default. + * - `require` - `{Object=}` - requires the controllers of other directives and binds them to + * this component's controller. The object keys specify the property names under which the required + * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}. + * - `$...` – additional properties to attach to the directive factory function and the controller + * constructor function. (This is used by the component router to annotate) + * + * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls. + * @description + * Register a **component definition** with the compiler. This is a shorthand for registering a special + * type of directive, which represents a self-contained UI component in your application. Such components + * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`). + * + * Component definitions are very simple and do not require as much configuration as defining general + * directives. Component definitions usually consist only of a template and a controller backing it. + * + * In order to make the definition easier, components enforce best practices like use of `controllerAs`, + * `bindToController`. They always have **isolate scope** and are restricted to elements. + * + * Here are a few examples of how you would usually define components: + * + * ```js + * var myMod = angular.module(...); + * myMod.component('myComp', { + * template: '
    My name is {{$ctrl.name}}
    ', + * controller: function() { + * this.name = 'shahar'; + * } + * }); + * + * myMod.component('myComp', { + * template: '
    My name is {{$ctrl.name}}
    ', + * bindings: {name: '@'} + * }); + * + * myMod.component('myComp', { + * templateUrl: 'views/my-comp.html', + * controller: 'MyCtrl', + * controllerAs: 'ctrl', + * bindings: {name: '@'} + * }); + * + * ``` + * For more examples, and an in-depth guide, see the {@link guide/component component guide}. + * + *
    + * See also {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + this.component = function registerComponent(name, options) { + if (!isString(name)) { + forEach(name, reverseParams(bind(this, registerComponent))); + return this; + } + + var controller = options.controller || function() {}; + + function factory($injector) { + function makeInjectable(fn) { + if (isFunction(fn) || isArray(fn)) { + return /** @this */ function(tElement, tAttrs) { + return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs}); + }; + } else { + return fn; + } + } + + var template = (!options.template && !options.templateUrl ? '' : options.template); + var ddo = { + controller: controller, + controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl', + template: makeInjectable(template), + templateUrl: makeInjectable(options.templateUrl), + transclude: options.transclude, + scope: {}, + bindToController: options.bindings || {}, + restrict: 'E', + require: options.require + }; + + // Copy annotations (starting with $) over to the DDO + forEach(options, function(val, key) { + if (key.charAt(0) === '$') ddo[key] = val; + }); + + return ddo; + } + + // TODO(pete) remove the following `forEach` before we release 1.6.0 + // The component-router@0.2.0 looks for the annotations on the controller constructor + // Nothing in AngularJS looks for annotations on the factory function but we can't remove + // it from 1.5.x yet. + + // Copy any annotation properties (starting with $) over to the factory and controller constructor functions + // These could be used by libraries such as the new component router + forEach(options, function(val, key) { + if (key.charAt(0) === '$') { + factory[key] = val; + // Don't try to copy over annotations to named controller + if (isFunction(controller)) controller[key] = val; + } + }); + + factory.$inject = ['$injector']; + + return this.directive(name, factory); + }; + + + /** + * @ngdoc method + * @name $compileProvider#aHrefSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at preventing XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.aHrefSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); + } + }; + + + /** + * @ngdoc method + * @name $compileProvider#imgSrcSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during img[src] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.imgSrcSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); + } + }; + + /** + * @ngdoc method + * @name $compileProvider#debugInfoEnabled + * + * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the + * current debugInfoEnabled state + * @returns {*} current value if used as getter or itself (chaining) if used as setter + * + * @kind function + * + * @description + * Call this method to enable/disable various debug runtime information in the compiler such as adding + * binding information and a reference to the current scope on to DOM elements. + * If enabled, the compiler will add the following to DOM elements that have been bound to the scope + * * `ng-binding` CSS class + * * `ng-scope` and `ng-isolated-scope` CSS classes + * * `$binding` data property containing an array of the binding expressions + * * Data properties used by the {@link angular.element#methods `scope()`/`isolateScope()` methods} to return + * the element's scope. + * * Placeholder comments will contain information about what directive and binding caused the placeholder. + * E.g. ``. + * + * You may want to disable this in production for a significant performance boost. See + * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. + * + * The default value is true. + */ + var debugInfoEnabled = true; + this.debugInfoEnabled = function(enabled) { + if (isDefined(enabled)) { + debugInfoEnabled = enabled; + return this; + } + return debugInfoEnabled; + }; + + /** + * @ngdoc method + * @name $compileProvider#preAssignBindingsEnabled + * + * @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the + * current preAssignBindingsEnabled state + * @returns {*} current value if used as getter or itself (chaining) if used as setter + * + * @kind function + * + * @description + * Call this method to enable/disable whether directive controllers are assigned bindings before + * calling the controller's constructor. + * If enabled (true), the compiler assigns the value of each of the bindings to the + * properties of the controller object before the constructor of this object is called. + * + * If disabled (false), the compiler calls the constructor first before assigning bindings. + * + * The default value is false. + * + * @deprecated + * sinceVersion="1.6.0" + * removeVersion="1.7.0" + * + * This method and the option to assign the bindings before calling the controller's constructor + * will be removed in v1.7.0. + */ + var preAssignBindingsEnabled = false; + this.preAssignBindingsEnabled = function(enabled) { + if (isDefined(enabled)) { + preAssignBindingsEnabled = enabled; + return this; + } + return preAssignBindingsEnabled; + }; + + /** + * @ngdoc method + * @name $compileProvider#strictComponentBindingsEnabled + * + * @param {boolean=} enabled update the strictComponentBindingsEnabled state if provided, otherwise just return the + * current strictComponentBindingsEnabled state + * @returns {*} current value if used as getter or itself (chaining) if used as setter + * + * @kind function + * + * @description + * Call this method to enable/disable strict component bindings check. If enabled, the compiler will enforce that + * for all bindings of a component that are not set as optional with `?`, an attribute needs to be provided + * on the component's HTML tag. + * + * The default value is false. + */ + var strictComponentBindingsEnabled = false; + this.strictComponentBindingsEnabled = function(enabled) { + if (isDefined(enabled)) { + strictComponentBindingsEnabled = enabled; + return this; + } + return strictComponentBindingsEnabled; + }; + + var TTL = 10; + /** + * @ngdoc method + * @name $compileProvider#onChangesTtl + * @description + * + * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result + * in several iterations of calls to these hooks. However if an application needs more than the default 10 + * iterations to stabilize then you should investigate what is causing the model to continuously change during + * the `$onChanges` hook execution. + * + * Increasing the TTL could have performance implications, so you should not change it without proper justification. + * + * @param {number} limit The number of `$onChanges` hook iterations. + * @returns {number|object} the current limit (or `this` if called as a setter for chaining) + */ + this.onChangesTtl = function(value) { + if (arguments.length) { + TTL = value; + return this; + } + return TTL; + }; + + var commentDirectivesEnabledConfig = true; + /** + * @ngdoc method + * @name $compileProvider#commentDirectivesEnabled + * @description + * + * It indicates to the compiler + * whether or not directives on comments should be compiled. + * Defaults to `true`. + * + * Calling this function with false disables the compilation of directives + * on comments for the whole application. + * This results in a compilation performance gain, + * as the compiler doesn't have to check comments when looking for directives. + * This should however only be used if you are sure that no comment directives are used in + * the application (including any 3rd party directives). + * + * @param {boolean} enabled `false` if the compiler may ignore directives on comments + * @returns {boolean|object} the current value (or `this` if called as a setter for chaining) + */ + this.commentDirectivesEnabled = function(value) { + if (arguments.length) { + commentDirectivesEnabledConfig = value; + return this; + } + return commentDirectivesEnabledConfig; + }; + + + var cssClassDirectivesEnabledConfig = true; + /** + * @ngdoc method + * @name $compileProvider#cssClassDirectivesEnabled + * @description + * + * It indicates to the compiler + * whether or not directives on element classes should be compiled. + * Defaults to `true`. + * + * Calling this function with false disables the compilation of directives + * on element classes for the whole application. + * This results in a compilation performance gain, + * as the compiler doesn't have to check element classes when looking for directives. + * This should however only be used if you are sure that no class directives are used in + * the application (including any 3rd party directives). + * + * @param {boolean} enabled `false` if the compiler may ignore directives on element classes + * @returns {boolean|object} the current value (or `this` if called as a setter for chaining) + */ + this.cssClassDirectivesEnabled = function(value) { + if (arguments.length) { + cssClassDirectivesEnabledConfig = value; + return this; + } + return cssClassDirectivesEnabledConfig; + }; + + this.$get = [ + '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse', + '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri', + function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse, + $controller, $rootScope, $sce, $animate, $$sanitizeUri) { + + var SIMPLE_ATTR_NAME = /^\w/; + var specialAttrHolder = window.document.createElement('div'); + + + var commentDirectivesEnabled = commentDirectivesEnabledConfig; + var cssClassDirectivesEnabled = cssClassDirectivesEnabledConfig; + + + var onChangesTtl = TTL; + // The onChanges hooks should all be run together in a single digest + // When changes occur, the call to trigger their hooks will be added to this queue + var onChangesQueue; + + // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest + function flushOnChangesQueue() { + try { + if (!(--onChangesTtl)) { + // We have hit the TTL limit so reset everything + onChangesQueue = undefined; + throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL); + } + // We must run this hook in an apply since the $$postDigest runs outside apply + $rootScope.$apply(function() { + var errors = []; + for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) { + try { + onChangesQueue[i](); + } catch (e) { + errors.push(e); + } + } + // Reset the queue to trigger a new schedule next time there is a change + onChangesQueue = undefined; + if (errors.length) { + throw errors; + } + }); + } finally { + onChangesTtl++; + } + } + + + function Attributes(element, attributesToCopy) { + if (attributesToCopy) { + var keys = Object.keys(attributesToCopy); + var i, l, key; + + for (i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + this[key] = attributesToCopy[key]; + } + } else { + this.$attr = {}; + } + + this.$$element = element; + } + + Attributes.prototype = { + /** + * @ngdoc method + * @name $compile.directive.Attributes#$normalize + * @kind function + * + * @description + * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or + * `data-`) to its normalized, camelCase form. + * + * Also there is special case for Moz prefix starting with upper case letter. + * + * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} + * + * @param {string} name Name to normalize + */ + $normalize: directiveNormalize, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$addClass + * @kind function + * + * @description + * Adds the CSS class value specified by the classVal parameter to the element. If animations + * are enabled then an animation will be triggered for the class addition. + * + * @param {string} classVal The className value that will be added to the element + */ + $addClass: function(classVal) { + if (classVal && classVal.length > 0) { + $animate.addClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$removeClass + * @kind function + * + * @description + * Removes the CSS class value specified by the classVal parameter from the element. If + * animations are enabled then an animation will be triggered for the class removal. + * + * @param {string} classVal The className value that will be removed from the element + */ + $removeClass: function(classVal) { + if (classVal && classVal.length > 0) { + $animate.removeClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$updateClass + * @kind function + * + * @description + * Adds and removes the appropriate CSS class values to the element based on the difference + * between the new and old CSS class values (specified as newClasses and oldClasses). + * + * @param {string} newClasses The current CSS className value + * @param {string} oldClasses The former CSS className value + */ + $updateClass: function(newClasses, oldClasses) { + var toAdd = tokenDifference(newClasses, oldClasses); + if (toAdd && toAdd.length) { + $animate.addClass(this.$$element, toAdd); + } + + var toRemove = tokenDifference(oldClasses, newClasses); + if (toRemove && toRemove.length) { + $animate.removeClass(this.$$element, toRemove); + } + }, + + /** + * Set a normalized attribute on the element in a way such that all directives + * can share the attribute. This function properly handles boolean attributes. + * @param {string} key Normalized key. (ie ngAttribute) + * @param {string|boolean} value The value to set. If `null` attribute will be deleted. + * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. + * Defaults to true. + * @param {string=} attrName Optional none normalized name. Defaults to key. + */ + $set: function(key, value, writeAttr, attrName) { + // TODO: decide whether or not to throw an error if "class" + //is set through this function since it may cause $updateClass to + //become unstable. + + var node = this.$$element[0], + booleanKey = getBooleanAttrName(node, key), + aliasedKey = getAliasedAttrName(key), + observer = key, + nodeName; + + if (booleanKey) { + this.$$element.prop(key, value); + attrName = booleanKey; + } else if (aliasedKey) { + this[aliasedKey] = value; + observer = aliasedKey; + } + + this[key] = value; + + // translate normalized key to actual key + if (attrName) { + this.$attr[key] = attrName; + } else { + attrName = this.$attr[key]; + if (!attrName) { + this.$attr[key] = attrName = snake_case(key, '-'); + } + } + + nodeName = nodeName_(this.$$element); + + if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) || + (nodeName === 'img' && key === 'src')) { + // sanitize a[href] and img[src] values + this[key] = value = $$sanitizeUri(value, key === 'src'); + } else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) { + // sanitize img[srcset] values + var result = ''; + + // first check if there are spaces because it's not the same pattern + var trimmedSrcset = trim(value); + // ( 999x ,| 999w ,| ,|, ) + var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/; + var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/; + + // split srcset into tuple of uri and descriptor except for the last item + var rawUris = trimmedSrcset.split(pattern); + + // for each tuples + var nbrUrisWith2parts = Math.floor(rawUris.length / 2); + for (var i = 0; i < nbrUrisWith2parts; i++) { + var innerIdx = i * 2; + // sanitize the uri + result += $$sanitizeUri(trim(rawUris[innerIdx]), true); + // add the descriptor + result += (' ' + trim(rawUris[innerIdx + 1])); + } + + // split the last item into uri and descriptor + var lastTuple = trim(rawUris[i * 2]).split(/\s/); + + // sanitize the last uri + result += $$sanitizeUri(trim(lastTuple[0]), true); + + // and add the last descriptor if any + if (lastTuple.length === 2) { + result += (' ' + trim(lastTuple[1])); + } + this[key] = value = result; + } + + if (writeAttr !== false) { + if (value === null || isUndefined(value)) { + this.$$element.removeAttr(attrName); + } else { + if (SIMPLE_ATTR_NAME.test(attrName)) { + this.$$element.attr(attrName, value); + } else { + setSpecialAttr(this.$$element[0], attrName, value); + } + } + } + + // fire observers + var $$observers = this.$$observers; + if ($$observers) { + forEach($$observers[observer], function(fn) { + try { + fn(value); + } catch (e) { + $exceptionHandler(e); + } + }); + } + }, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$observe + * @kind function + * + * @description + * Observes an interpolated attribute. + * + * The observer function will be invoked once during the next `$digest` following + * compilation. The observer is then invoked whenever the interpolated value + * changes. + * + * @param {string} key Normalized key. (ie ngAttribute) . + * @param {function(interpolatedValue)} fn Function that will be called whenever + the interpolated value of the attribute changes. + * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation + * guide} for more info. + * @returns {function()} Returns a deregistration function for this observer. + */ + $observe: function(key, fn) { + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = createMap())), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); + + return function() { + arrayRemove(listeners, fn); + }; + } + }; + + function setSpecialAttr(element, attrName, value) { + // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute` + // so we have to jump through some hoops to get such an attribute + // https://github.com/angular/angular.js/pull/13318 + specialAttrHolder.innerHTML = ''; + var attributes = specialAttrHolder.firstChild.attributes; + var attribute = attributes[0]; + // We have to remove the attribute from its container element before we can add it to the destination element + attributes.removeNamedItem(attribute.name); + attribute.value = value; + element.attributes.setNamedItem(attribute); + } + + function safeAddClass($element, className) { + try { + $element.addClass(className); + } catch (e) { + // ignore, since it means that we are trying to set class on + // SVG element, where class name is read-only. + } + } + + + var startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + denormalizeTemplate = (startSymbol === '{{' && endSymbol === '}}') + ? identity + : function denormalizeTemplate(template) { + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + }, + NG_ATTR_BINDING = /^ngAttr[A-Z]/; + var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/; + + compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) { + var bindings = $element.data('$binding') || []; + + if (isArray(binding)) { + bindings = bindings.concat(binding); + } else { + bindings.push(binding); + } + + $element.data('$binding', bindings); + } : noop; + + compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) { + safeAddClass($element, 'ng-binding'); + } : noop; + + compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) { + var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope'; + $element.data(dataName, scope); + } : noop; + + compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) { + safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope'); + } : noop; + + compile.$$createComment = function(directiveName, comment) { + var content = ''; + if (debugInfoEnabled) { + content = ' ' + (directiveName || '') + ': '; + if (comment) content += comment + ' '; + } + return window.document.createComment(content); + }; + + return compile; + + //================================ + + function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, + previousCompileContext) { + if (!($compileNodes instanceof jqLite)) { + // jquery always rewraps, whereas we need to preserve the original selector so that we can + // modify it. + $compileNodes = jqLite($compileNodes); + } + var compositeLinkFn = + compileNodes($compileNodes, transcludeFn, $compileNodes, + maxPriority, ignoreDirective, previousCompileContext); + compile.$$addScopeClass($compileNodes); + var namespace = null; + return function publicLinkFn(scope, cloneConnectFn, options) { + if (!$compileNodes) { + throw $compileMinErr('multilink', 'This element has already been linked.'); + } + assertArg(scope, 'scope'); + + if (previousCompileContext && previousCompileContext.needsNewScope) { + // A parent directive did a replace and a directive on this element asked + // for transclusion, which caused us to lose a layer of element on which + // we could hold the new transclusion scope, so we will create it manually + // here. + scope = scope.$parent.$new(); + } + + options = options || {}; + var parentBoundTranscludeFn = options.parentBoundTranscludeFn, + transcludeControllers = options.transcludeControllers, + futureParentElement = options.futureParentElement; + + // When `parentBoundTranscludeFn` is passed, it is a + // `controllersBoundTransclude` function (it was previously passed + // as `transclude` to directive.link) so we must unwrap it to get + // its `boundTranscludeFn` + if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) { + parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude; + } + + if (!namespace) { + namespace = detectNamespaceForChildElements(futureParentElement); + } + var $linkNode; + if (namespace !== 'html') { + // When using a directive with replace:true and templateUrl the $compileNodes + // (or a child element inside of them) + // might change, so we need to recreate the namespace adapted compileNodes + // for call to the link function. + // Note: This will already clone the nodes... + $linkNode = jqLite( + wrapTemplate(namespace, jqLite('
    ').append($compileNodes).html()) + ); + } else if (cloneConnectFn) { + // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart + // and sometimes changes the structure of the DOM. + $linkNode = JQLitePrototype.clone.call($compileNodes); + } else { + $linkNode = $compileNodes; + } + + if (transcludeControllers) { + for (var controllerName in transcludeControllers) { + $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance); + } + } + + compile.$$addScopeInfo($linkNode, scope); + + if (cloneConnectFn) cloneConnectFn($linkNode, scope); + if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); + + if (!cloneConnectFn) { + $compileNodes = compositeLinkFn = null; + } + return $linkNode; + }; + } + + function detectNamespaceForChildElements(parentElement) { + // TODO: Make this detect MathML as well... + var node = parentElement && parentElement[0]; + if (!node) { + return 'html'; + } else { + return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html'; + } + } + + /** + * Compile function matches each node in nodeList against the directives. Once all directives + * for a particular node are collected their compile functions are executed. The compile + * functions return values - the linking functions - are combined into a composite linking + * function, which is the a linking function for the node. + * + * @param {NodeList} nodeList an array of nodes or NodeList to compile + * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the + * scope argument is auto-generated to the new child of the transcluded parent scope. + * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then + * the rootElement must be set the jqLite collection of the compile root. This is + * needed so that the jqLite collection items can be replaced with widgets. + * @param {number=} maxPriority Max directive priority. + * @returns {Function} A composite linking function of all of the matched directives or null. + */ + function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, + previousCompileContext) { + var linkFns = [], + // `nodeList` can be either an element's `.childNodes` (live NodeList) + // or a jqLite/jQuery collection or an array + notLiveList = isArray(nodeList) || (nodeList instanceof jqLite), + attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound; + + + for (var i = 0; i < nodeList.length; i++) { + attrs = new Attributes(); + + // Support: IE 11 only + // Workaround for #11781 and #14924 + if (msie === 11) { + mergeConsecutiveTextNodes(nodeList, i, notLiveList); + } + + // We must always refer to `nodeList[i]` hereafter, + // since the nodes can be replaced underneath us. + directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, + ignoreDirective); + + nodeLinkFn = (directives.length) + ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, + null, [], [], previousCompileContext) + : null; + + if (nodeLinkFn && nodeLinkFn.scope) { + compile.$$addScopeClass(attrs.$$element); + } + + childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || + !(childNodes = nodeList[i].childNodes) || + !childNodes.length) + ? null + : compileNodes(childNodes, + nodeLinkFn ? ( + (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement) + && nodeLinkFn.transclude) : transcludeFn); + + if (nodeLinkFn || childLinkFn) { + linkFns.push(i, nodeLinkFn, childLinkFn); + linkFnFound = true; + nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn; + } + + //use the previous context only for the first element in the virtual group + previousCompileContext = null; + } + + // return a linking function if we have found anything, null otherwise + return linkFnFound ? compositeLinkFn : null; + + function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) { + var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn; + var stableNodeList; + + + if (nodeLinkFnFound) { + // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our + // offsets don't get screwed up + var nodeListLength = nodeList.length; + stableNodeList = new Array(nodeListLength); + + // create a sparse array by only copying the elements which have a linkFn + for (i = 0; i < linkFns.length; i += 3) { + idx = linkFns[i]; + stableNodeList[idx] = nodeList[idx]; + } + } else { + stableNodeList = nodeList; + } + + for (i = 0, ii = linkFns.length; i < ii;) { + node = stableNodeList[linkFns[i++]]; + nodeLinkFn = linkFns[i++]; + childLinkFn = linkFns[i++]; + + if (nodeLinkFn) { + if (nodeLinkFn.scope) { + childScope = scope.$new(); + compile.$$addScopeInfo(jqLite(node), childScope); + } else { + childScope = scope; + } + + if (nodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn( + scope, nodeLinkFn.transclude, parentBoundTranscludeFn); + + } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { + childBoundTranscludeFn = parentBoundTranscludeFn; + + } else if (!parentBoundTranscludeFn && transcludeFn) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn); + + } else { + childBoundTranscludeFn = null; + } + + nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn); + + } else if (childLinkFn) { + childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); + } + } + } + } + + function mergeConsecutiveTextNodes(nodeList, idx, notLiveList) { + var node = nodeList[idx]; + var parent = node.parentNode; + var sibling; + + if (node.nodeType !== NODE_TYPE_TEXT) { + return; + } + + while (true) { + sibling = parent ? node.nextSibling : nodeList[idx + 1]; + if (!sibling || sibling.nodeType !== NODE_TYPE_TEXT) { + break; + } + + node.nodeValue = node.nodeValue + sibling.nodeValue; + + if (sibling.parentNode) { + sibling.parentNode.removeChild(sibling); + } + if (notLiveList && sibling === nodeList[idx + 1]) { + nodeList.splice(idx + 1, 1); + } + } + } + + function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) { + function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) { + + if (!transcludedScope) { + transcludedScope = scope.$new(false, containingScope); + transcludedScope.$$transcluded = true; + } + + return transcludeFn(transcludedScope, cloneFn, { + parentBoundTranscludeFn: previousBoundTranscludeFn, + transcludeControllers: controllers, + futureParentElement: futureParentElement + }); + } + + // We need to attach the transclusion slots onto the `boundTranscludeFn` + // so that they are available inside the `controllersBoundTransclude` function + var boundSlots = boundTranscludeFn.$$slots = createMap(); + for (var slotName in transcludeFn.$$slots) { + if (transcludeFn.$$slots[slotName]) { + boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn); + } else { + boundSlots[slotName] = null; + } + } + + return boundTranscludeFn; + } + + /** + * Looks for directives on the given node and adds them to the directive collection which is + * sorted. + * + * @param node Node to search. + * @param directives An array to which the directives are added to. This array is sorted before + * the function returns. + * @param attrs The shared attrs object which is used to populate the normalized attributes. + * @param {number=} maxPriority Max directive priority. + */ + function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) { + var nodeType = node.nodeType, + attrsMap = attrs.$attr, + match, + nodeName, + className; + + switch (nodeType) { + case NODE_TYPE_ELEMENT: /* Element */ + + nodeName = nodeName_(node); + + // use the node name: + addDirective(directives, + directiveNormalize(nodeName), 'E', maxPriority, ignoreDirective); + + // iterate over the attributes + for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, + j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { + var attrStartName = false; + var attrEndName = false; + + attr = nAttrs[j]; + name = attr.name; + value = attr.value; + + // support ngAttr attribute binding + ngAttrName = directiveNormalize(name); + isNgAttr = NG_ATTR_BINDING.test(ngAttrName); + if (isNgAttr) { + name = name.replace(PREFIX_REGEXP, '') + .substr(8).replace(/_(.)/g, function(match, letter) { + return letter.toUpperCase(); + }); + } + + var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE); + if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) { + attrStartName = name; + attrEndName = name.substr(0, name.length - 5) + 'end'; + name = name.substr(0, name.length - 6); + } + + nName = directiveNormalize(name.toLowerCase()); + attrsMap[nName] = name; + if (isNgAttr || !attrs.hasOwnProperty(nName)) { + attrs[nName] = value; + if (getBooleanAttrName(node, nName)) { + attrs[nName] = true; // presence means true + } + } + addAttrInterpolateDirective(node, directives, value, nName, isNgAttr); + addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, + attrEndName); + } + + if (nodeName === 'input' && node.getAttribute('type') === 'hidden') { + // Hidden input elements can have strange behaviour when navigating back to the page + // This tells the browser not to try to cache and reinstate previous values + node.setAttribute('autocomplete', 'off'); + } + + // use class as directive + if (!cssClassDirectivesEnabled) break; + className = node.className; + if (isObject(className)) { + // Maybe SVGAnimatedString + className = className.animVal; + } + if (isString(className) && className !== '') { + while ((match = CLASS_DIRECTIVE_REGEXP.exec(className))) { + nName = directiveNormalize(match[2]); + if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[3]); + } + className = className.substr(match.index + match[0].length); + } + } + break; + case NODE_TYPE_TEXT: /* Text Node */ + addTextInterpolateDirective(directives, node.nodeValue); + break; + case NODE_TYPE_COMMENT: /* Comment */ + if (!commentDirectivesEnabled) break; + collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective); + break; + } + + directives.sort(byPriority); + return directives; + } + + function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) { + // function created because of performance, try/catch disables + // the optimization of the whole function #14848 + try { + var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); + if (match) { + var nName = directiveNormalize(match[1]); + if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[2]); + } + } + } catch (e) { + // turns out that under some circumstances IE9 throws errors when one attempts to read + // comment's node value. + // Just ignore it and continue. (Can't seem to reproduce in test case.) + } + } + + /** + * Given a node with a directive-start it collects all of the siblings until it finds + * directive-end. + * @param node + * @param attrStart + * @param attrEnd + * @returns {*} + */ + function groupScan(node, attrStart, attrEnd) { + var nodes = []; + var depth = 0; + if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { + do { + if (!node) { + throw $compileMinErr('uterdir', + 'Unterminated attribute, found \'{0}\' but no matching \'{1}\' found.', + attrStart, attrEnd); + } + if (node.nodeType === NODE_TYPE_ELEMENT) { + if (node.hasAttribute(attrStart)) depth++; + if (node.hasAttribute(attrEnd)) depth--; + } + nodes.push(node); + node = node.nextSibling; + } while (depth > 0); + } else { + nodes.push(node); + } + + return jqLite(nodes); + } + + /** + * Wrapper for linking function which converts normal linking function into a grouped + * linking function. + * @param linkFn + * @param attrStart + * @param attrEnd + * @returns {Function} + */ + function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { + return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) { + element = groupScan(element[0], attrStart, attrEnd); + return linkFn(scope, element, attrs, controllers, transcludeFn); + }; + } + + /** + * A function generator that is used to support both eager and lazy compilation + * linking function. + * @param eager + * @param $compileNodes + * @param transcludeFn + * @param maxPriority + * @param ignoreDirective + * @param previousCompileContext + * @returns {Function} + */ + function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) { + var compiled; + + if (eager) { + return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext); + } + return /** @this */ function lazyCompilation() { + if (!compiled) { + compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext); + + // Null out all of these references in order to make them eligible for garbage collection + // since this is a potentially long lived closure + $compileNodes = transcludeFn = previousCompileContext = null; + } + return compiled.apply(this, arguments); + }; + } + + /** + * Once the directives have been collected, their compile functions are executed. This method + * is responsible for inlining directive templates as well as terminating the application + * of the directives if the terminal directive has been reached. + * + * @param {Array} directives Array of collected directives to execute their compile function. + * this needs to be pre-sorted by priority order. + * @param {Node} compileNode The raw DOM node to apply the compile functions to + * @param {Object} templateAttrs The shared attribute function + * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the + * scope argument is auto-generated to the new + * child of the transcluded parent scope. + * @param {JQLite} jqCollection If we are working on the root of the compile tree then this + * argument has the root jqLite array so that we can replace nodes + * on it. + * @param {Object=} originalReplaceDirective An optional directive that will be ignored when + * compiling the transclusion. + * @param {Array.} preLinkFns + * @param {Array.} postLinkFns + * @param {Object} previousCompileContext Context used for previous compilation of the current + * node + * @returns {Function} linkFn + */ + function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, + jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, + previousCompileContext) { + previousCompileContext = previousCompileContext || {}; + + var terminalPriority = -Number.MAX_VALUE, + newScopeDirective = previousCompileContext.newScopeDirective, + controllerDirectives = previousCompileContext.controllerDirectives, + newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, + templateDirective = previousCompileContext.templateDirective, + nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, + hasTranscludeDirective = false, + hasTemplate = false, + hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, + $compileNode = templateAttrs.$$element = jqLite(compileNode), + directive, + directiveName, + $template, + replaceDirective = originalReplaceDirective, + childTranscludeFn = transcludeFn, + linkFn, + didScanForMultipleTransclusion = false, + mightHaveMultipleTransclusionError = false, + directiveValue; + + // executes all directives on the current element + for (var i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + var attrStart = directive.$$start; + var attrEnd = directive.$$end; + + // collect multiblock sections + if (attrStart) { + $compileNode = groupScan(compileNode, attrStart, attrEnd); + } + $template = undefined; + + if (terminalPriority > directive.priority) { + break; // prevent further processing of directives + } + + directiveValue = directive.scope; + + if (directiveValue) { + + // skip the check for directives with async templates, we'll check the derived sync + // directive when the template arrives + if (!directive.templateUrl) { + if (isObject(directiveValue)) { + // This directive is trying to add an isolated scope. + // Check that there is no scope of any kind already + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective, + directive, $compileNode); + newIsolateScopeDirective = directive; + } else { + // This directive is trying to add a child scope. + // Check that there is no isolated scope already + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, + $compileNode); + } + } + + newScopeDirective = newScopeDirective || directive; + } + + directiveName = directive.name; + + // If we encounter a condition that can result in transclusion on the directive, + // then scan ahead in the remaining directives for others that may cause a multiple + // transclusion error to be thrown during the compilation process. If a matching directive + // is found, then we know that when we encounter a transcluded directive, we need to eagerly + // compile the `transclude` function rather than doing it lazily in order to throw + // exceptions at the correct time + if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template)) + || (directive.transclude && !directive.$$tlb))) { + var candidateDirective; + + for (var scanningIndex = i + 1; (candidateDirective = directives[scanningIndex++]);) { + if ((candidateDirective.transclude && !candidateDirective.$$tlb) + || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) { + mightHaveMultipleTransclusionError = true; + break; + } + } + + didScanForMultipleTransclusion = true; + } + + if (!directive.templateUrl && directive.controller) { + controllerDirectives = controllerDirectives || createMap(); + assertNoDuplicate('\'' + directiveName + '\' controller', + controllerDirectives[directiveName], directive, $compileNode); + controllerDirectives[directiveName] = directive; + } + + directiveValue = directive.transclude; + + if (directiveValue) { + hasTranscludeDirective = true; + + // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. + // This option should only be used by directives that know how to safely handle element transclusion, + // where the transcluded nodes are added or replaced after linking. + if (!directive.$$tlb) { + assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); + nonTlbTranscludeDirective = directive; + } + + if (directiveValue === 'element') { + hasElementTranscludeDirective = true; + terminalPriority = directive.priority; + $template = $compileNode; + $compileNode = templateAttrs.$$element = + jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName])); + compileNode = $compileNode[0]; + replaceWith(jqCollection, sliceArgs($template), compileNode); + + // Support: Chrome < 50 + // https://github.com/angular/angular.js/issues/14041 + + // In the versions of V8 prior to Chrome 50, the document fragment that is created + // in the `replaceWith` function is improperly garbage collected despite still + // being referenced by the `parentNode` property of all of the child nodes. By adding + // a reference to the fragment via a different property, we can avoid that incorrect + // behavior. + // TODO: remove this line after Chrome 50 has been released + $template[0].$$parentNode = $template[0].parentNode; + + childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority, + replaceDirective && replaceDirective.name, { + // Don't pass in: + // - controllerDirectives - otherwise we'll create duplicates controllers + // - newIsolateScopeDirective or templateDirective - combining templates with + // element transclusion doesn't make sense. + // + // We need only nonTlbTranscludeDirective so that we prevent putting transclusion + // on the same element more than once. + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + } else { + + var slots = createMap(); + + if (!isObject(directiveValue)) { + $template = jqLite(jqLiteClone(compileNode)).contents(); + } else { + + // We have transclusion slots, + // collect them up, compile them and store their transclusion functions + $template = []; + + var slotMap = createMap(); + var filledSlots = createMap(); + + // Parse the element selectors + forEach(directiveValue, function(elementSelector, slotName) { + // If an element selector starts with a ? then it is optional + var optional = (elementSelector.charAt(0) === '?'); + elementSelector = optional ? elementSelector.substring(1) : elementSelector; + + slotMap[elementSelector] = slotName; + + // We explicitly assign `null` since this implies that a slot was defined but not filled. + // Later when calling boundTransclusion functions with a slot name we only error if the + // slot is `undefined` + slots[slotName] = null; + + // filledSlots contains `true` for all slots that are either optional or have been + // filled. This is used to check that we have not missed any required slots + filledSlots[slotName] = optional; + }); + + // Add the matching elements into their slot + forEach($compileNode.contents(), function(node) { + var slotName = slotMap[directiveNormalize(nodeName_(node))]; + if (slotName) { + filledSlots[slotName] = true; + slots[slotName] = slots[slotName] || []; + slots[slotName].push(node); + } else { + $template.push(node); + } + }); + + // Check for required slots that were not filled + forEach(filledSlots, function(filled, slotName) { + if (!filled) { + throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName); + } + }); + + for (var slotName in slots) { + if (slots[slotName]) { + // Only define a transclusion function if the slot was filled + slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn); + } + } + } + + $compileNode.empty(); // clear contents + childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined, + undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope}); + childTranscludeFn.$$slots = slots; + } + } + + if (directive.template) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + directiveValue = (isFunction(directive.template)) + ? directive.template($compileNode, templateAttrs) + : directive.template; + + directiveValue = denormalizeTemplate(directiveValue); + + if (directive.replace) { + replaceDirective = directive; + if (jqLiteIsTextNode(directiveValue)) { + $template = []; + } else { + $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue))); + } + compileNode = $template[0]; + + if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { + throw $compileMinErr('tplrt', + 'Template for directive \'{0}\' must have exactly one root element. {1}', + directiveName, ''); + } + + replaceWith(jqCollection, $compileNode, compileNode); + + var newTemplateAttrs = {$attr: {}}; + + // combine directives from the original node and from the template: + // - take the array of directives for this element + // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) + // - collect directives from the template and sort them by priority + // - combine directives as: processed + template + unprocessed + var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); + var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); + + if (newIsolateScopeDirective || newScopeDirective) { + // The original directive caused the current element to be replaced but this element + // also needs to have a new scope, so we need to tell the template directives + // that they would need to get their scope from further up, if they require transclusion + markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective); + } + directives = directives.concat(templateDirectives).concat(unprocessedDirectives); + mergeTemplateAttributes(templateAttrs, newTemplateAttrs); + + ii = directives.length; + } else { + $compileNode.html(directiveValue); + } + } + + if (directive.templateUrl) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + if (directive.replace) { + replaceDirective = directive; + } + + // eslint-disable-next-line no-func-assign + nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, + templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { + controllerDirectives: controllerDirectives, + newScopeDirective: (newScopeDirective !== directive) && newScopeDirective, + newIsolateScopeDirective: newIsolateScopeDirective, + templateDirective: templateDirective, + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + ii = directives.length; + } else if (directive.compile) { + try { + linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); + var context = directive.$$originalDirective || directive; + if (isFunction(linkFn)) { + addLinkFns(null, bind(context, linkFn), attrStart, attrEnd); + } else if (linkFn) { + addLinkFns(bind(context, linkFn.pre), bind(context, linkFn.post), attrStart, attrEnd); + } + } catch (e) { + $exceptionHandler(e, startingTag($compileNode)); + } + } + + if (directive.terminal) { + nodeLinkFn.terminal = true; + terminalPriority = Math.max(terminalPriority, directive.priority); + } + + } + + nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; + nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; + nodeLinkFn.templateOnThisElement = hasTemplate; + nodeLinkFn.transclude = childTranscludeFn; + + previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; + + // might be normal or delayed nodeLinkFn depending on if templateUrl is present + return nodeLinkFn; + + //////////////////// + + function addLinkFns(pre, post, attrStart, attrEnd) { + if (pre) { + if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); + pre.require = directive.require; + pre.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + pre = cloneAndAnnotateFn(pre, {isolateScope: true}); + } + preLinkFns.push(pre); + } + if (post) { + if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); + post.require = directive.require; + post.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + post = cloneAndAnnotateFn(post, {isolateScope: true}); + } + postLinkFns.push(post); + } + } + + function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { + var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element, + attrs, scopeBindingInfo; + + if (compileNode === linkNode) { + attrs = templateAttrs; + $element = templateAttrs.$$element; + } else { + $element = jqLite(linkNode); + attrs = new Attributes($element, templateAttrs); + } + + controllerScope = scope; + if (newIsolateScopeDirective) { + isolateScope = scope.$new(true); + } else if (newScopeDirective) { + controllerScope = scope.$parent; + } + + if (boundTranscludeFn) { + // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn` + // is later passed as `parentBoundTranscludeFn` to `publicLinkFn` + transcludeFn = controllersBoundTransclude; + transcludeFn.$$boundTransclude = boundTranscludeFn; + // expose the slots on the `$transclude` function + transcludeFn.isSlotFilled = function(slotName) { + return !!boundTranscludeFn.$$slots[slotName]; + }; + } + + if (controllerDirectives) { + elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective); + } + + if (newIsolateScopeDirective) { + // Initialize isolate scope bindings for new isolate scope directive. + compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || + templateDirective === newIsolateScopeDirective.$$originalDirective))); + compile.$$addScopeClass($element, true); + isolateScope.$$isolateBindings = + newIsolateScopeDirective.$$isolateBindings; + scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope, + isolateScope.$$isolateBindings, + newIsolateScopeDirective); + if (scopeBindingInfo.removeWatches) { + isolateScope.$on('$destroy', scopeBindingInfo.removeWatches); + } + } + + // Initialize bindToController bindings + for (var name in elementControllers) { + var controllerDirective = controllerDirectives[name]; + var controller = elementControllers[name]; + var bindings = controllerDirective.$$bindings.bindToController; + + if (preAssignBindingsEnabled) { + if (bindings) { + controller.bindingInfo = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); + } else { + controller.bindingInfo = {}; + } + + var controllerResult = controller(); + if (controllerResult !== controller.instance) { + // If the controller constructor has a return value, overwrite the instance + // from setupControllers + controller.instance = controllerResult; + $element.data('$' + controllerDirective.name + 'Controller', controllerResult); + if (controller.bindingInfo.removeWatches) { + controller.bindingInfo.removeWatches(); + } + controller.bindingInfo = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); + } + } else { + controller.instance = controller(); + $element.data('$' + controllerDirective.name + 'Controller', controller.instance); + controller.bindingInfo = + initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective); + } + } + + // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy + forEach(controllerDirectives, function(controllerDirective, name) { + var require = controllerDirective.require; + if (controllerDirective.bindToController && !isArray(require) && isObject(require)) { + extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers)); + } + }); + + // Handle the init and destroy lifecycle hooks on all controllers that have them + forEach(elementControllers, function(controller) { + var controllerInstance = controller.instance; + if (isFunction(controllerInstance.$onChanges)) { + try { + controllerInstance.$onChanges(controller.bindingInfo.initialChanges); + } catch (e) { + $exceptionHandler(e); + } + } + if (isFunction(controllerInstance.$onInit)) { + try { + controllerInstance.$onInit(); + } catch (e) { + $exceptionHandler(e); + } + } + if (isFunction(controllerInstance.$doCheck)) { + controllerScope.$watch(function() { controllerInstance.$doCheck(); }); + controllerInstance.$doCheck(); + } + if (isFunction(controllerInstance.$onDestroy)) { + controllerScope.$on('$destroy', function callOnDestroyHook() { + controllerInstance.$onDestroy(); + }); + } + }); + + // PRELINKING + for (i = 0, ii = preLinkFns.length; i < ii; i++) { + linkFn = preLinkFns[i]; + invokeLinkFn(linkFn, + linkFn.isolateScope ? isolateScope : scope, + $element, + attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), + transcludeFn + ); + } + + // RECURSION + // We only pass the isolate scope, if the isolate directive has a template, + // otherwise the child elements do not belong to the isolate directive. + var scopeToChild = scope; + if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { + scopeToChild = isolateScope; + } + if (childLinkFn) { + childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); + } + + // POSTLINKING + for (i = postLinkFns.length - 1; i >= 0; i--) { + linkFn = postLinkFns[i]; + invokeLinkFn(linkFn, + linkFn.isolateScope ? isolateScope : scope, + $element, + attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), + transcludeFn + ); + } + + // Trigger $postLink lifecycle hooks + forEach(elementControllers, function(controller) { + var controllerInstance = controller.instance; + if (isFunction(controllerInstance.$postLink)) { + controllerInstance.$postLink(); + } + }); + + // This is the function that is injected as `$transclude`. + // Note: all arguments are optional! + function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) { + var transcludeControllers; + // No scope passed in: + if (!isScope(scope)) { + slotName = futureParentElement; + futureParentElement = cloneAttachFn; + cloneAttachFn = scope; + scope = undefined; + } + + if (hasElementTranscludeDirective) { + transcludeControllers = elementControllers; + } + if (!futureParentElement) { + futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element; + } + if (slotName) { + // slotTranscludeFn can be one of three things: + // * a transclude function - a filled slot + // * `null` - an optional slot that was not filled + // * `undefined` - a slot that was not declared (i.e. invalid) + var slotTranscludeFn = boundTranscludeFn.$$slots[slotName]; + if (slotTranscludeFn) { + return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); + } else if (isUndefined(slotTranscludeFn)) { + throw $compileMinErr('noslot', + 'No parent directive that requires a transclusion with slot name "{0}". ' + + 'Element: {1}', + slotName, startingTag($element)); + } + } else { + return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); + } + } + } + } + + function getControllers(directiveName, require, $element, elementControllers) { + var value; + + if (isString(require)) { + var match = require.match(REQUIRE_PREFIX_REGEXP); + var name = require.substring(match[0].length); + var inheritType = match[1] || match[3]; + var optional = match[2] === '?'; + + //If only parents then start at the parent element + if (inheritType === '^^') { + $element = $element.parent(); + //Otherwise attempt getting the controller from elementControllers in case + //the element is transcluded (and has no data) and to avoid .data if possible + } else { + value = elementControllers && elementControllers[name]; + value = value && value.instance; + } + + if (!value) { + var dataName = '$' + name + 'Controller'; + value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName); + } + + if (!value && !optional) { + throw $compileMinErr('ctreq', + 'Controller \'{0}\', required by directive \'{1}\', can\'t be found!', + name, directiveName); + } + } else if (isArray(require)) { + value = []; + for (var i = 0, ii = require.length; i < ii; i++) { + value[i] = getControllers(directiveName, require[i], $element, elementControllers); + } + } else if (isObject(require)) { + value = {}; + forEach(require, function(controller, property) { + value[property] = getControllers(directiveName, controller, $element, elementControllers); + }); + } + + return value || null; + } + + function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) { + var elementControllers = createMap(); + for (var controllerKey in controllerDirectives) { + var directive = controllerDirectives[controllerKey]; + var locals = { + $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, + $element: $element, + $attrs: attrs, + $transclude: transcludeFn + }; + + var controller = directive.controller; + if (controller === '@') { + controller = attrs[directive.name]; + } + + var controllerInstance = $controller(controller, locals, true, directive.controllerAs); + + // For directives with element transclusion the element is a comment. + // In this case .data will not attach any data. + // Instead, we save the controllers for the element in a local hash and attach to .data + // later, once we have the actual element. + elementControllers[directive.name] = controllerInstance; + $element.data('$' + directive.name + 'Controller', controllerInstance.instance); + } + return elementControllers; + } + + // Depending upon the context in which a directive finds itself it might need to have a new isolated + // or child scope created. For instance: + // * if the directive has been pulled into a template because another directive with a higher priority + // asked for element transclusion + // * if the directive itself asks for transclusion but it is at the root of a template and the original + // element was replaced. See https://github.com/angular/angular.js/issues/12936 + function markDirectiveScope(directives, isolateScope, newScope) { + for (var j = 0, jj = directives.length; j < jj; j++) { + directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope}); + } + } + + /** + * looks up the directive and decorates it with exception handling and proper parameters. We + * call this the boundDirective. + * + * @param {string} name name of the directive to look up. + * @param {string} location The directive must be found in specific format. + * String containing any of theses characters: + * + * * `E`: element name + * * `A': attribute + * * `C`: class + * * `M`: comment + * @returns {boolean} true if directive was added. + */ + function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, + endAttrName) { + if (name === ignoreDirective) return null; + var match = null; + if (hasDirectives.hasOwnProperty(name)) { + for (var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + if ((isUndefined(maxPriority) || maxPriority > directive.priority) && + directive.restrict.indexOf(location) !== -1) { + if (startAttrName) { + directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); + } + if (!directive.$$bindings) { + var bindings = directive.$$bindings = + parseDirectiveBindings(directive, directive.name); + if (isObject(bindings.isolateScope)) { + directive.$$isolateBindings = bindings.isolateScope; + } + } + tDirectives.push(directive); + match = directive; + } + } + } + return match; + } + + + /** + * looks up the directive and returns true if it is a multi-element directive, + * and therefore requires DOM nodes between -start and -end markers to be grouped + * together. + * + * @param {string} name name of the directive to look up. + * @returns true if directive was registered as multi-element. + */ + function directiveIsMultiElement(name) { + if (hasDirectives.hasOwnProperty(name)) { + for (var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + if (directive.multiElement) { + return true; + } + } + } + return false; + } + + /** + * When the element is replaced with HTML template then the new attributes + * on the template need to be merged with the existing attributes in the DOM. + * The desired effect is to have both of the attributes present. + * + * @param {object} dst destination attributes (original DOM) + * @param {object} src source attributes (from the directive template) + */ + function mergeTemplateAttributes(dst, src) { + var srcAttr = src.$attr, + dstAttr = dst.$attr; + + // reapply the old attributes to the new element + forEach(dst, function(value, key) { + if (key.charAt(0) !== '$') { + if (src[key] && src[key] !== value) { + if (value.length) { + value += (key === 'style' ? ';' : ' ') + src[key]; + } else { + value = src[key]; + } + } + dst.$set(key, value, true, srcAttr[key]); + } + }); + + // copy the new attributes on the old attrs object + forEach(src, function(value, key) { + // Check if we already set this attribute in the loop above. + // `dst` will never contain hasOwnProperty as DOM parser won't let it. + // You will get an "InvalidCharacterError: DOM Exception 5" error if you + // have an attribute like "has-own-property" or "data-has-own-property", etc. + if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') { + dst[key] = value; + + if (key !== 'class' && key !== 'style') { + dstAttr[key] = srcAttr[key]; + } + } + }); + } + + + function compileTemplateUrl(directives, $compileNode, tAttrs, + $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { + var linkQueue = [], + afterTemplateNodeLinkFn, + afterTemplateChildLinkFn, + beforeTemplateCompileNode = $compileNode[0], + origAsyncDirective = directives.shift(), + derivedSyncDirective = inherit(origAsyncDirective, { + templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective + }), + templateUrl = (isFunction(origAsyncDirective.templateUrl)) + ? origAsyncDirective.templateUrl($compileNode, tAttrs) + : origAsyncDirective.templateUrl, + templateNamespace = origAsyncDirective.templateNamespace; + + $compileNode.empty(); + + $templateRequest(templateUrl) + .then(function(content) { + var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; + + content = denormalizeTemplate(content); + + if (origAsyncDirective.replace) { + if (jqLiteIsTextNode(content)) { + $template = []; + } else { + $template = removeComments(wrapTemplate(templateNamespace, trim(content))); + } + compileNode = $template[0]; + + if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { + throw $compileMinErr('tplrt', + 'Template for directive \'{0}\' must have exactly one root element. {1}', + origAsyncDirective.name, templateUrl); + } + + tempTemplateAttrs = {$attr: {}}; + replaceWith($rootElement, $compileNode, compileNode); + var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); + + if (isObject(origAsyncDirective.scope)) { + // the original directive that caused the template to be loaded async required + // an isolate scope + markDirectiveScope(templateDirectives, true); + } + directives = templateDirectives.concat(directives); + mergeTemplateAttributes(tAttrs, tempTemplateAttrs); + } else { + compileNode = beforeTemplateCompileNode; + $compileNode.html(content); + } + + directives.unshift(derivedSyncDirective); + + afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, + childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, + previousCompileContext); + forEach($rootElement, function(node, i) { + if (node === compileNode) { + $rootElement[i] = $compileNode[0]; + } + }); + afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); + + while (linkQueue.length) { + var scope = linkQueue.shift(), + beforeTemplateLinkNode = linkQueue.shift(), + linkRootElement = linkQueue.shift(), + boundTranscludeFn = linkQueue.shift(), + linkNode = $compileNode[0]; + + if (scope.$$destroyed) continue; + + if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { + var oldClasses = beforeTemplateLinkNode.className; + + if (!(previousCompileContext.hasElementTranscludeDirective && + origAsyncDirective.replace)) { + // it was cloned therefore we have to clone as well. + linkNode = jqLiteClone(compileNode); + } + replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); + + // Copy in CSS classes from original node + safeAddClass(jqLite(linkNode), oldClasses); + } + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } else { + childBoundTranscludeFn = boundTranscludeFn; + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, + childBoundTranscludeFn); + } + linkQueue = null; + }).catch(function(error) { + if (isError(error)) { + $exceptionHandler(error); + } + }); + + return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { + var childBoundTranscludeFn = boundTranscludeFn; + if (scope.$$destroyed) return; + if (linkQueue) { + linkQueue.push(scope, + node, + rootElement, + childBoundTranscludeFn); + } else { + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); + } + }; + } + + + /** + * Sorting function for bound directives. + */ + function byPriority(a, b) { + var diff = b.priority - a.priority; + if (diff !== 0) return diff; + if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; + return a.index - b.index; + } + + function assertNoDuplicate(what, previousDirective, directive, element) { + + function wrapModuleNameIfDefined(moduleName) { + return moduleName ? + (' (module: ' + moduleName + ')') : + ''; + } + + if (previousDirective) { + throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}', + previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName), + directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element)); + } + } + + + function addTextInterpolateDirective(directives, text) { + var interpolateFn = $interpolate(text, true); + if (interpolateFn) { + directives.push({ + priority: 0, + compile: function textInterpolateCompileFn(templateNode) { + var templateNodeParent = templateNode.parent(), + hasCompileParent = !!templateNodeParent.length; + + // When transcluding a template that has bindings in the root + // we don't have a parent and thus need to add the class during linking fn. + if (hasCompileParent) compile.$$addBindingClass(templateNodeParent); + + return function textInterpolateLinkFn(scope, node) { + var parent = node.parent(); + if (!hasCompileParent) compile.$$addBindingClass(parent); + compile.$$addBindingInfo(parent, interpolateFn.expressions); + scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { + node[0].nodeValue = value; + }); + }; + } + }); + } + } + + + function wrapTemplate(type, template) { + type = lowercase(type || 'html'); + switch (type) { + case 'svg': + case 'math': + var wrapper = window.document.createElement('div'); + wrapper.innerHTML = '<' + type + '>' + template + ''; + return wrapper.childNodes[0].childNodes; + default: + return template; + } + } + + + function getTrustedContext(node, attrNormalizedName) { + if (attrNormalizedName === 'srcdoc') { + return $sce.HTML; + } + var tag = nodeName_(node); + // All tags with src attributes require a RESOURCE_URL value, except for + // img and various html5 media tags. + if (attrNormalizedName === 'src' || attrNormalizedName === 'ngSrc') { + if (['img', 'video', 'audio', 'source', 'track'].indexOf(tag) === -1) { + return $sce.RESOURCE_URL; + } + // maction[xlink:href] can source SVG. It's not limited to . + } else if (attrNormalizedName === 'xlinkHref' || + (tag === 'form' && attrNormalizedName === 'action') || + // links can be stylesheets or imports, which can run script in the current origin + (tag === 'link' && attrNormalizedName === 'href') + ) { + return $sce.RESOURCE_URL; + } + } + + + function addAttrInterpolateDirective(node, directives, value, name, isNgAttr) { + var trustedContext = getTrustedContext(node, name); + var mustHaveExpression = !isNgAttr; + var allOrNothing = ALL_OR_NOTHING_ATTRS[name] || isNgAttr; + + var interpolateFn = $interpolate(value, mustHaveExpression, trustedContext, allOrNothing); + + // no interpolation found -> ignore + if (!interpolateFn) return; + + if (name === 'multiple' && nodeName_(node) === 'select') { + throw $compileMinErr('selmulti', + 'Binding to the \'multiple\' attribute is not supported. Element: {0}', + startingTag(node)); + } + + if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { + throw $compileMinErr('nodomevents', + 'Interpolations for HTML DOM event attributes are disallowed. Please use the ' + + 'ng- versions (such as ng-click instead of onclick) instead.'); + } + + directives.push({ + priority: 100, + compile: function() { + return { + pre: function attrInterpolatePreLinkFn(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = createMap())); + + // If the attribute has changed since last $interpolate()ed + var newValue = attr[name]; + if (newValue !== value) { + // we need to interpolate again since the attribute value has been updated + // (e.g. by another directive's compile function) + // ensure unset/empty values make interpolateFn falsy + interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing); + value = newValue; + } + + // if attribute was updated so that there is no interpolation going on we don't want to + // register any observers + if (!interpolateFn) return; + + // initialize attr object so that it's ready in case we need the value for isolate + // scope initialization, otherwise the value would not be available from isolate + // directive's linking fn during linking phase + attr[name] = interpolateFn(scope); + + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { + //special case for class attribute addition + removal + //so that class changes can tap into the animation + //hooks provided by the $animate service. Be sure to + //skip animations when the first digest occurs (when + //both the new and the old values are the same) since + //the CSS classes are the non-interpolated values + if (name === 'class' && newValue !== oldValue) { + attr.$updateClass(newValue, oldValue); + } else { + attr.$set(name, newValue); + } + }); + } + }; + } + }); + } + + + /** + * This is a special jqLite.replaceWith, which can replace items which + * have no parents, provided that the containing jqLite collection is provided. + * + * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes + * in the root of the tree. + * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep + * the shell, but replace its DOM node reference. + * @param {Node} newNode The new DOM node. + */ + function replaceWith($rootElement, elementsToRemove, newNode) { + var firstElementToRemove = elementsToRemove[0], + removeCount = elementsToRemove.length, + parent = firstElementToRemove.parentNode, + i, ii; + + if ($rootElement) { + for (i = 0, ii = $rootElement.length; i < ii; i++) { + if ($rootElement[i] === firstElementToRemove) { + $rootElement[i++] = newNode; + for (var j = i, j2 = j + removeCount - 1, + jj = $rootElement.length; + j < jj; j++, j2++) { + if (j2 < jj) { + $rootElement[j] = $rootElement[j2]; + } else { + delete $rootElement[j]; + } + } + $rootElement.length -= removeCount - 1; + + // If the replaced element is also the jQuery .context then replace it + // .context is a deprecated jQuery api, so we should set it only when jQuery set it + // http://api.jquery.com/context/ + if ($rootElement.context === firstElementToRemove) { + $rootElement.context = newNode; + } + break; + } + } + } + + if (parent) { + parent.replaceChild(newNode, firstElementToRemove); + } + + // Append all the `elementsToRemove` to a fragment. This will... + // - remove them from the DOM + // - allow them to still be traversed with .nextSibling + // - allow a single fragment.qSA to fetch all elements being removed + var fragment = window.document.createDocumentFragment(); + for (i = 0; i < removeCount; i++) { + fragment.appendChild(elementsToRemove[i]); + } + + if (jqLite.hasData(firstElementToRemove)) { + // Copy over user data (that includes AngularJS's $scope etc.). Don't copy private + // data here because there's no public interface in jQuery to do that and copying over + // event listeners (which is the main use of private data) wouldn't work anyway. + jqLite.data(newNode, jqLite.data(firstElementToRemove)); + + // Remove $destroy event listeners from `firstElementToRemove` + jqLite(firstElementToRemove).off('$destroy'); + } + + // Cleanup any data/listeners on the elements and children. + // This includes invoking the $destroy event on any elements with listeners. + jqLite.cleanData(fragment.querySelectorAll('*')); + + // Update the jqLite collection to only contain the `newNode` + for (i = 1; i < removeCount; i++) { + delete elementsToRemove[i]; + } + elementsToRemove[0] = newNode; + elementsToRemove.length = 1; + } + + + function cloneAndAnnotateFn(fn, annotation) { + return extend(function() { return fn.apply(null, arguments); }, fn, annotation); + } + + + function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { + try { + linkFn(scope, $element, attrs, controllers, transcludeFn); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + + function strictBindingsCheck(attrName, directiveName) { + if (strictComponentBindingsEnabled) { + throw $compileMinErr('missingattr', + 'Attribute \'{0}\' of \'{1}\' is non-optional and must be set!', + attrName, directiveName); + } + } + + // Set up $watches for isolate scope and controller bindings. + function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) { + var removeWatchCollection = []; + var initialChanges = {}; + var changes; + + forEach(bindings, function initializeBinding(definition, scopeName) { + var attrName = definition.attrName, + optional = definition.optional, + mode = definition.mode, // @, =, <, or & + lastValue, + parentGet, parentSet, compare, removeWatch; + + switch (mode) { + + case '@': + if (!optional && !hasOwnProperty.call(attrs, attrName)) { + strictBindingsCheck(attrName, directive.name); + destination[scopeName] = attrs[attrName] = undefined; + + } + removeWatch = attrs.$observe(attrName, function(value) { + if (isString(value) || isBoolean(value)) { + var oldValue = destination[scopeName]; + recordChanges(scopeName, value, oldValue); + destination[scopeName] = value; + } + }); + attrs.$$observers[attrName].$$scope = scope; + lastValue = attrs[attrName]; + if (isString(lastValue)) { + // If the attribute has been provided then we trigger an interpolation to ensure + // the value is there for use in the link fn + destination[scopeName] = $interpolate(lastValue)(scope); + } else if (isBoolean(lastValue)) { + // If the attributes is one of the BOOLEAN_ATTR then AngularJS will have converted + // the value to boolean rather than a string, so we special case this situation + destination[scopeName] = lastValue; + } + initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]); + removeWatchCollection.push(removeWatch); + break; + + case '=': + if (!hasOwnProperty.call(attrs, attrName)) { + if (optional) break; + strictBindingsCheck(attrName, directive.name); + attrs[attrName] = undefined; + } + if (optional && !attrs[attrName]) break; + + parentGet = $parse(attrs[attrName]); + if (parentGet.literal) { + compare = equals; + } else { + compare = simpleCompare; + } + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = destination[scopeName] = parentGet(scope); + throw $compileMinErr('nonassign', + 'Expression \'{0}\' in attribute \'{1}\' used with directive \'{2}\' is non-assignable!', + attrs[attrName], attrName, directive.name); + }; + lastValue = destination[scopeName] = parentGet(scope); + var parentValueWatch = function parentValueWatch(parentValue) { + if (!compare(parentValue, destination[scopeName])) { + // we are out of sync and need to copy + if (!compare(parentValue, lastValue)) { + // parent changed and it has precedence + destination[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(scope, parentValue = destination[scopeName]); + } + } + lastValue = parentValue; + return lastValue; + }; + parentValueWatch.$stateful = true; + if (definition.collection) { + removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch); + } else { + removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); + } + removeWatchCollection.push(removeWatch); + break; + + case '<': + if (!hasOwnProperty.call(attrs, attrName)) { + if (optional) break; + strictBindingsCheck(attrName, directive.name); + attrs[attrName] = undefined; + } + if (optional && !attrs[attrName]) break; + + parentGet = $parse(attrs[attrName]); + var deepWatch = parentGet.literal; + + var initialValue = destination[scopeName] = parentGet(scope); + initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]); + + removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) { + if (oldValue === newValue) { + if (oldValue === initialValue || (deepWatch && equals(oldValue, initialValue))) { + return; + } + oldValue = initialValue; + } + recordChanges(scopeName, newValue, oldValue); + destination[scopeName] = newValue; + }, deepWatch); + + removeWatchCollection.push(removeWatch); + break; + + case '&': + if (!optional && !hasOwnProperty.call(attrs, attrName)) { + strictBindingsCheck(attrName, directive.name); + } + // Don't assign Object.prototype method to scope + parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop; + + // Don't assign noop to destination if expression is not valid + if (parentGet === noop && optional) break; + + destination[scopeName] = function(locals) { + return parentGet(scope, locals); + }; + break; + } + }); + + function recordChanges(key, currentValue, previousValue) { + if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) { + // If we have not already scheduled the top level onChangesQueue handler then do so now + if (!onChangesQueue) { + scope.$$postDigest(flushOnChangesQueue); + onChangesQueue = []; + } + // If we have not already queued a trigger of onChanges for this controller then do so now + if (!changes) { + changes = {}; + onChangesQueue.push(triggerOnChangesHook); + } + // If the has been a change on this property already then we need to reuse the previous value + if (changes[key]) { + previousValue = changes[key].previousValue; + } + // Store this change + changes[key] = new SimpleChange(previousValue, currentValue); + } + } + + function triggerOnChangesHook() { + destination.$onChanges(changes); + // Now clear the changes so that we schedule onChanges when more changes arrive + changes = undefined; + } + + return { + initialChanges: initialChanges, + removeWatches: removeWatchCollection.length && function removeWatches() { + for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) { + removeWatchCollection[i](); + } + } + }; + } + }]; +} + +function SimpleChange(previous, current) { + this.previousValue = previous; + this.currentValue = current; +} +SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; }; + + +var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i; +var SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g; + +/** + * Converts all accepted directives format into proper directive name. + * @param name Name to normalize + */ +function directiveNormalize(name) { + return name + .replace(PREFIX_REGEXP, '') + .replace(SPECIAL_CHARS_REGEXP, function(_, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); +} + +/** + * @ngdoc type + * @name $compile.directive.Attributes + * + * @description + * A shared object between directive compile / linking functions which contains normalized DOM + * element attributes. The values reflect current binding state `{{ }}`. The normalization is + * needed since all of these are treated as equivalent in AngularJS: + * + * ``` + * + * ``` + */ + +/** + * @ngdoc property + * @name $compile.directive.Attributes#$attr + * + * @description + * A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc method + * @name $compile.directive.Attributes#$set + * @kind function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. The value can be an interpolated string. + */ + + + +/** + * Closure compiler type information + */ + +function nodesetLinkingFn( + /* angular.Scope */ scope, + /* NodeList */ nodeList, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +) {} + +function directiveLinkingFn( + /* nodesetLinkingFn */ nodesetLinkingFn, + /* angular.Scope */ scope, + /* Node */ node, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +) {} + +function tokenDifference(str1, str2) { + var values = '', + tokens1 = str1.split(/\s+/), + tokens2 = str2.split(/\s+/); + + outer: + for (var i = 0; i < tokens1.length; i++) { + var token = tokens1[i]; + for (var j = 0; j < tokens2.length; j++) { + if (token === tokens2[j]) continue outer; + } + values += (values.length > 0 ? ' ' : '') + token; + } + return values; +} + +function removeComments(jqNodes) { + jqNodes = jqLite(jqNodes); + var i = jqNodes.length; + + if (i <= 1) { + return jqNodes; + } + + while (i--) { + var node = jqNodes[i]; + if (node.nodeType === NODE_TYPE_COMMENT || + (node.nodeType === NODE_TYPE_TEXT && node.nodeValue.trim() === '')) { + splice.call(jqNodes, i, 1); + } + } + return jqNodes; +} + +var $controllerMinErr = minErr('$controller'); + + +var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/; +function identifierForController(controller, ident) { + if (ident && isString(ident)) return ident; + if (isString(controller)) { + var match = CNTRL_REG.exec(controller); + if (match) return match[3]; + } +} + + +/** + * @ngdoc provider + * @name $controllerProvider + * @this + * + * @description + * The {@link ng.$controller $controller service} is used by AngularJS to create new + * controllers. + * + * This provider allows controller registration via the + * {@link ng.$controllerProvider#register register} method. + */ +function $ControllerProvider() { + var controllers = {}, + globals = false; + + /** + * @ngdoc method + * @name $controllerProvider#has + * @param {string} name Controller name to check. + */ + this.has = function(name) { + return controllers.hasOwnProperty(name); + }; + + /** + * @ngdoc method + * @name $controllerProvider#register + * @param {string|Object} name Controller name, or an object map of controllers where the keys are + * the names and the values are the constructors. + * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI + * annotations in the array notation). + */ + this.register = function(name, constructor) { + assertNotHasOwnProperty(name, 'controller'); + if (isObject(name)) { + extend(controllers, name); + } else { + controllers[name] = constructor; + } + }; + + /** + * @ngdoc method + * @name $controllerProvider#allowGlobals + * @description If called, allows `$controller` to find controller constructors on `window` + * + * @deprecated + * sinceVersion="v1.3.0" + * removeVersion="v1.7.0" + * This method of finding controllers has been deprecated. + */ + this.allowGlobals = function() { + globals = true; + }; + + + this.$get = ['$injector', '$window', function($injector, $window) { + + /** + * @ngdoc service + * @name $controller + * @requires $injector + * + * @param {Function|string} constructor If called with a function then it's considered to be the + * controller constructor function. Otherwise it's considered to be a string which is used + * to retrieve the controller constructor using the following steps: + * + * * check if a controller with given name is registered via `$controllerProvider` + * * check if evaluating the string on the current scope returns a constructor + * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global + * `window` object (deprecated, not recommended) + * + * The string can use the `controller as property` syntax, where the controller instance is published + * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this + * to work correctly. + * + * @param {Object} locals Injection locals for Controller. + * @return {Object} Instance of given controller. + * + * @description + * `$controller` service is responsible for instantiating controllers. + * + * It's just a simple call to {@link auto.$injector $injector}, but extracted into + * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). + */ + return function $controller(expression, locals, later, ident) { + // PRIVATE API: + // param `later` --- indicates that the controller's constructor is invoked at a later time. + // If true, $controller will allocate the object with the correct + // prototype chain, but will not invoke the controller until a returned + // callback is invoked. + // param `ident` --- An optional label which overrides the label parsed from the controller + // expression, if any. + var instance, match, constructor, identifier; + later = later === true; + if (ident && isString(ident)) { + identifier = ident; + } + + if (isString(expression)) { + match = expression.match(CNTRL_REG); + if (!match) { + throw $controllerMinErr('ctrlfmt', + 'Badly formed controller string \'{0}\'. ' + + 'Must match `__name__ as __id__` or `__name__`.', expression); + } + constructor = match[1]; + identifier = identifier || match[3]; + expression = controllers.hasOwnProperty(constructor) + ? controllers[constructor] + : getter(locals.$scope, constructor, true) || + (globals ? getter($window, constructor, true) : undefined); + + if (!expression) { + throw $controllerMinErr('ctrlreg', + 'The controller with the name \'{0}\' is not registered.', constructor); + } + + assertArgFn(expression, constructor, true); + } + + if (later) { + // Instantiate controller later: + // This machinery is used to create an instance of the object before calling the + // controller's constructor itself. + // + // This allows properties to be added to the controller before the constructor is + // invoked. Primarily, this is used for isolate scope bindings in $compile. + // + // This feature is not intended for use by applications, and is thus not documented + // publicly. + // Object creation: http://jsperf.com/create-constructor/2 + var controllerPrototype = (isArray(expression) ? + expression[expression.length - 1] : expression).prototype; + instance = Object.create(controllerPrototype || null); + + if (identifier) { + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + + return extend(function $controllerInit() { + var result = $injector.invoke(expression, instance, locals, constructor); + if (result !== instance && (isObject(result) || isFunction(result))) { + instance = result; + if (identifier) { + // If result changed, re-assign controllerAs value to scope. + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + } + return instance; + }, { + instance: instance, + identifier: identifier + }); + } + + instance = $injector.instantiate(expression, locals, constructor); + + if (identifier) { + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + + return instance; + }; + + function addIdentifier(locals, identifier, instance, name) { + if (!(locals && isObject(locals.$scope))) { + throw minErr('$controller')('noscp', + 'Cannot export controller \'{0}\' as \'{1}\'! No $scope object provided via `locals`.', + name, identifier); + } + + locals.$scope[identifier] = instance; + } + }]; +} + +/** + * @ngdoc service + * @name $document + * @requires $window + * @this + * + * @description + * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. + * + * @example + + +
    +

    $document title:

    +

    window.document title:

    +
    +
    + + angular.module('documentExample', []) + .controller('ExampleController', ['$scope', '$document', function($scope, $document) { + $scope.title = $document[0].title; + $scope.windowTitle = angular.element(window.document)[0].title; + }]); + +
    + */ +function $DocumentProvider() { + this.$get = ['$window', function(window) { + return jqLite(window.document); + }]; +} + + +/** + * @private + * @this + * Listens for document visibility change and makes the current status accessible. + */ +function $$IsDocumentHiddenProvider() { + this.$get = ['$document', '$rootScope', function($document, $rootScope) { + var doc = $document[0]; + var hidden = doc && doc.hidden; + + $document.on('visibilitychange', changeListener); + + $rootScope.$on('$destroy', function() { + $document.off('visibilitychange', changeListener); + }); + + function changeListener() { + hidden = doc.hidden; + } + + return function() { + return hidden; + }; + }]; +} + +/** + * @ngdoc service + * @name $exceptionHandler + * @requires ng.$log + * @this + * + * @description + * Any uncaught exception in AngularJS expressions is delegated to this service. + * The default implementation simply delegates to `$log.error` which logs it into + * the browser console. + * + * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by + * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. + * + * ## Example: + * + * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught + * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead + * of `$log.error()`. + * + * ```js + * angular. + * module('exceptionOverwrite', []). + * factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) { + * return function myExceptionHandler(exception, cause) { + * logErrorsToBackend(exception, cause); + * $log.warn(exception, cause); + * }; + * }]); + * ``` + * + *
    + * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind` + * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler} + * (unless executed during a digest). + * + * If you wish, you can manually delegate exceptions, e.g. + * `try { ... } catch(e) { $exceptionHandler(e); }` + * + * @param {Error} exception Exception associated with the error. + * @param {string=} cause Optional information about the context in which + * the error was thrown. + * + */ +function $ExceptionHandlerProvider() { + this.$get = ['$log', function($log) { + return function(exception, cause) { + $log.error.apply($log, arguments); + }; + }]; +} + +var $$ForceReflowProvider = /** @this */ function() { + this.$get = ['$document', function($document) { + return function(domNode) { + //the line below will force the browser to perform a repaint so + //that all the animated elements within the animation frame will + //be properly updated and drawn on screen. This is required to + //ensure that the preparation animation is properly flushed so that + //the active state picks up from there. DO NOT REMOVE THIS LINE. + //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH + //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND + //WILL TAKE YEARS AWAY FROM YOUR LIFE. + if (domNode) { + if (!domNode.nodeType && domNode instanceof jqLite) { + domNode = domNode[0]; + } + } else { + domNode = $document[0].body; + } + return domNode.offsetWidth + 1; + }; + }]; +}; + +var APPLICATION_JSON = 'application/json'; +var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; +var JSON_START = /^\[|^\{(?!\{)/; +var JSON_ENDS = { + '[': /]$/, + '{': /}$/ +}; +var JSON_PROTECTION_PREFIX = /^\)]\}',?\n/; +var $httpMinErr = minErr('$http'); + +function serializeValue(v) { + if (isObject(v)) { + return isDate(v) ? v.toISOString() : toJson(v); + } + return v; +} + + +/** @this */ +function $HttpParamSerializerProvider() { + /** + * @ngdoc service + * @name $httpParamSerializer + * @description + * + * Default {@link $http `$http`} params serializer that converts objects to strings + * according to the following rules: + * + * * `{'foo': 'bar'}` results in `foo=bar` + * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object) + * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element) + * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object) + * + * Note that serializer will sort the request parameters alphabetically. + * */ + + this.$get = function() { + return function ngParamSerializer(params) { + if (!params) return ''; + var parts = []; + forEachSorted(params, function(value, key) { + if (value === null || isUndefined(value) || isFunction(value)) return; + if (isArray(value)) { + forEach(value, function(v) { + parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v))); + }); + } else { + parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value))); + } + }); + + return parts.join('&'); + }; + }; +} + +/** @this */ +function $HttpParamSerializerJQLikeProvider() { + /** + * @ngdoc service + * @name $httpParamSerializerJQLike + * + * @description + * + * Alternative {@link $http `$http`} params serializer that follows + * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic. + * The serializer will also sort the params alphabetically. + * + * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property: + * + * ```js + * $http({ + * url: myUrl, + * method: 'GET', + * params: myParams, + * paramSerializer: '$httpParamSerializerJQLike' + * }); + * ``` + * + * It is also possible to set it as the default `paramSerializer` in the + * {@link $httpProvider#defaults `$httpProvider`}. + * + * Additionally, you can inject the serializer and use it explicitly, for example to serialize + * form data for submission: + * + * ```js + * .controller(function($http, $httpParamSerializerJQLike) { + * //... + * + * $http({ + * url: myUrl, + * method: 'POST', + * data: $httpParamSerializerJQLike(myData), + * headers: { + * 'Content-Type': 'application/x-www-form-urlencoded' + * } + * }); + * + * }); + * ``` + * + * */ + this.$get = function() { + return function jQueryLikeParamSerializer(params) { + if (!params) return ''; + var parts = []; + serialize(params, '', true); + return parts.join('&'); + + function serialize(toSerialize, prefix, topLevel) { + if (toSerialize === null || isUndefined(toSerialize)) return; + if (isArray(toSerialize)) { + forEach(toSerialize, function(value, index) { + serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']'); + }); + } else if (isObject(toSerialize) && !isDate(toSerialize)) { + forEachSorted(toSerialize, function(value, key) { + serialize(value, prefix + + (topLevel ? '' : '[') + + key + + (topLevel ? '' : ']')); + }); + } else { + parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize))); + } + } + }; + }; +} + +function defaultHttpResponseTransform(data, headers) { + if (isString(data)) { + // Strip json vulnerability protection prefix and trim whitespace + var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim(); + + if (tempData) { + var contentType = headers('Content-Type'); + var hasJsonContentType = contentType && (contentType.indexOf(APPLICATION_JSON) === 0); + + if (hasJsonContentType || isJsonLike(tempData)) { + try { + data = fromJson(tempData); + } catch (e) { + if (!hasJsonContentType) { + return data; + } + throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' + + 'Parse error: "{1}"', data, e); + } + } + } + } + + return data; +} + +function isJsonLike(str) { + var jsonStart = str.match(JSON_START); + return jsonStart && JSON_ENDS[jsonStart[0]].test(str); +} + +/** + * Parse headers into key value object + * + * @param {string} headers Raw headers as a string + * @returns {Object} Parsed headers as key value object + */ +function parseHeaders(headers) { + var parsed = createMap(), i; + + function fillInParsed(key, val) { + if (key) { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + } + + if (isString(headers)) { + forEach(headers.split('\n'), function(line) { + i = line.indexOf(':'); + fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1))); + }); + } else if (isObject(headers)) { + forEach(headers, function(headerVal, headerKey) { + fillInParsed(lowercase(headerKey), trim(headerVal)); + }); + } + + return parsed; +} + + +/** + * Returns a function that provides access to parsed headers. + * + * Headers are lazy parsed when first requested. + * @see parseHeaders + * + * @param {(string|Object)} headers Headers to provide access to. + * @returns {function(string=)} Returns a getter function which if called with: + * + * - if called with an argument returns a single header value or null + * - if called with no arguments returns an object containing all headers. + */ +function headersGetter(headers) { + var headersObj; + + return function(name) { + if (!headersObj) headersObj = parseHeaders(headers); + + if (name) { + var value = headersObj[lowercase(name)]; + if (value === undefined) { + value = null; + } + return value; + } + + return headersObj; + }; +} + + +/** + * Chain all given functions + * + * This function is used for both request and response transforming + * + * @param {*} data Data to transform. + * @param {function(string=)} headers HTTP headers getter fn. + * @param {number} status HTTP status code of the response. + * @param {(Function|Array.)} fns Function or an array of functions. + * @returns {*} Transformed data. + */ +function transformData(data, headers, status, fns) { + if (isFunction(fns)) { + return fns(data, headers, status); + } + + forEach(fns, function(fn) { + data = fn(data, headers, status); + }); + + return data; +} + + +function isSuccess(status) { + return 200 <= status && status < 300; +} + + +/** + * @ngdoc provider + * @name $httpProvider + * @this + * + * @description + * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. + * */ +function $HttpProvider() { + /** + * @ngdoc property + * @name $httpProvider#defaults + * @description + * + * Object containing default values for all {@link ng.$http $http} requests. + * + * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with + * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses + * by default. See {@link $http#caching $http Caching} for more information. + * + * - **`defaults.headers`** - {Object} - Default headers for all $http requests. + * Refer to {@link ng.$http#setting-http-headers $http} for documentation on + * setting default headers. + * - **`defaults.headers.common`** + * - **`defaults.headers.post`** + * - **`defaults.headers.put`** + * - **`defaults.headers.patch`** + * + * - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the + * callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the + * {@link $jsonpCallbacks} service. Defaults to `'callback'`. + * + * - **`defaults.paramSerializer`** - `{string|function(Object):string}` - A function + * used to the prepare string representation of request parameters (specified as an object). + * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}. + * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}. + * + * - **`defaults.transformRequest`** - + * `{Array|function(data, headersGetter)}` - + * An array of functions (or a single function) which are applied to the request data. + * By default, this is an array with one request transformation function: + * + * - If the `data` property of the request configuration object contains an object, serialize it + * into JSON format. + * + * - **`defaults.transformResponse`** - + * `{Array|function(data, headersGetter, status)}` - + * An array of functions (or a single function) which are applied to the response data. By default, + * this is an array which applies one response transformation function that does two things: + * + * - If XSRF prefix is detected, strip it + * (see {@link ng.$http#security-considerations Security Considerations in the $http docs}). + * - If the `Content-Type` is `application/json` or the response looks like JSON, + * deserialize it using a JSON parser. + * + * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. + * Defaults value is `'XSRF-TOKEN'`. + * + * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the + * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. + * + **/ + var defaults = this.defaults = { + // transform incoming response data + transformResponse: [defaultHttpResponseTransform], + + // transform outgoing request data + transformRequest: [function(d) { + return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d; + }], + + // default headers + headers: { + common: { + 'Accept': 'application/json, text/plain, */*' + }, + post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) + }, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', + + paramSerializer: '$httpParamSerializer', + + jsonpCallbackParam: 'callback' + }; + + var useApplyAsync = false; + /** + * @ngdoc method + * @name $httpProvider#useApplyAsync + * @description + * + * Configure $http service to combine processing of multiple http responses received at around + * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in + * significant performance improvement for bigger applications that make many HTTP requests + * concurrently (common during application bootstrap). + * + * Defaults to false. If no value is specified, returns the current configured value. + * + * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred + * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window + * to load and share the same digest cycle. + * + * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. + * otherwise, returns the current configured value. + **/ + this.useApplyAsync = function(value) { + if (isDefined(value)) { + useApplyAsync = !!value; + return this; + } + return useApplyAsync; + }; + + /** + * @ngdoc property + * @name $httpProvider#interceptors + * @description + * + * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} + * pre-processing of request or postprocessing of responses. + * + * These service factories are ordered by request, i.e. they are applied in the same order as the + * array, on request, but reverse order, on response. + * + * {@link ng.$http#interceptors Interceptors detailed info} + **/ + var interceptorFactories = this.interceptors = []; + + this.$get = ['$browser', '$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', '$sce', + function($browser, $httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector, $sce) { + + var defaultCache = $cacheFactory('$http'); + + /** + * Make sure that default param serializer is exposed as a function + */ + defaults.paramSerializer = isString(defaults.paramSerializer) ? + $injector.get(defaults.paramSerializer) : defaults.paramSerializer; + + /** + * Interceptors stored in reverse order. Inner interceptors before outer interceptors. + * The reversal is needed so that we can build up the interception chain around the + * server request. + */ + var reversedInterceptors = []; + + forEach(interceptorFactories, function(interceptorFactory) { + reversedInterceptors.unshift(isString(interceptorFactory) + ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); + }); + + /** + * @ngdoc service + * @kind function + * @name $http + * @requires ng.$httpBackend + * @requires $cacheFactory + * @requires $rootScope + * @requires $q + * @requires $injector + * + * @description + * The `$http` service is a core AngularJS service that facilitates communication with the remote + * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) + * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). + * + * For unit testing applications that use `$http` service, see + * {@link ngMock.$httpBackend $httpBackend mock}. + * + * For a higher level of abstraction, please check out the {@link ngResource.$resource + * $resource} service. + * + * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by + * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage + * it is important to familiarize yourself with these APIs and the guarantees they provide. + * + * + * ## General usage + * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} — + * that is used to generate an HTTP request and returns a {@link ng.$q promise}. + * + * ```js + * // Simple GET request example: + * $http({ + * method: 'GET', + * url: '/someUrl' + * }).then(function successCallback(response) { + * // this callback will be called asynchronously + * // when the response is available + * }, function errorCallback(response) { + * // called asynchronously if an error occurs + * // or server returns response with an error status. + * }); + * ``` + * + * The response object has these properties: + * + * - **data** – `{string|Object}` – The response body transformed with the transform + * functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * - **statusText** – `{string}` – HTTP status text of the response. + * - **xhrStatus** – `{string}` – Status of the XMLHttpRequest (`complete`, `error`, `timeout` or `abort`). + * + * A response status code between 200 and 299 is considered a success status and will result in + * the success callback being called. Any response status code outside of that range is + * considered an error status and will result in the error callback being called. + * Also, status codes less than -1 are normalized to zero. -1 usually means the request was + * aborted, e.g. using a `config.timeout`. + * Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning + * that the outcome (success or error) will be determined by the final response status code. + * + * + * ## Shortcut methods + * + * Shortcut methods are also available. All shortcut methods require passing in the URL, and + * request data must be passed in for POST/PUT requests. An optional config can be passed as the + * last argument. + * + * ```js + * $http.get('/someUrl', config).then(successCallback, errorCallback); + * $http.post('/someUrl', data, config).then(successCallback, errorCallback); + * ``` + * + * Complete list of shortcut methods: + * + * - {@link ng.$http#get $http.get} + * - {@link ng.$http#head $http.head} + * - {@link ng.$http#post $http.post} + * - {@link ng.$http#put $http.put} + * - {@link ng.$http#delete $http.delete} + * - {@link ng.$http#jsonp $http.jsonp} + * - {@link ng.$http#patch $http.patch} + * + * + * ## Writing Unit Tests that use $http + * When unit testing (using {@link ngMock ngMock}), it is necessary to call + * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending + * request using trained responses. + * + * ``` + * $httpBackend.expectGET(...); + * $http.get(...); + * $httpBackend.flush(); + * ``` + * + * ## Setting HTTP Headers + * + * The $http service will automatically add certain HTTP headers to all requests. These defaults + * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration + * object, which currently contains this default configuration: + * + * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): + * - Accept: application/json, text/plain, \*/\* + * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) + * - `Content-Type: application/json` + * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) + * - `Content-Type: application/json` + * + * To add or overwrite these defaults, simply add or remove a property from these configuration + * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object + * with the lowercased HTTP method name as the key, e.g. + * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`. + * + * The defaults can also be set at runtime via the `$http.defaults` object in the same + * fashion. For example: + * + * ``` + * module.run(function($http) { + * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'; + * }); + * ``` + * + * In addition, you can supply a `headers` property in the config object passed when + * calling `$http(config)`, which overrides the defaults without changing them globally. + * + * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, + * Use the `headers` property, setting the desired header to `undefined`. For example: + * + * ```js + * var req = { + * method: 'POST', + * url: 'http://example.com', + * headers: { + * 'Content-Type': undefined + * }, + * data: { test: 'test' } + * } + * + * $http(req).then(function(){...}, function(){...}); + * ``` + * + * ## Transforming Requests and Responses + * + * Both requests and responses can be transformed using transformation functions: `transformRequest` + * and `transformResponse`. These properties can be a single function that returns + * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions, + * which allows you to `push` or `unshift` a new transformation function into the transformation chain. + * + *
    + * **Note:** AngularJS does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline. + * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference). + * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest + * function will be reflected on the scope and in any templates where the object is data-bound. + * To prevent this, transform functions should have no side-effects. + * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return. + *
    + * + * ### Default Transformations + * + * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and + * `defaults.transformResponse` properties. If a request does not provide its own transformations + * then these will be applied. + * + * You can augment or replace the default transformations by modifying these properties by adding to or + * replacing the array. + * + * AngularJS provides the following default transformations: + * + * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`) is + * an array with one function that does the following: + * + * - If the `data` property of the request configuration object contains an object, serialize it + * into JSON format. + * + * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`) is + * an array with one function that does the following: + * + * - If XSRF prefix is detected, strip it (see Security Considerations section below). + * - If the `Content-Type` is `application/json` or the response looks like JSON, + * deserialize it using a JSON parser. + * + * + * ### Overriding the Default Transformations Per Request + * + * If you wish to override the request/response transformations only for a single request then provide + * `transformRequest` and/or `transformResponse` properties on the configuration object passed + * into `$http`. + * + * Note that if you provide these properties on the config object the default transformations will be + * overwritten. If you wish to augment the default transformations then you must include them in your + * local transformation array. + * + * The following code demonstrates adding a new response transformation to be run after the default response + * transformations have been run. + * + * ```js + * function appendTransform(defaults, transform) { + * + * // We can't guarantee that the default transformation is an array + * defaults = angular.isArray(defaults) ? defaults : [defaults]; + * + * // Append the new transformation to the defaults + * return defaults.concat(transform); + * } + * + * $http({ + * url: '...', + * method: 'GET', + * transformResponse: appendTransform($http.defaults.transformResponse, function(value) { + * return doTransform(value); + * }) + * }); + * ``` + * + * + * ## Caching + * + * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must + * set the config.cache value or the default cache value to TRUE or to a cache object (created + * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes + * precedence over the default cache value. + * + * In order to: + * * cache all responses - set the default cache value to TRUE or to a cache object + * * cache a specific response - set config.cache value to TRUE or to a cache object + * + * If caching is enabled, but neither the default cache nor config.cache are set to a cache object, + * then the default `$cacheFactory("$http")` object is used. + * + * The default cache value can be set by updating the + * {@link ng.$http#defaults `$http.defaults.cache`} property or the + * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property. + * + * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using + * the relevant cache object. The next time the same request is made, the response is returned + * from the cache without sending a request to the server. + * + * Take note that: + * + * * Only GET and JSONP requests are cached. + * * The cache key is the request URL including search parameters; headers are not considered. + * * Cached responses are returned asynchronously, in the same way as responses from the server. + * * If multiple identical requests are made using the same cache, which is not yet populated, + * one request will be made to the server and remaining requests will return the same response. + * * A cache-control header on the response does not affect if or how responses are cached. + * + * + * ## Interceptors + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication, or any kind of synchronous or + * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be + * able to intercept requests before they are handed to the server and + * responses before they are handed over to the application code that + * initiated these requests. The interceptors leverage the {@link ng.$q + * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. + * + * The interceptors are service factories that are registered with the `$httpProvider` by + * adding them to the `$httpProvider.interceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor. + * + * There are two kinds of interceptors (and two kinds of rejection interceptors): + * + * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to + * modify the `config` object or create a new one. The function needs to return the `config` + * object directly, or a promise containing the `config` or a new `config` object. + * * `requestError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * * `response`: interceptors get called with http `response` object. The function is free to + * modify the `response` object or create a new one. The function needs to return the `response` + * object directly, or as a promise containing the `response` or a new `response` object. + * * `responseError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * + * + * ```js + * // register the interceptor as a service + * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { + * return { + * // optional method + * 'request': function(config) { + * // do something on success + * return config; + * }, + * + * // optional method + * 'requestError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * }, + * + * + * + * // optional method + * 'response': function(response) { + * // do something on success + * return response; + * }, + * + * // optional method + * 'responseError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * } + * }; + * }); + * + * $httpProvider.interceptors.push('myHttpInterceptor'); + * + * + * // alternatively, register the interceptor via an anonymous factory + * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { + * return { + * 'request': function(config) { + * // same as above + * }, + * + * 'response': function(response) { + * // same as above + * } + * }; + * }); + * ``` + * + * ## Security Considerations + * + * When designing web applications, consider security threats from: + * + * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) + * + * Both server and the client must cooperate in order to eliminate these threats. AngularJS comes + * pre-configured with strategies that address these issues, but for this to work backend server + * cooperation is required. + * + * ### JSON Vulnerability Protection + * + * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * allows third party website to turn your JSON resource URL into + * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To + * counter this your server can prefix all JSON requests with following string `")]}',\n"`. + * AngularJS will automatically strip the prefix before processing it as JSON. + * + * For example if your server needs to return: + * ```js + * ['one','two'] + * ``` + * + * which is vulnerable to attack, your server can return: + * ```js + * )]}', + * ['one','two'] + * ``` + * + * AngularJS will strip the prefix, before processing the JSON. + * + * + * ### Cross Site Request Forgery (XSRF) Protection + * + * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by + * which the attacker can trick an authenticated user into unknowingly executing actions on your + * website. AngularJS provides a mechanism to counter XSRF. When performing XHR requests, the + * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP + * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the + * cookie, your server can be assured that the XHR came from JavaScript running on your domain. + * The header will not be set for cross-domain requests. + * + * To take advantage of this, your server needs to set a token in a JavaScript readable session + * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the + * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure + * that only JavaScript running on your domain could have sent the request. The token must be + * unique for each user and must be verifiable by the server (to prevent the JavaScript from + * making up its own tokens). We recommend that the token is a digest of your site's + * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) + * for added security. + * + * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName + * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, + * or the per-request config object. + * + * In order to prevent collisions in environments where multiple AngularJS apps share the + * same domain or subdomain, we recommend that each application uses unique cookie name. + * + * @param {object} config Object describing the request to be made and how it should be + * processed. The object has following properties: + * + * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) + * - **url** – `{string|TrustedObject}` – Absolute or relative URL of the resource that is being requested; + * or an object created by a call to `$sce.trustAsResourceUrl(url)`. + * - **params** – `{Object.}` – Map of strings or objects which will be serialized + * with the `paramSerializer` and appended as GET parameters. + * - **data** – `{string|Object}` – Data to be sent as the request message data. + * - **headers** – `{Object}` – Map of strings or functions which return strings representing + * HTTP headers to send to the server. If the return value of a function is null, the + * header will not be sent. Functions accept a config object as an argument. + * - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object. + * To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`. + * The handler will be called in the context of a `$apply` block. + * - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload + * object. To bind events to the XMLHttpRequest object, use `eventHandlers`. + * The handler will be called in the context of a `$apply` block. + * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. + * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. + * - **transformRequest** – + * `{function(data, headersGetter)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * request body and headers and returns its transformed (typically serialized) version. + * See {@link ng.$http#overriding-the-default-transformations-per-request + * Overriding the Default Transformations} + * - **transformResponse** – + * `{function(data, headersGetter, status)|Array.}` – + * transform function or an array of such functions. The transform function takes the http + * response body, headers and status and returns its transformed (typically deserialized) version. + * See {@link ng.$http#overriding-the-default-transformations-per-request + * Overriding the Default Transformations} + * - **paramSerializer** - `{string|function(Object):string}` - A function used to + * prepare the string representation of request parameters (specified as an object). + * If specified as string, it is interpreted as function registered with the + * {@link $injector $injector}, which means you can create your own serializer + * by registering it as a {@link auto.$provide#service service}. + * The default serializer is the {@link $httpParamSerializer $httpParamSerializer}; + * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike} + * - **cache** – `{boolean|Object}` – A boolean value or object created with + * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response. + * See {@link $http#caching $http Caching} for more information. + * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} + * that should abort the request when resolved. + * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the + * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) + * for more information. + * - **responseType** - `{string}` - see + * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype). + * + * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object + * when the request succeeds or fails. + * + * + * @property {Array.} pendingRequests Array of config objects for currently pending + * requests. This is primarily meant to be used for debugging purposes. + * + * + * @example + + +
    + + +
    + + + +
    http status code: {{status}}
    +
    http response data: {{data}}
    +
    +
    + + angular.module('httpExample', []) + .config(['$sceDelegateProvider', function($sceDelegateProvider) { + // We must whitelist the JSONP endpoint that we are using to show that we trust it + $sceDelegateProvider.resourceUrlWhitelist([ + 'self', + 'https://angularjs.org/**' + ]); + }]) + .controller('FetchController', ['$scope', '$http', '$templateCache', + function($scope, $http, $templateCache) { + $scope.method = 'GET'; + $scope.url = 'http-hello.html'; + + $scope.fetch = function() { + $scope.code = null; + $scope.response = null; + + $http({method: $scope.method, url: $scope.url, cache: $templateCache}). + then(function(response) { + $scope.status = response.status; + $scope.data = response.data; + }, function(response) { + $scope.data = response.data || 'Request failed'; + $scope.status = response.status; + }); + }; + + $scope.updateModel = function(method, url) { + $scope.method = method; + $scope.url = url; + }; + }]); + + + Hello, $http! + + + var status = element(by.binding('status')); + var data = element(by.binding('data')); + var fetchBtn = element(by.id('fetchbtn')); + var sampleGetBtn = element(by.id('samplegetbtn')); + var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); + + it('should make an xhr GET request', function() { + sampleGetBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('200'); + expect(data.getText()).toMatch(/Hello, \$http!/); + }); + +// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185 +// it('should make a JSONP request to angularjs.org', function() { +// var sampleJsonpBtn = element(by.id('samplejsonpbtn')); +// sampleJsonpBtn.click(); +// fetchBtn.click(); +// expect(status.getText()).toMatch('200'); +// expect(data.getText()).toMatch(/Super Hero!/); +// }); + + it('should make JSONP request to invalid URL and invoke the error handler', + function() { + invalidJsonpBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('0'); + expect(data.getText()).toMatch('Request failed'); + }); + +
    + */ + function $http(requestConfig) { + + if (!isObject(requestConfig)) { + throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); + } + + if (!isString($sce.valueOf(requestConfig.url))) { + throw minErr('$http')('badreq', 'Http request configuration url must be a string or a $sce trusted object. Received: {0}', requestConfig.url); + } + + var config = extend({ + method: 'get', + transformRequest: defaults.transformRequest, + transformResponse: defaults.transformResponse, + paramSerializer: defaults.paramSerializer, + jsonpCallbackParam: defaults.jsonpCallbackParam + }, requestConfig); + + config.headers = mergeHeaders(requestConfig); + config.method = uppercase(config.method); + config.paramSerializer = isString(config.paramSerializer) ? + $injector.get(config.paramSerializer) : config.paramSerializer; + + $browser.$$incOutstandingRequestCount(); + + var requestInterceptors = []; + var responseInterceptors = []; + var promise = $q.resolve(config); + + // apply interceptors + forEach(reversedInterceptors, function(interceptor) { + if (interceptor.request || interceptor.requestError) { + requestInterceptors.unshift(interceptor.request, interceptor.requestError); + } + if (interceptor.response || interceptor.responseError) { + responseInterceptors.push(interceptor.response, interceptor.responseError); + } + }); + + promise = chainInterceptors(promise, requestInterceptors); + promise = promise.then(serverRequest); + promise = chainInterceptors(promise, responseInterceptors); + promise = promise.finally(completeOutstandingRequest); + + return promise; + + + function chainInterceptors(promise, interceptors) { + for (var i = 0, ii = interceptors.length; i < ii;) { + var thenFn = interceptors[i++]; + var rejectFn = interceptors[i++]; + + promise = promise.then(thenFn, rejectFn); + } + + interceptors.length = 0; + + return promise; + } + + function completeOutstandingRequest() { + $browser.$$completeOutstandingRequest(noop); + } + + function executeHeaderFns(headers, config) { + var headerContent, processedHeaders = {}; + + forEach(headers, function(headerFn, header) { + if (isFunction(headerFn)) { + headerContent = headerFn(config); + if (headerContent != null) { + processedHeaders[header] = headerContent; + } + } else { + processedHeaders[header] = headerFn; + } + }); + + return processedHeaders; + } + + function mergeHeaders(config) { + var defHeaders = defaults.headers, + reqHeaders = extend({}, config.headers), + defHeaderName, lowercaseDefHeaderName, reqHeaderName; + + defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); + + // using for-in instead of forEach to avoid unnecessary iteration after header has been found + defaultHeadersIteration: + for (defHeaderName in defHeaders) { + lowercaseDefHeaderName = lowercase(defHeaderName); + + for (reqHeaderName in reqHeaders) { + if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { + continue defaultHeadersIteration; + } + } + + reqHeaders[defHeaderName] = defHeaders[defHeaderName]; + } + + // execute if header value is a function for merged headers + return executeHeaderFns(reqHeaders, shallowCopy(config)); + } + + function serverRequest(config) { + var headers = config.headers; + var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest); + + // strip content-type if data is undefined + if (isUndefined(reqData)) { + forEach(headers, function(value, header) { + if (lowercase(header) === 'content-type') { + delete headers[header]; + } + }); + } + + if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { + config.withCredentials = defaults.withCredentials; + } + + // send request + return sendReq(config, reqData).then(transformResponse, transformResponse); + } + + function transformResponse(response) { + // make a copy since the response must be cacheable + var resp = extend({}, response); + resp.data = transformData(response.data, response.headers, response.status, + config.transformResponse); + return (isSuccess(response.status)) + ? resp + : $q.reject(resp); + } + } + + $http.pendingRequests = []; + + /** + * @ngdoc method + * @name $http#get + * + * @description + * Shortcut method to perform `GET` request. + * + * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; + * or an object created by a call to `$sce.trustAsResourceUrl(url)`. + * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#delete + * + * @description + * Shortcut method to perform `DELETE` request. + * + * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; + * or an object created by a call to `$sce.trustAsResourceUrl(url)`. + * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#head + * + * @description + * Shortcut method to perform `HEAD` request. + * + * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; + * or an object created by a call to `$sce.trustAsResourceUrl(url)`. + * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#jsonp + * + * @description + * Shortcut method to perform `JSONP` request. + * + * Note that, since JSONP requests are sensitive because the response is given full access to the browser, + * the url must be declared, via {@link $sce} as a trusted resource URL. + * You can trust a URL by adding it to the whitelist via + * {@link $sceDelegateProvider#resourceUrlWhitelist `$sceDelegateProvider.resourceUrlWhitelist`} or + * by explicitly trusting the URL via {@link $sce#trustAsResourceUrl `$sce.trustAsResourceUrl(url)`}. + * + * You should avoid generating the URL for the JSONP request from user provided data. + * Provide additional query parameters via `params` property of the `config` parameter, rather than + * modifying the URL itself. + * + * JSONP requests must specify a callback to be used in the response from the server. This callback + * is passed as a query parameter in the request. You must specify the name of this parameter by + * setting the `jsonpCallbackParam` property on the request config object. + * + * ``` + * $http.jsonp('some/trusted/url', {jsonpCallbackParam: 'callback'}) + * ``` + * + * You can also specify a default callback parameter name in `$http.defaults.jsonpCallbackParam`. + * Initially this is set to `'callback'`. + * + *
    + * You can no longer use the `JSON_CALLBACK` string as a placeholder for specifying where the callback + * parameter value should go. + *
    + * + * If you would like to customise where and how the callbacks are stored then try overriding + * or decorating the {@link $jsonpCallbacks} service. + * + * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested; + * or an object created by a call to `$sce.trustAsResourceUrl(url)`. + * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage + * @returns {HttpPromise} Future object + */ + createShortMethods('get', 'delete', 'head', 'jsonp'); + + /** + * @ngdoc method + * @name $http#post + * + * @description + * Shortcut method to perform `POST` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#put + * + * @description + * Shortcut method to perform `PUT` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#patch + * + * @description + * Shortcut method to perform `PATCH` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage + * @returns {HttpPromise} Future object + */ + createShortMethodsWithData('post', 'put', 'patch'); + + /** + * @ngdoc property + * @name $http#defaults + * + * @description + * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of + * default headers, withCredentials as well as request and response transformations. + * + * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. + */ + $http.defaults = defaults; + + + return $http; + + + function createShortMethods(names) { + forEach(arguments, function(name) { + $http[name] = function(url, config) { + return $http(extend({}, config || {}, { + method: name, + url: url + })); + }; + }); + } + + + function createShortMethodsWithData(name) { + forEach(arguments, function(name) { + $http[name] = function(url, data, config) { + return $http(extend({}, config || {}, { + method: name, + url: url, + data: data + })); + }; + }); + } + + + /** + * Makes the request. + * + * !!! ACCESSES CLOSURE VARS: + * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests + */ + function sendReq(config, reqData) { + var deferred = $q.defer(), + promise = deferred.promise, + cache, + cachedResp, + reqHeaders = config.headers, + isJsonp = lowercase(config.method) === 'jsonp', + url = config.url; + + if (isJsonp) { + // JSONP is a pretty sensitive operation where we're allowing a script to have full access to + // our DOM and JS space. So we require that the URL satisfies SCE.RESOURCE_URL. + url = $sce.getTrustedResourceUrl(url); + } else if (!isString(url)) { + // If it is not a string then the URL must be a $sce trusted object + url = $sce.valueOf(url); + } + + url = buildUrl(url, config.paramSerializer(config.params)); + + if (isJsonp) { + // Check the url and add the JSONP callback placeholder + url = sanitizeJsonpCallbackParam(url, config.jsonpCallbackParam); + } + + $http.pendingRequests.push(config); + promise.then(removePendingReq, removePendingReq); + + if ((config.cache || defaults.cache) && config.cache !== false && + (config.method === 'GET' || config.method === 'JSONP')) { + cache = isObject(config.cache) ? config.cache + : isObject(/** @type {?} */ (defaults).cache) + ? /** @type {?} */ (defaults).cache + : defaultCache; + } + + if (cache) { + cachedResp = cache.get(url); + if (isDefined(cachedResp)) { + if (isPromiseLike(cachedResp)) { + // cached request has already been sent, but there is no response yet + cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult); + } else { + // serving from cache + if (isArray(cachedResp)) { + resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3], cachedResp[4]); + } else { + resolvePromise(cachedResp, 200, {}, 'OK', 'complete'); + } + } + } else { + // put the promise for the non-transformed response into cache as a placeholder + cache.put(url, promise); + } + } + + + // if we won't have the response in cache, set the xsrf headers and + // send the request to the backend + if (isUndefined(cachedResp)) { + var xsrfValue = urlIsSameOrigin(config.url) + ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName] + : undefined; + if (xsrfValue) { + reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; + } + + $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, + config.withCredentials, config.responseType, + createApplyHandlers(config.eventHandlers), + createApplyHandlers(config.uploadEventHandlers)); + } + + return promise; + + function createApplyHandlers(eventHandlers) { + if (eventHandlers) { + var applyHandlers = {}; + forEach(eventHandlers, function(eventHandler, key) { + applyHandlers[key] = function(event) { + if (useApplyAsync) { + $rootScope.$applyAsync(callEventHandler); + } else if ($rootScope.$$phase) { + callEventHandler(); + } else { + $rootScope.$apply(callEventHandler); + } + + function callEventHandler() { + eventHandler(event); + } + }; + }); + return applyHandlers; + } + } + + + /** + * Callback registered to $httpBackend(): + * - caches the response if desired + * - resolves the raw $http promise + * - calls $apply + */ + function done(status, response, headersString, statusText, xhrStatus) { + if (cache) { + if (isSuccess(status)) { + cache.put(url, [status, response, parseHeaders(headersString), statusText, xhrStatus]); + } else { + // remove promise from the cache + cache.remove(url); + } + } + + function resolveHttpPromise() { + resolvePromise(response, status, headersString, statusText, xhrStatus); + } + + if (useApplyAsync) { + $rootScope.$applyAsync(resolveHttpPromise); + } else { + resolveHttpPromise(); + if (!$rootScope.$$phase) $rootScope.$apply(); + } + } + + + /** + * Resolves the raw $http promise. + */ + function resolvePromise(response, status, headers, statusText, xhrStatus) { + //status: HTTP response status code, 0, -1 (aborted by timeout / promise) + status = status >= -1 ? status : 0; + + (isSuccess(status) ? deferred.resolve : deferred.reject)({ + data: response, + status: status, + headers: headersGetter(headers), + config: config, + statusText: statusText, + xhrStatus: xhrStatus + }); + } + + function resolvePromiseWithResult(result) { + resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText, result.xhrStatus); + } + + function removePendingReq() { + var idx = $http.pendingRequests.indexOf(config); + if (idx !== -1) $http.pendingRequests.splice(idx, 1); + } + } + + + function buildUrl(url, serializedParams) { + if (serializedParams.length > 0) { + url += ((url.indexOf('?') === -1) ? '?' : '&') + serializedParams; + } + return url; + } + + function sanitizeJsonpCallbackParam(url, cbKey) { + var parts = url.split('?'); + if (parts.length > 2) { + // Throw if the url contains more than one `?` query indicator + throw $httpMinErr('badjsonp', 'Illegal use more than one "?", in url, "{1}"', url); + } + var params = parseKeyValue(parts[1]); + forEach(params, function(value, key) { + if (value === 'JSON_CALLBACK') { + // Throw if the url already contains a reference to JSON_CALLBACK + throw $httpMinErr('badjsonp', 'Illegal use of JSON_CALLBACK in url, "{0}"', url); + } + if (key === cbKey) { + // Throw if the callback param was already provided + throw $httpMinErr('badjsonp', 'Illegal use of callback param, "{0}", in url, "{1}"', cbKey, url); + } + }); + + // Add in the JSON_CALLBACK callback param value + url += ((url.indexOf('?') === -1) ? '?' : '&') + cbKey + '=JSON_CALLBACK'; + + return url; + } + }]; +} + +/** + * @ngdoc service + * @name $xhrFactory + * @this + * + * @description + * Factory function used to create XMLHttpRequest objects. + * + * Replace or decorate this service to create your own custom XMLHttpRequest objects. + * + * ``` + * angular.module('myApp', []) + * .factory('$xhrFactory', function() { + * return function createXhr(method, url) { + * return new window.XMLHttpRequest({mozSystem: true}); + * }; + * }); + * ``` + * + * @param {string} method HTTP method of the request (GET, POST, PUT, ..) + * @param {string} url URL of the request. + */ +function $xhrFactoryProvider() { + this.$get = function() { + return function createXhr() { + return new window.XMLHttpRequest(); + }; + }; +} + +/** + * @ngdoc service + * @name $httpBackend + * @requires $jsonpCallbacks + * @requires $document + * @requires $xhrFactory + * @this + * + * @description + * HTTP backend used by the {@link ng.$http service} that delegates to + * XMLHttpRequest object or JSONP and deals with browser incompatibilities. + * + * You should never need to use this service directly, instead use the higher-level abstractions: + * {@link ng.$http $http} or {@link ngResource.$resource $resource}. + * + * During testing this implementation is swapped with {@link ngMock.$httpBackend mock + * $httpBackend} which can be trained with responses. + */ +function $HttpBackendProvider() { + this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) { + return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]); + }]; +} + +function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { + // TODO(vojta): fix the signature + return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) { + url = url || $browser.url(); + + if (lowercase(method) === 'jsonp') { + var callbackPath = callbacks.createCallback(url); + var jsonpDone = jsonpReq(url, callbackPath, function(status, text) { + // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING) + var response = (status === 200) && callbacks.getResponse(callbackPath); + completeRequest(callback, status, response, '', text, 'complete'); + callbacks.removeCallback(callbackPath); + }); + } else { + + var xhr = createXhr(method, url); + + xhr.open(method, url, true); + forEach(headers, function(value, key) { + if (isDefined(value)) { + xhr.setRequestHeader(key, value); + } + }); + + xhr.onload = function requestLoaded() { + var statusText = xhr.statusText || ''; + + // responseText is the old-school way of retrieving response (supported by IE9) + // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) + var response = ('response' in xhr) ? xhr.response : xhr.responseText; + + // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) + var status = xhr.status === 1223 ? 204 : xhr.status; + + // fix status code when it is 0 (0 status is undocumented). + // Occurs when accessing file resources or on Android 4.1 stock browser + // while retrieving files from application cache. + if (status === 0) { + status = response ? 200 : urlResolve(url).protocol === 'file' ? 404 : 0; + } + + completeRequest(callback, + status, + response, + xhr.getAllResponseHeaders(), + statusText, + 'complete'); + }; + + var requestError = function() { + // The response is always empty + // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error + completeRequest(callback, -1, null, null, '', 'error'); + }; + + var requestAborted = function() { + completeRequest(callback, -1, null, null, '', 'abort'); + }; + + var requestTimeout = function() { + // The response is always empty + // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error + completeRequest(callback, -1, null, null, '', 'timeout'); + }; + + xhr.onerror = requestError; + xhr.onabort = requestAborted; + xhr.ontimeout = requestTimeout; + + forEach(eventHandlers, function(value, key) { + xhr.addEventListener(key, value); + }); + + forEach(uploadEventHandlers, function(value, key) { + xhr.upload.addEventListener(key, value); + }); + + if (withCredentials) { + xhr.withCredentials = true; + } + + if (responseType) { + try { + xhr.responseType = responseType; + } catch (e) { + // WebKit added support for the json responseType value on 09/03/2013 + // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are + // known to throw when setting the value "json" as the response type. Other older + // browsers implementing the responseType + // + // The json response type can be ignored if not supported, because JSON payloads are + // parsed on the client-side regardless. + if (responseType !== 'json') { + throw e; + } + } + } + + xhr.send(isUndefined(post) ? null : post); + } + + if (timeout > 0) { + var timeoutId = $browserDefer(timeoutRequest, timeout); + } else if (isPromiseLike(timeout)) { + timeout.then(timeoutRequest); + } + + + function timeoutRequest() { + if (jsonpDone) { + jsonpDone(); + } + if (xhr) { + xhr.abort(); + } + } + + function completeRequest(callback, status, response, headersString, statusText, xhrStatus) { + // cancel timeout and subsequent timeout promise resolution + if (isDefined(timeoutId)) { + $browserDefer.cancel(timeoutId); + } + jsonpDone = xhr = null; + + callback(status, response, headersString, statusText, xhrStatus); + } + }; + + function jsonpReq(url, callbackPath, done) { + url = url.replace('JSON_CALLBACK', callbackPath); + // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.: + // - fetches local scripts via XHR and evals them + // - adds and immediately removes script elements from the document + var script = rawDocument.createElement('script'), callback = null; + script.type = 'text/javascript'; + script.src = url; + script.async = true; + + callback = function(event) { + script.removeEventListener('load', callback); + script.removeEventListener('error', callback); + rawDocument.body.removeChild(script); + script = null; + var status = -1; + var text = 'unknown'; + + if (event) { + if (event.type === 'load' && !callbacks.wasCalled(callbackPath)) { + event = { type: 'error' }; + } + text = event.type; + status = event.type === 'error' ? 404 : 200; + } + + if (done) { + done(status, text); + } + }; + + script.addEventListener('load', callback); + script.addEventListener('error', callback); + rawDocument.body.appendChild(script); + return callback; + } +} + +var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate'); +$interpolateMinErr.throwNoconcat = function(text) { + throw $interpolateMinErr('noconcat', + 'Error while interpolating: {0}\nStrict Contextual Escaping disallows ' + + 'interpolations that concatenate multiple expressions when a trusted value is ' + + 'required. See http://docs.angularjs.org/api/ng.$sce', text); +}; + +$interpolateMinErr.interr = function(text, err) { + return $interpolateMinErr('interr', 'Can\'t interpolate: {0}\n{1}', text, err.toString()); +}; + +/** + * @ngdoc provider + * @name $interpolateProvider + * @this + * + * @description + * + * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. + * + *
    + * This feature is sometimes used to mix different markup languages, e.g. to wrap an AngularJS + * template within a Python Jinja template (or any other template language). Mixing templating + * languages is **very dangerous**. The embedding template language will not safely escape AngularJS + * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS) + * security bugs! + *
    + * + * @example + + + +
    + //demo.label// +
    +
    + + it('should interpolate binding with custom symbols', function() { + expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); + }); + +
    + */ +function $InterpolateProvider() { + var startSymbol = '{{'; + var endSymbol = '}}'; + + /** + * @ngdoc method + * @name $interpolateProvider#startSymbol + * @description + * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. + * + * @param {string=} value new value to set the starting symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.startSymbol = function(value) { + if (value) { + startSymbol = value; + return this; + } else { + return startSymbol; + } + }; + + /** + * @ngdoc method + * @name $interpolateProvider#endSymbol + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * @param {string=} value new value to set the ending symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.endSymbol = function(value) { + if (value) { + endSymbol = value; + return this; + } else { + return endSymbol; + } + }; + + + this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { + var startSymbolLength = startSymbol.length, + endSymbolLength = endSymbol.length, + escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'), + escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g'); + + function escape(ch) { + return '\\\\\\' + ch; + } + + function unescapeText(text) { + return text.replace(escapedStartRegexp, startSymbol). + replace(escapedEndRegexp, endSymbol); + } + + // TODO: this is the same as the constantWatchDelegate in parse.js + function constantWatchDelegate(scope, listener, objectEquality, constantInterp) { + var unwatch = scope.$watch(function constantInterpolateWatch(scope) { + unwatch(); + return constantInterp(scope); + }, listener, objectEquality); + return unwatch; + } + + /** + * @ngdoc service + * @name $interpolate + * @kind function + * + * @requires $parse + * @requires $sce + * + * @description + * + * Compiles a string with markup into an interpolation function. This service is used by the + * HTML {@link ng.$compile $compile} service for data binding. See + * {@link ng.$interpolateProvider $interpolateProvider} for configuring the + * interpolation markup. + * + * + * ```js + * var $interpolate = ...; // injected + * var exp = $interpolate('Hello {{name | uppercase}}!'); + * expect(exp({name:'AngularJS'})).toEqual('Hello ANGULAR!'); + * ``` + * + * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is + * `true`, the interpolation function will return `undefined` unless all embedded expressions + * evaluate to a value other than `undefined`. + * + * ```js + * var $interpolate = ...; // injected + * var context = {greeting: 'Hello', name: undefined }; + * + * // default "forgiving" mode + * var exp = $interpolate('{{greeting}} {{name}}!'); + * expect(exp(context)).toEqual('Hello !'); + * + * // "allOrNothing" mode + * exp = $interpolate('{{greeting}} {{name}}!', false, null, true); + * expect(exp(context)).toBeUndefined(); + * context.name = 'AngularJS'; + * expect(exp(context)).toEqual('Hello AngularJS!'); + * ``` + * + * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior. + * + * #### Escaped Interpolation + * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers + * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash). + * It will be rendered as a regular start/end marker, and will not be interpreted as an expression + * or binding. + * + * This enables web-servers to prevent script injection attacks and defacing attacks, to some + * degree, while also enabling code examples to work without relying on the + * {@link ng.directive:ngNonBindable ngNonBindable} directive. + * + * **For security purposes, it is strongly encouraged that web servers escape user-supplied data, + * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all + * interpolation start/end markers with their escaped counterparts.** + * + * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered + * output when the $interpolate service processes the text. So, for HTML elements interpolated + * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter + * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such, + * this is typically useful only when user-data is used in rendering a template from the server, or + * when otherwise untrusted data is used by a directive. + * + * + * + *
    + *

    {{apptitle}}: \{\{ username = "defaced value"; \}\} + *

    + *

    {{username}} attempts to inject code which will deface the + * application, but fails to accomplish their task, because the server has correctly + * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash) + * characters.

    + *

    Instead, the result of the attempted script injection is visible, and can be removed + * from the database by an administrator.

    + *
    + *
    + *
    + * + * @knownIssue + * It is currently not possible for an interpolated expression to contain the interpolation end + * symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e. + * an interpolated expression consisting of a single-quote (`'`) and the `' }}` string. + * + * @knownIssue + * All directives and components must use the standard `{{` `}}` interpolation symbols + * in their templates. If you change the application interpolation symbols the {@link $compile} + * service will attempt to denormalize the standard symbols to the custom symbols. + * The denormalization process is not clever enough to know not to replace instances of the standard + * symbols where they would not normally be treated as interpolation symbols. For example in the following + * code snippet the closing braces of the literal object will get incorrectly denormalized: + * + * ``` + *
    + * ``` + * + * See https://github.com/angular/angular.js/pull/14610#issuecomment-219401099 for more information. + * + * @param {string} text The text with markup to interpolate. + * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have + * embedded expression in order to return an interpolation function. Strings with no + * embedded expression will return null for the interpolation function. + * @param {string=} trustedContext when provided, the returned function passes the interpolated + * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, + * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that + * provides Strict Contextual Escaping for details. + * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined + * unless all embedded expressions evaluate to a value other than `undefined`. + * @returns {function(context)} an interpolation function which is used to compute the + * interpolated string. The function has these parameters: + * + * - `context`: evaluation context for all expressions embedded in the interpolated text + */ + function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) { + // Provide a quick exit and simplified result function for text with no interpolation + if (!text.length || text.indexOf(startSymbol) === -1) { + var constantInterp; + if (!mustHaveExpression) { + var unescapedText = unescapeText(text); + constantInterp = valueFn(unescapedText); + constantInterp.exp = text; + constantInterp.expressions = []; + constantInterp.$$watchDelegate = constantWatchDelegate; + } + return constantInterp; + } + + allOrNothing = !!allOrNothing; + var startIndex, + endIndex, + index = 0, + expressions = [], + parseFns = [], + textLength = text.length, + exp, + concat = [], + expressionPositions = []; + + while (index < textLength) { + if (((startIndex = text.indexOf(startSymbol, index)) !== -1) && + ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1)) { + if (index !== startIndex) { + concat.push(unescapeText(text.substring(index, startIndex))); + } + exp = text.substring(startIndex + startSymbolLength, endIndex); + expressions.push(exp); + parseFns.push($parse(exp, parseStringifyInterceptor)); + index = endIndex + endSymbolLength; + expressionPositions.push(concat.length); + concat.push(''); + } else { + // we did not find an interpolation, so we have to add the remainder to the separators array + if (index !== textLength) { + concat.push(unescapeText(text.substring(index))); + } + break; + } + } + + // Concatenating expressions makes it hard to reason about whether some combination of + // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a + // single expression be used for iframe[src], object[src], etc., we ensure that the value + // that's used is assigned or constructed by some JS code somewhere that is more testable or + // make it obvious that you bound the value to some user controlled value. This helps reduce + // the load when auditing for XSS issues. + if (trustedContext && concat.length > 1) { + $interpolateMinErr.throwNoconcat(text); + } + + if (!mustHaveExpression || expressions.length) { + var compute = function(values) { + for (var i = 0, ii = expressions.length; i < ii; i++) { + if (allOrNothing && isUndefined(values[i])) return; + concat[expressionPositions[i]] = values[i]; + } + return concat.join(''); + }; + + var getValue = function(value) { + return trustedContext ? + $sce.getTrusted(trustedContext, value) : + $sce.valueOf(value); + }; + + return extend(function interpolationFn(context) { + var i = 0; + var ii = expressions.length; + var values = new Array(ii); + + try { + for (; i < ii; i++) { + values[i] = parseFns[i](context); + } + + return compute(values); + } catch (err) { + $exceptionHandler($interpolateMinErr.interr(text, err)); + } + + }, { + // all of these properties are undocumented for now + exp: text, //just for compatibility with regular watchers created via $watch + expressions: expressions, + $$watchDelegate: function(scope, listener) { + var lastValue; + return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) { + var currValue = compute(values); + listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); + lastValue = currValue; + }); + } + }); + } + + function parseStringifyInterceptor(value) { + try { + value = getValue(value); + return allOrNothing && !isDefined(value) ? value : stringify(value); + } catch (err) { + $exceptionHandler($interpolateMinErr.interr(text, err)); + } + } + } + + + /** + * @ngdoc method + * @name $interpolate#startSymbol + * @description + * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. + * + * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change + * the symbol. + * + * @returns {string} start symbol. + */ + $interpolate.startSymbol = function() { + return startSymbol; + }; + + + /** + * @ngdoc method + * @name $interpolate#endSymbol + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change + * the symbol. + * + * @returns {string} end symbol. + */ + $interpolate.endSymbol = function() { + return endSymbol; + }; + + return $interpolate; + }]; +} + +/** @this */ +function $IntervalProvider() { + this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser', + function($rootScope, $window, $q, $$q, $browser) { + var intervals = {}; + + + /** + * @ngdoc service + * @name $interval + * + * @description + * AngularJS's wrapper for `window.setInterval`. The `fn` function is executed every `delay` + * milliseconds. + * + * The return value of registering an interval function is a promise. This promise will be + * notified upon each tick of the interval, and will be resolved after `count` iterations, or + * run indefinitely if `count` is not defined. The value of the notification will be the + * number of iterations that have run. + * To cancel an interval, call `$interval.cancel(promise)`. + * + * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to + * move forward by `millis` milliseconds and trigger any functions scheduled to run in that + * time. + * + *
    + * **Note**: Intervals created by this service must be explicitly destroyed when you are finished + * with them. In particular they are not automatically destroyed when a controller's scope or a + * directive's element are destroyed. + * You should take this into consideration and make sure to always cancel the interval at the + * appropriate moment. See the example below for more details on how and when to do this. + *
    + * + * @param {function()} fn A function that should be called repeatedly. If no additional arguments + * are passed (see below), the function is called with the current iteration count. + * @param {number} delay Number of milliseconds between each function call. + * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat + * indefinitely. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @param {...*=} Pass additional parameters to the executed function. + * @returns {promise} A promise which will be notified on each iteration. It will resolve once all iterations of the interval complete. + * + * @example + * + * + * + * + *
    + *
    + *
    + * Current time is: + *
    + * Blood 1 : {{blood_1}} + * Blood 2 : {{blood_2}} + * + * + * + *
    + *
    + * + *
    + *
    + */ + function interval(fn, delay, count, invokeApply) { + var hasParams = arguments.length > 4, + args = hasParams ? sliceArgs(arguments, 4) : [], + setInterval = $window.setInterval, + clearInterval = $window.clearInterval, + iteration = 0, + skipApply = (isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise; + + count = isDefined(count) ? count : 0; + + promise.$$intervalId = setInterval(function tick() { + if (skipApply) { + $browser.defer(callback); + } else { + $rootScope.$evalAsync(callback); + } + deferred.notify(iteration++); + + if (count > 0 && iteration >= count) { + deferred.resolve(iteration); + clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + } + + if (!skipApply) $rootScope.$apply(); + + }, delay); + + intervals[promise.$$intervalId] = deferred; + + return promise; + + function callback() { + if (!hasParams) { + fn(iteration); + } else { + fn.apply(null, args); + } + } + } + + + /** + * @ngdoc method + * @name $interval#cancel + * + * @description + * Cancels a task associated with the `promise`. + * + * @param {Promise=} promise returned by the `$interval` function. + * @returns {boolean} Returns `true` if the task was successfully canceled. + */ + interval.cancel = function(promise) { + if (promise && promise.$$intervalId in intervals) { + // Interval cancels should not report as unhandled promise. + markQExceptionHandled(intervals[promise.$$intervalId].promise); + intervals[promise.$$intervalId].reject('canceled'); + $window.clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + return true; + } + return false; + }; + + return interval; + }]; +} + +/** + * @ngdoc service + * @name $jsonpCallbacks + * @requires $window + * @description + * This service handles the lifecycle of callbacks to handle JSONP requests. + * Override this service if you wish to customise where the callbacks are stored and + * how they vary compared to the requested url. + */ +var $jsonpCallbacksProvider = /** @this */ function() { + this.$get = function() { + var callbacks = angular.callbacks; + var callbackMap = {}; + + function createCallback(callbackId) { + var callback = function(data) { + callback.data = data; + callback.called = true; + }; + callback.id = callbackId; + return callback; + } + + return { + /** + * @ngdoc method + * @name $jsonpCallbacks#createCallback + * @param {string} url the url of the JSONP request + * @returns {string} the callback path to send to the server as part of the JSONP request + * @description + * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback + * to pass to the server, which will be used to call the callback with its payload in the JSONP response. + */ + createCallback: function(url) { + var callbackId = '_' + (callbacks.$$counter++).toString(36); + var callbackPath = 'angular.callbacks.' + callbackId; + var callback = createCallback(callbackId); + callbackMap[callbackPath] = callbacks[callbackId] = callback; + return callbackPath; + }, + /** + * @ngdoc method + * @name $jsonpCallbacks#wasCalled + * @param {string} callbackPath the path to the callback that was sent in the JSONP request + * @returns {boolean} whether the callback has been called, as a result of the JSONP response + * @description + * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the + * callback that was passed in the request. + */ + wasCalled: function(callbackPath) { + return callbackMap[callbackPath].called; + }, + /** + * @ngdoc method + * @name $jsonpCallbacks#getResponse + * @param {string} callbackPath the path to the callback that was sent in the JSONP request + * @returns {*} the data received from the response via the registered callback + * @description + * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback + * in the JSONP response. + */ + getResponse: function(callbackPath) { + return callbackMap[callbackPath].data; + }, + /** + * @ngdoc method + * @name $jsonpCallbacks#removeCallback + * @param {string} callbackPath the path to the callback that was sent in the JSONP request + * @description + * {@link $httpBackend} calls this method to remove the callback after the JSONP request has + * completed or timed-out. + */ + removeCallback: function(callbackPath) { + var callback = callbackMap[callbackPath]; + delete callbacks[callback.id]; + delete callbackMap[callbackPath]; + } + }; + }; +}; + +/** + * @ngdoc service + * @name $locale + * + * @description + * $locale service provides localization rules for various AngularJS components. As of right now the + * only public api is: + * + * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) + */ + +var PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/, + DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; +var $locationMinErr = minErr('$location'); + + +/** + * Encode path using encodeUriSegment, ignoring forward slashes + * + * @param {string} path Path to encode + * @returns {string} + */ +function encodePath(path) { + var segments = path.split('/'), + i = segments.length; + + while (i--) { + // decode forward slashes to prevent them from being double encoded + segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, '/')); + } + + return segments.join('/'); +} + +function decodePath(path, html5Mode) { + var segments = path.split('/'), + i = segments.length; + + while (i--) { + segments[i] = decodeURIComponent(segments[i]); + if (html5Mode) { + // encode forward slashes to prevent them from being mistaken for path separators + segments[i] = segments[i].replace(/\//g, '%2F'); + } + } + + return segments.join('/'); +} + +function parseAbsoluteUrl(absoluteUrl, locationObj) { + var parsedUrl = urlResolve(absoluteUrl); + + locationObj.$$protocol = parsedUrl.protocol; + locationObj.$$host = parsedUrl.hostname; + locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; +} + +var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/; +function parseAppUrl(url, locationObj, html5Mode) { + + if (DOUBLE_SLASH_REGEX.test(url)) { + throw $locationMinErr('badpath', 'Invalid url "{0}".', url); + } + + var prefixed = (url.charAt(0) !== '/'); + if (prefixed) { + url = '/' + url; + } + var match = urlResolve(url); + var path = prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname; + locationObj.$$path = decodePath(path, html5Mode); + locationObj.$$search = parseKeyValue(match.search); + locationObj.$$hash = decodeURIComponent(match.hash); + + // make sure path starts with '/'; + if (locationObj.$$path && locationObj.$$path.charAt(0) !== '/') { + locationObj.$$path = '/' + locationObj.$$path; + } +} + +function startsWith(str, search) { + return str.slice(0, search.length) === search; +} + +/** + * + * @param {string} base + * @param {string} url + * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with + * the expected string. + */ +function stripBaseUrl(base, url) { + if (startsWith(url, base)) { + return url.substr(base.length); + } +} + + +function stripHash(url) { + var index = url.indexOf('#'); + return index === -1 ? url : url.substr(0, index); +} + +function trimEmptyHash(url) { + return url.replace(/(#.+)|#$/, '$1'); +} + + +function stripFile(url) { + return url.substr(0, stripHash(url).lastIndexOf('/') + 1); +} + +/* return the server only (scheme://host:port) */ +function serverBase(url) { + return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); +} + + +/** + * LocationHtml5Url represents a URL + * This object is exposed as $location service when HTML5 mode is enabled and supported + * + * @constructor + * @param {string} appBase application base URL + * @param {string} appBaseNoFile application base URL stripped of any filename + * @param {string} basePrefix URL path prefix + */ +function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) { + this.$$html5 = true; + basePrefix = basePrefix || ''; + parseAbsoluteUrl(appBase, this); + + + /** + * Parse given HTML5 (regular) URL string into properties + * @param {string} url HTML5 URL + * @private + */ + this.$$parse = function(url) { + var pathUrl = stripBaseUrl(appBaseNoFile, url); + if (!isString(pathUrl)) { + throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, + appBaseNoFile); + } + + parseAppUrl(pathUrl, this, true); + + if (!this.$$path) { + this.$$path = '/'; + } + + this.$$compose(); + }; + + /** + * Compose url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' + + this.$$urlUpdatedByLocation = true; + }; + + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } + var appUrl, prevAppUrl; + var rewrittenUrl; + + + if (isDefined(appUrl = stripBaseUrl(appBase, url))) { + prevAppUrl = appUrl; + if (basePrefix && isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) { + rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl); + } else { + rewrittenUrl = appBase + prevAppUrl; + } + } else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) { + rewrittenUrl = appBaseNoFile + appUrl; + } else if (appBaseNoFile === url + '/') { + rewrittenUrl = appBaseNoFile; + } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); + } + return !!rewrittenUrl; + }; +} + + +/** + * LocationHashbangUrl represents URL + * This object is exposed as $location service when developer doesn't opt into html5 mode. + * It also serves as the base class for html5 mode fallback on legacy browsers. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} appBaseNoFile application base URL stripped of any filename + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) { + + parseAbsoluteUrl(appBase, this); + + + /** + * Parse given hashbang URL into properties + * @param {string} url Hashbang URL + * @private + */ + this.$$parse = function(url) { + var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url); + var withoutHashUrl; + + if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') { + + // The rest of the URL starts with a hash so we have + // got either a hashbang path or a plain hash fragment + withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl); + if (isUndefined(withoutHashUrl)) { + // There was no hashbang prefix so we just have a hash fragment + withoutHashUrl = withoutBaseUrl; + } + + } else { + // There was no hashbang path nor hash fragment: + // If we are in HTML5 mode we use what is left as the path; + // Otherwise we ignore what is left + if (this.$$html5) { + withoutHashUrl = withoutBaseUrl; + } else { + withoutHashUrl = ''; + if (isUndefined(withoutBaseUrl)) { + appBase = url; + /** @type {?} */ (this).replace(); + } + } + } + + parseAppUrl(withoutHashUrl, this, false); + + this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); + + this.$$compose(); + + /* + * In Windows, on an anchor node on documents loaded from + * the filesystem, the browser will return a pathname + * prefixed with the drive name ('/C:/path') when a + * pathname without a drive is set: + * * a.setAttribute('href', '/foo') + * * a.pathname === '/C:/foo' //true + * + * Inside of AngularJS, we're always using pathnames that + * do not include drive names for routing. + */ + function removeWindowsDriveName(path, url, base) { + /* + Matches paths for file protocol on windows, + such as /C:/foo/bar, and captures only /foo/bar. + */ + var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; + + var firstPathSegmentMatch; + + //Get the relative path from the input URL. + if (startsWith(url, base)) { + url = url.replace(base, ''); + } + + // The input URL intentionally contains a first path segment that ends with a colon. + if (windowsFilePathExp.exec(url)) { + return path; + } + + firstPathSegmentMatch = windowsFilePathExp.exec(path); + return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; + } + }; + + /** + * Compose hashbang URL and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); + + this.$$urlUpdatedByLocation = true; + }; + + this.$$parseLinkUrl = function(url, relHref) { + if (stripHash(appBase) === stripHash(url)) { + this.$$parse(url); + return true; + } + return false; + }; +} + + +/** + * LocationHashbangUrl represents URL + * This object is exposed as $location service when html5 history api is enabled but the browser + * does not support it. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} appBaseNoFile application base URL stripped of any filename + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) { + this.$$html5 = true; + LocationHashbangUrl.apply(this, arguments); + + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } + + var rewrittenUrl; + var appUrl; + + if (appBase === stripHash(url)) { + rewrittenUrl = url; + } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) { + rewrittenUrl = appBase + hashPrefix + appUrl; + } else if (appBaseNoFile === url + '/') { + rewrittenUrl = appBaseNoFile; + } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); + } + return !!rewrittenUrl; + }; + + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#' + this.$$absUrl = appBase + hashPrefix + this.$$url; + + this.$$urlUpdatedByLocation = true; + }; + +} + + +var locationPrototype = { + + /** + * Ensure absolute URL is initialized. + * @private + */ + $$absUrl:'', + + /** + * Are we in html5 mode? + * @private + */ + $$html5: false, + + /** + * Has any change been replacing? + * @private + */ + $$replace: false, + + /** + * @ngdoc method + * @name $location#absUrl + * + * @description + * This method is getter only. + * + * Return full URL representation with all segments encoded according to rules specified in + * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo + * var absUrl = $location.absUrl(); + * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" + * ``` + * + * @return {string} full URL + */ + absUrl: locationGetter('$$absUrl'), + + /** + * @ngdoc method + * @name $location#url + * + * @description + * This method is getter / setter. + * + * Return URL (e.g. `/path?a=b#hash`) when called without any parameter. + * + * Change path, search and hash, when called with parameter and return `$location`. + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo + * var url = $location.url(); + * // => "/some/path?foo=bar&baz=xoxo" + * ``` + * + * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`) + * @return {string} url + */ + url: function(url) { + if (isUndefined(url)) { + return this.$$url; + } + + var match = PATH_MATCH.exec(url); + if (match[1] || url === '') this.path(decodeURIComponent(match[1])); + if (match[2] || match[1] || url === '') this.search(match[3] || ''); + this.hash(match[5] || ''); + + return this; + }, + + /** + * @ngdoc method + * @name $location#protocol + * + * @description + * This method is getter only. + * + * Return protocol of current URL. + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo + * var protocol = $location.protocol(); + * // => "http" + * ``` + * + * @return {string} protocol of current URL + */ + protocol: locationGetter('$$protocol'), + + /** + * @ngdoc method + * @name $location#host + * + * @description + * This method is getter only. + * + * Return host of current URL. + * + * Note: compared to the non-AngularJS version `location.host` which returns `hostname:port`, this returns the `hostname` portion only. + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo + * var host = $location.host(); + * // => "example.com" + * + * // given URL http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo + * host = $location.host(); + * // => "example.com" + * host = location.host; + * // => "example.com:8080" + * ``` + * + * @return {string} host of current URL. + */ + host: locationGetter('$$host'), + + /** + * @ngdoc method + * @name $location#port + * + * @description + * This method is getter only. + * + * Return port of current URL. + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo + * var port = $location.port(); + * // => 80 + * ``` + * + * @return {Number} port + */ + port: locationGetter('$$port'), + + /** + * @ngdoc method + * @name $location#path + * + * @description + * This method is getter / setter. + * + * Return path of current URL when called without any parameter. + * + * Change path when called with parameter and return `$location`. + * + * Note: Path should always begin with forward slash (/), this method will add the forward slash + * if it is missing. + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo + * var path = $location.path(); + * // => "/some/path" + * ``` + * + * @param {(string|number)=} path New path + * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter + */ + path: locationGetterSetter('$$path', function(path) { + path = path !== null ? path.toString() : ''; + return path.charAt(0) === '/' ? path : '/' + path; + }), + + /** + * @ngdoc method + * @name $location#search + * + * @description + * This method is getter / setter. + * + * Return search part (as object) of current URL when called without any parameter. + * + * Change search part when called with parameter and return `$location`. + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo + * var searchObject = $location.search(); + * // => {foo: 'bar', baz: 'xoxo'} + * + * // set foo to 'yipee' + * $location.search('foo', 'yipee'); + * // $location.search() => {foo: 'yipee', baz: 'xoxo'} + * ``` + * + * @param {string|Object.|Object.>} search New search params - string or + * hash object. + * + * When called with a single argument the method acts as a setter, setting the `search` component + * of `$location` to the specified value. + * + * If the argument is a hash object containing an array of values, these values will be encoded + * as duplicate search parameters in the URL. + * + * @param {(string|Number|Array|boolean)=} paramValue If `search` is a string or number, then `paramValue` + * will override only a single search property. + * + * If `paramValue` is an array, it will override the property of the `search` component of + * `$location` specified via the first argument. + * + * If `paramValue` is `null`, the property specified via the first argument will be deleted. + * + * If `paramValue` is `true`, the property specified via the first argument will be added with no + * value nor trailing equal sign. + * + * @return {Object} If called with no arguments returns the parsed `search` object. If called with + * one or more arguments returns `$location` object itself. + */ + search: function(search, paramValue) { + switch (arguments.length) { + case 0: + return this.$$search; + case 1: + if (isString(search) || isNumber(search)) { + search = search.toString(); + this.$$search = parseKeyValue(search); + } else if (isObject(search)) { + search = copy(search, {}); + // remove object undefined or null properties + forEach(search, function(value, key) { + if (value == null) delete search[key]; + }); + + this.$$search = search; + } else { + throw $locationMinErr('isrcharg', + 'The first argument of the `$location#search()` call must be a string or an object.'); + } + break; + default: + if (isUndefined(paramValue) || paramValue === null) { + delete this.$$search[search]; + } else { + this.$$search[search] = paramValue; + } + } + + this.$$compose(); + return this; + }, + + /** + * @ngdoc method + * @name $location#hash + * + * @description + * This method is getter / setter. + * + * Returns the hash fragment when called without any parameters. + * + * Changes the hash fragment when called with a parameter and returns `$location`. + * + * + * ```js + * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue + * var hash = $location.hash(); + * // => "hashValue" + * ``` + * + * @param {(string|number)=} hash New hash fragment + * @return {string} hash + */ + hash: locationGetterSetter('$$hash', function(hash) { + return hash !== null ? hash.toString() : ''; + }), + + /** + * @ngdoc method + * @name $location#replace + * + * @description + * If called, all changes to $location during the current `$digest` will replace the current history + * record, instead of adding a new one. + */ + replace: function() { + this.$$replace = true; + return this; + } +}; + +forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) { + Location.prototype = Object.create(locationPrototype); + + /** + * @ngdoc method + * @name $location#state + * + * @description + * This method is getter / setter. + * + * Return the history state object when called without any parameter. + * + * Change the history state object when called with one parameter and return `$location`. + * The state object is later passed to `pushState` or `replaceState`. + * + * NOTE: This method is supported only in HTML5 mode and only in browsers supporting + * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support + * older browsers (like IE9 or Android < 4.0), don't use this method. + * + * @param {object=} state State object for pushState or replaceState + * @return {object} state + */ + Location.prototype.state = function(state) { + if (!arguments.length) { + return this.$$state; + } + + if (Location !== LocationHtml5Url || !this.$$html5) { + throw $locationMinErr('nostate', 'History API state support is available only ' + + 'in HTML5 mode and only in browsers supporting HTML5 History API'); + } + // The user might modify `stateObject` after invoking `$location.state(stateObject)` + // but we're changing the $$state reference to $browser.state() during the $digest + // so the modification window is narrow. + this.$$state = isUndefined(state) ? null : state; + this.$$urlUpdatedByLocation = true; + + return this; + }; +}); + + +function locationGetter(property) { + return /** @this */ function() { + return this[property]; + }; +} + + +function locationGetterSetter(property, preprocess) { + return /** @this */ function(value) { + if (isUndefined(value)) { + return this[property]; + } + + this[property] = preprocess(value); + this.$$compose(); + + return this; + }; +} + + +/** + * @ngdoc service + * @name $location + * + * @requires $rootElement + * + * @description + * The $location service parses the URL in the browser address bar (based on the + * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. + * + * **The $location service:** + * + * - Exposes the current URL in the browser address bar, so you can + * - Watch and observe the URL. + * - Change the URL. + * - Synchronizes the URL with the browser when the user + * - Changes the address bar. + * - Clicks the back or forward button (or clicks a History link). + * - Clicks on a link. + * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). + * + * For more information see {@link guide/$location Developer Guide: Using $location} + */ + +/** + * @ngdoc provider + * @name $locationProvider + * @this + * + * @description + * Use the `$locationProvider` to configure how the application deep linking paths are stored. + */ +function $LocationProvider() { + var hashPrefix = '!', + html5Mode = { + enabled: false, + requireBase: true, + rewriteLinks: true + }; + + /** + * @ngdoc method + * @name $locationProvider#hashPrefix + * @description + * The default value for the prefix is `'!'`. + * @param {string=} prefix Prefix for hash part (containing path and search) + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.hashPrefix = function(prefix) { + if (isDefined(prefix)) { + hashPrefix = prefix; + return this; + } else { + return hashPrefix; + } + }; + + /** + * @ngdoc method + * @name $locationProvider#html5Mode + * @description + * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value. + * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported + * properties: + * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to + * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not + * support `pushState`. + * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies + * whether or not a tag is required to be present. If `enabled` and `requireBase` are + * true, and a base tag is not present, an error will be thrown when `$location` is injected. + * See the {@link guide/$location $location guide for more information} + * - **rewriteLinks** - `{boolean|string}` - (default: `true`) When html5Mode is enabled, + * enables/disables URL rewriting for relative links. If set to a string, URL rewriting will + * only happen on links with an attribute that matches the given string. For example, if set + * to `'internal-link'`, then the URL will only be rewritten for `` links. + * Note that [attribute name normalization](guide/directive#normalization) does not apply + * here, so `'internalLink'` will **not** match `'internal-link'`. + * + * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter + */ + this.html5Mode = function(mode) { + if (isBoolean(mode)) { + html5Mode.enabled = mode; + return this; + } else if (isObject(mode)) { + + if (isBoolean(mode.enabled)) { + html5Mode.enabled = mode.enabled; + } + + if (isBoolean(mode.requireBase)) { + html5Mode.requireBase = mode.requireBase; + } + + if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) { + html5Mode.rewriteLinks = mode.rewriteLinks; + } + + return this; + } else { + return html5Mode; + } + }; + + /** + * @ngdoc event + * @name $location#$locationChangeStart + * @eventType broadcast on root scope + * @description + * Broadcasted before a URL will change. + * + * This change can be prevented by calling + * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more + * details about event object. Upon successful change + * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. + * + * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when + * the browser supports the HTML5 History API. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + * @param {string=} newState New history state object + * @param {string=} oldState History state object that was before it was changed. + */ + + /** + * @ngdoc event + * @name $location#$locationChangeSuccess + * @eventType broadcast on root scope + * @description + * Broadcasted after a URL was changed. + * + * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when + * the browser supports the HTML5 History API. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + * @param {string=} newState New history state object + * @param {string=} oldState History state object that was before it was changed. + */ + + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window', + function($rootScope, $browser, $sniffer, $rootElement, $window) { + var $location, + LocationMode, + baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' + initialUrl = $browser.url(), + appBase; + + if (html5Mode.enabled) { + if (!baseHref && html5Mode.requireBase) { + throw $locationMinErr('nobase', + '$location in HTML5 mode requires a tag to be present!'); + } + appBase = serverBase(initialUrl) + (baseHref || '/'); + LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; + } else { + appBase = stripHash(initialUrl); + LocationMode = LocationHashbangUrl; + } + var appBaseNoFile = stripFile(appBase); + + $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix); + $location.$$parseLinkUrl(initialUrl, initialUrl); + + $location.$$state = $browser.state(); + + var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; + + function setBrowserUrlWithFallback(url, replace, state) { + var oldUrl = $location.url(); + var oldState = $location.$$state; + try { + $browser.url(url, replace, state); + + // Make sure $location.state() returns referentially identical (not just deeply equal) + // state object; this makes possible quick checking if the state changed in the digest + // loop. Checking deep equality would be too expensive. + $location.$$state = $browser.state(); + } catch (e) { + // Restore old values if pushState fails + $location.url(oldUrl); + $location.$$state = oldState; + + throw e; + } + } + + $rootElement.on('click', function(event) { + var rewriteLinks = html5Mode.rewriteLinks; + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (nodeName_(elm[0]) !== 'a') { + // ignore rewriting if no A tag (reached root element, or no parent - removed from document) + if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; + } + + if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return; + + var absHref = elm.prop('href'); + // get the actual href attribute - see + // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx + var relHref = elm.attr('href') || elm.attr('xlink:href'); + + if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { + // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during + // an animation. + absHref = urlResolve(absHref.animVal).href; + } + + // Ignore when url is started with javascript: or mailto: + if (IGNORE_URI_REGEXP.test(absHref)) return; + + if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) { + if ($location.$$parseLinkUrl(absHref, relHref)) { + // We do a preventDefault for all urls that are part of the AngularJS application, + // in html5mode and also without, so that we are able to abort navigation without + // getting double entries in the location history. + event.preventDefault(); + // update location manually + if ($location.absUrl() !== $browser.url()) { + $rootScope.$apply(); + // hack to work around FF6 bug 684208 when scenario runner clicks on links + $window.angular['ff-684208-preventDefault'] = true; + } + } + } + }); + + + // rewrite hashbang url <> html5 url + if (trimEmptyHash($location.absUrl()) !== trimEmptyHash(initialUrl)) { + $browser.url($location.absUrl(), true); + } + + var initializing = true; + + // update $location when $browser url changes + $browser.onUrlChange(function(newUrl, newState) { + + if (!startsWith(newUrl, appBaseNoFile)) { + // If we are navigating outside of the app then force a reload + $window.location.href = newUrl; + return; + } + + $rootScope.$evalAsync(function() { + var oldUrl = $location.absUrl(); + var oldState = $location.$$state; + var defaultPrevented; + newUrl = trimEmptyHash(newUrl); + $location.$$parse(newUrl); + $location.$$state = newState; + + defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + newState, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + setBrowserUrlWithFallback(oldUrl, false, oldState); + } else { + initializing = false; + afterLocationChange(oldUrl, oldState); + } + }); + if (!$rootScope.$$phase) $rootScope.$digest(); + }); + + // update browser + $rootScope.$watch(function $locationWatch() { + if (initializing || $location.$$urlUpdatedByLocation) { + $location.$$urlUpdatedByLocation = false; + + var oldUrl = trimEmptyHash($browser.url()); + var newUrl = trimEmptyHash($location.absUrl()); + var oldState = $browser.state(); + var currentReplace = $location.$$replace; + var urlOrStateChanged = oldUrl !== newUrl || + ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); + + if (initializing || urlOrStateChanged) { + initializing = false; + + $rootScope.$evalAsync(function() { + var newUrl = $location.absUrl(); + var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + $location.$$state, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + } else { + if (urlOrStateChanged) { + setBrowserUrlWithFallback(newUrl, currentReplace, + oldState === $location.$$state ? null : $location.$$state); + } + afterLocationChange(oldUrl, oldState); + } + }); + } + } + + $location.$$replace = false; + + // we don't need to return anything because $evalAsync will make the digest loop dirty when + // there is a change + }); + + return $location; + + function afterLocationChange(oldUrl, oldState) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl, + $location.$$state, oldState); + } +}]; +} + +/** + * @ngdoc service + * @name $log + * @requires $window + * + * @description + * Simple service for logging. Default implementation safely writes the message + * into the browser's console (if present). + * + * The main purpose of this service is to simplify debugging and troubleshooting. + * + * To reveal the location of the calls to `$log` in the JavaScript console, + * you can "blackbox" the AngularJS source in your browser: + * + * [Mozilla description of blackboxing](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Black_box_a_source). + * [Chrome description of blackboxing](https://developer.chrome.com/devtools/docs/blackboxing). + * + * Note: Not all browsers support blackboxing. + * + * The default is to log `debug` messages. You can use + * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. + * + * @example + + + angular.module('logExample', []) + .controller('LogController', ['$scope', '$log', function($scope, $log) { + $scope.$log = $log; + $scope.message = 'Hello World!'; + }]); + + +
    +

    Reload this page with open console, enter text and hit the log button...

    + + + + + + +
    +
    +
    + */ + +/** + * @ngdoc provider + * @name $logProvider + * @this + * + * @description + * Use the `$logProvider` to configure how the application logs messages + */ +function $LogProvider() { + var debug = true, + self = this; + + /** + * @ngdoc method + * @name $logProvider#debugEnabled + * @description + * @param {boolean=} flag enable or disable debug level messages + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.debugEnabled = function(flag) { + if (isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } + }; + + this.$get = ['$window', function($window) { + // Support: IE 9-11, Edge 12-14+ + // IE/Edge display errors in such a way that it requires the user to click in 4 places + // to see the stack trace. There is no way to feature-detect it so there's a chance + // of the user agent sniffing to go wrong but since it's only about logging, this shouldn't + // break apps. Other browsers display errors in a sensible way and some of them map stack + // traces along source maps if available so it makes sense to let browsers display it + // as they want. + var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent); + + return { + /** + * @ngdoc method + * @name $log#log + * + * @description + * Write a log message + */ + log: consoleLog('log'), + + /** + * @ngdoc method + * @name $log#info + * + * @description + * Write an information message + */ + info: consoleLog('info'), + + /** + * @ngdoc method + * @name $log#warn + * + * @description + * Write a warning message + */ + warn: consoleLog('warn'), + + /** + * @ngdoc method + * @name $log#error + * + * @description + * Write an error message + */ + error: consoleLog('error'), + + /** + * @ngdoc method + * @name $log#debug + * + * @description + * Write a debug message + */ + debug: (function() { + var fn = consoleLog('debug'); + + return function() { + if (debug) { + fn.apply(self, arguments); + } + }; + })() + }; + + function formatError(arg) { + if (isError(arg)) { + if (arg.stack && formatStackTrace) { + arg = (arg.message && arg.stack.indexOf(arg.message) === -1) + ? 'Error: ' + arg.message + '\n' + arg.stack + : arg.stack; + } else if (arg.sourceURL) { + arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; + } + } + return arg; + } + + function consoleLog(type) { + var console = $window.console || {}, + logFn = console[type] || console.log || noop; + + return function() { + var args = []; + forEach(arguments, function(arg) { + args.push(formatError(arg)); + }); + // Support: IE 9 only + // console methods don't inherit from Function.prototype in IE 9 so we can't + // call `logFn.apply(console, args)` directly. + return Function.prototype.apply.call(logFn, console, args); + }; + } + }]; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +var $parseMinErr = minErr('$parse'); + +var objectValueOf = {}.constructor.prototype.valueOf; + +// Sandboxing AngularJS Expressions +// ------------------------------ +// AngularJS expressions are no longer sandboxed. So it is now even easier to access arbitrary JS code by +// various means such as obtaining a reference to native JS functions like the Function constructor. +// +// As an example, consider the following AngularJS expression: +// +// {}.toString.constructor('alert("evil JS code")') +// +// It is important to realize that if you create an expression from a string that contains user provided +// content then it is possible that your application contains a security vulnerability to an XSS style attack. +// +// See https://docs.angularjs.org/guide/security + + +function getStringValue(name) { + // Property names must be strings. This means that non-string objects cannot be used + // as keys in an object. Any non-string object, including a number, is typecasted + // into a string via the toString method. + // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names + // + // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it + // to a string. It's not always possible. If `name` is an object and its `toString` method is + // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown: + // + // TypeError: Cannot convert object to primitive value + // + // For performance reasons, we don't catch this error here and allow it to propagate up the call + // stack. Note that you'll get the same error in JavaScript if you try to access a property using + // such a 'broken' object as a key. + return name + ''; +} + + +var OPERATORS = createMap(); +forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; }); +var ESCAPE = {'n':'\n', 'f':'\f', 'r':'\r', 't':'\t', 'v':'\v', '\'':'\'', '"':'"'}; + + +///////////////////////////////////////// + + +/** + * @constructor + */ +var Lexer = function Lexer(options) { + this.options = options; +}; + +Lexer.prototype = { + constructor: Lexer, + + lex: function(text) { + this.text = text; + this.index = 0; + this.tokens = []; + + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (ch === '"' || ch === '\'') { + this.readString(ch); + } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { + this.readNumber(); + } else if (this.isIdentifierStart(this.peekMultichar())) { + this.readIdent(); + } else if (this.is(ch, '(){}[].,;:?')) { + this.tokens.push({index: this.index, text: ch}); + this.index++; + } else if (this.isWhitespace(ch)) { + this.index++; + } else { + var ch2 = ch + this.peek(); + var ch3 = ch2 + this.peek(2); + var op1 = OPERATORS[ch]; + var op2 = OPERATORS[ch2]; + var op3 = OPERATORS[ch3]; + if (op1 || op2 || op3) { + var token = op3 ? ch3 : (op2 ? ch2 : ch); + this.tokens.push({index: this.index, text: token, operator: true}); + this.index += token.length; + } else { + this.throwError('Unexpected next character ', this.index, this.index + 1); + } + } + } + return this.tokens; + }, + + is: function(ch, chars) { + return chars.indexOf(ch) !== -1; + }, + + peek: function(i) { + var num = i || 1; + return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; + }, + + isNumber: function(ch) { + return ('0' <= ch && ch <= '9') && typeof ch === 'string'; + }, + + isWhitespace: function(ch) { + // IE treats non-breaking space as \u00A0 + return (ch === ' ' || ch === '\r' || ch === '\t' || + ch === '\n' || ch === '\v' || ch === '\u00A0'); + }, + + isIdentifierStart: function(ch) { + return this.options.isIdentifierStart ? + this.options.isIdentifierStart(ch, this.codePointAt(ch)) : + this.isValidIdentifierStart(ch); + }, + + isValidIdentifierStart: function(ch) { + return ('a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' === ch || ch === '$'); + }, + + isIdentifierContinue: function(ch) { + return this.options.isIdentifierContinue ? + this.options.isIdentifierContinue(ch, this.codePointAt(ch)) : + this.isValidIdentifierContinue(ch); + }, + + isValidIdentifierContinue: function(ch, cp) { + return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch); + }, + + codePointAt: function(ch) { + if (ch.length === 1) return ch.charCodeAt(0); + // eslint-disable-next-line no-bitwise + return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00; + }, + + peekMultichar: function() { + var ch = this.text.charAt(this.index); + var peek = this.peek(); + if (!peek) { + return ch; + } + var cp1 = ch.charCodeAt(0); + var cp2 = peek.charCodeAt(0); + if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) { + return ch + peek; + } + return ch; + }, + + isExpOperator: function(ch) { + return (ch === '-' || ch === '+' || this.isNumber(ch)); + }, + + throwError: function(error, start, end) { + end = end || this.index; + var colStr = (isDefined(start) + ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' + : ' ' + end); + throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', + error, colStr, this.text); + }, + + readNumber: function() { + var number = ''; + var start = this.index; + while (this.index < this.text.length) { + var ch = lowercase(this.text.charAt(this.index)); + if (ch === '.' || this.isNumber(ch)) { + number += ch; + } else { + var peekCh = this.peek(); + if (ch === 'e' && this.isExpOperator(peekCh)) { + number += ch; + } else if (this.isExpOperator(ch) && + peekCh && this.isNumber(peekCh) && + number.charAt(number.length - 1) === 'e') { + number += ch; + } else if (this.isExpOperator(ch) && + (!peekCh || !this.isNumber(peekCh)) && + number.charAt(number.length - 1) === 'e') { + this.throwError('Invalid exponent'); + } else { + break; + } + } + this.index++; + } + this.tokens.push({ + index: start, + text: number, + constant: true, + value: Number(number) + }); + }, + + readIdent: function() { + var start = this.index; + this.index += this.peekMultichar().length; + while (this.index < this.text.length) { + var ch = this.peekMultichar(); + if (!this.isIdentifierContinue(ch)) { + break; + } + this.index += ch.length; + } + this.tokens.push({ + index: start, + text: this.text.slice(start, this.index), + identifier: true + }); + }, + + readString: function(quote) { + var start = this.index; + this.index++; + var string = ''; + var rawString = quote; + var escape = false; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + rawString += ch; + if (escape) { + if (ch === 'u') { + var hex = this.text.substring(this.index + 1, this.index + 5); + if (!hex.match(/[\da-f]{4}/i)) { + this.throwError('Invalid unicode escape [\\u' + hex + ']'); + } + this.index += 4; + string += String.fromCharCode(parseInt(hex, 16)); + } else { + var rep = ESCAPE[ch]; + string = string + (rep || ch); + } + escape = false; + } else if (ch === '\\') { + escape = true; + } else if (ch === quote) { + this.index++; + this.tokens.push({ + index: start, + text: rawString, + constant: true, + value: string + }); + return; + } else { + string += ch; + } + this.index++; + } + this.throwError('Unterminated quote', start); + } +}; + +var AST = function AST(lexer, options) { + this.lexer = lexer; + this.options = options; +}; + +AST.Program = 'Program'; +AST.ExpressionStatement = 'ExpressionStatement'; +AST.AssignmentExpression = 'AssignmentExpression'; +AST.ConditionalExpression = 'ConditionalExpression'; +AST.LogicalExpression = 'LogicalExpression'; +AST.BinaryExpression = 'BinaryExpression'; +AST.UnaryExpression = 'UnaryExpression'; +AST.CallExpression = 'CallExpression'; +AST.MemberExpression = 'MemberExpression'; +AST.Identifier = 'Identifier'; +AST.Literal = 'Literal'; +AST.ArrayExpression = 'ArrayExpression'; +AST.Property = 'Property'; +AST.ObjectExpression = 'ObjectExpression'; +AST.ThisExpression = 'ThisExpression'; +AST.LocalsExpression = 'LocalsExpression'; + +// Internal use only +AST.NGValueParameter = 'NGValueParameter'; + +AST.prototype = { + ast: function(text) { + this.text = text; + this.tokens = this.lexer.lex(text); + + var value = this.program(); + + if (this.tokens.length !== 0) { + this.throwError('is an unexpected token', this.tokens[0]); + } + + return value; + }, + + program: function() { + var body = []; + while (true) { + if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) + body.push(this.expressionStatement()); + if (!this.expect(';')) { + return { type: AST.Program, body: body}; + } + } + }, + + expressionStatement: function() { + return { type: AST.ExpressionStatement, expression: this.filterChain() }; + }, + + filterChain: function() { + var left = this.expression(); + while (this.expect('|')) { + left = this.filter(left); + } + return left; + }, + + expression: function() { + return this.assignment(); + }, + + assignment: function() { + var result = this.ternary(); + if (this.expect('=')) { + if (!isAssignable(result)) { + throw $parseMinErr('lval', 'Trying to assign a value to a non l-value'); + } + + result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='}; + } + return result; + }, + + ternary: function() { + var test = this.logicalOR(); + var alternate; + var consequent; + if (this.expect('?')) { + alternate = this.expression(); + if (this.consume(':')) { + consequent = this.expression(); + return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent}; + } + } + return test; + }, + + logicalOR: function() { + var left = this.logicalAND(); + while (this.expect('||')) { + left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() }; + } + return left; + }, + + logicalAND: function() { + var left = this.equality(); + while (this.expect('&&')) { + left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()}; + } + return left; + }, + + equality: function() { + var left = this.relational(); + var token; + while ((token = this.expect('==','!=','===','!=='))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() }; + } + return left; + }, + + relational: function() { + var left = this.additive(); + var token; + while ((token = this.expect('<', '>', '<=', '>='))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() }; + } + return left; + }, + + additive: function() { + var left = this.multiplicative(); + var token; + while ((token = this.expect('+','-'))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() }; + } + return left; + }, + + multiplicative: function() { + var left = this.unary(); + var token; + while ((token = this.expect('*','/','%'))) { + left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() }; + } + return left; + }, + + unary: function() { + var token; + if ((token = this.expect('+', '-', '!'))) { + return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() }; + } else { + return this.primary(); + } + }, + + primary: function() { + var primary; + if (this.expect('(')) { + primary = this.filterChain(); + this.consume(')'); + } else if (this.expect('[')) { + primary = this.arrayDeclaration(); + } else if (this.expect('{')) { + primary = this.object(); + } else if (this.selfReferential.hasOwnProperty(this.peek().text)) { + primary = copy(this.selfReferential[this.consume().text]); + } else if (this.options.literals.hasOwnProperty(this.peek().text)) { + primary = { type: AST.Literal, value: this.options.literals[this.consume().text]}; + } else if (this.peek().identifier) { + primary = this.identifier(); + } else if (this.peek().constant) { + primary = this.constant(); + } else { + this.throwError('not a primary expression', this.peek()); + } + + var next; + while ((next = this.expect('(', '[', '.'))) { + if (next.text === '(') { + primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() }; + this.consume(')'); + } else if (next.text === '[') { + primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true }; + this.consume(']'); + } else if (next.text === '.') { + primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false }; + } else { + this.throwError('IMPOSSIBLE'); + } + } + return primary; + }, + + filter: function(baseExpression) { + var args = [baseExpression]; + var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true}; + + while (this.expect(':')) { + args.push(this.expression()); + } + + return result; + }, + + parseArguments: function() { + var args = []; + if (this.peekToken().text !== ')') { + do { + args.push(this.filterChain()); + } while (this.expect(',')); + } + return args; + }, + + identifier: function() { + var token = this.consume(); + if (!token.identifier) { + this.throwError('is not a valid identifier', token); + } + return { type: AST.Identifier, name: token.text }; + }, + + constant: function() { + // TODO check that it is a constant + return { type: AST.Literal, value: this.consume().value }; + }, + + arrayDeclaration: function() { + var elements = []; + if (this.peekToken().text !== ']') { + do { + if (this.peek(']')) { + // Support trailing commas per ES5.1. + break; + } + elements.push(this.expression()); + } while (this.expect(',')); + } + this.consume(']'); + + return { type: AST.ArrayExpression, elements: elements }; + }, + + object: function() { + var properties = [], property; + if (this.peekToken().text !== '}') { + do { + if (this.peek('}')) { + // Support trailing commas per ES5.1. + break; + } + property = {type: AST.Property, kind: 'init'}; + if (this.peek().constant) { + property.key = this.constant(); + property.computed = false; + this.consume(':'); + property.value = this.expression(); + } else if (this.peek().identifier) { + property.key = this.identifier(); + property.computed = false; + if (this.peek(':')) { + this.consume(':'); + property.value = this.expression(); + } else { + property.value = property.key; + } + } else if (this.peek('[')) { + this.consume('['); + property.key = this.expression(); + this.consume(']'); + property.computed = true; + this.consume(':'); + property.value = this.expression(); + } else { + this.throwError('invalid key', this.peek()); + } + properties.push(property); + } while (this.expect(',')); + } + this.consume('}'); + + return {type: AST.ObjectExpression, properties: properties }; + }, + + throwError: function(msg, token) { + throw $parseMinErr('syntax', + 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', + token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); + }, + + consume: function(e1) { + if (this.tokens.length === 0) { + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + } + + var token = this.expect(e1); + if (!token) { + this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); + } + return token; + }, + + peekToken: function() { + if (this.tokens.length === 0) { + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + } + return this.tokens[0]; + }, + + peek: function(e1, e2, e3, e4) { + return this.peekAhead(0, e1, e2, e3, e4); + }, + + peekAhead: function(i, e1, e2, e3, e4) { + if (this.tokens.length > i) { + var token = this.tokens[i]; + var t = token.text; + if (t === e1 || t === e2 || t === e3 || t === e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + }, + + expect: function(e1, e2, e3, e4) { + var token = this.peek(e1, e2, e3, e4); + if (token) { + this.tokens.shift(); + return token; + } + return false; + }, + + selfReferential: { + 'this': {type: AST.ThisExpression }, + '$locals': {type: AST.LocalsExpression } + } +}; + +function ifDefined(v, d) { + return typeof v !== 'undefined' ? v : d; +} + +function plusFn(l, r) { + if (typeof l === 'undefined') return r; + if (typeof r === 'undefined') return l; + return l + r; +} + +function isStateless($filter, filterName) { + var fn = $filter(filterName); + return !fn.$stateful; +} + +var PURITY_ABSOLUTE = 1; +var PURITY_RELATIVE = 2; + +// Detect nodes which could depend on non-shallow state of objects +function isPure(node, parentIsPure) { + switch (node.type) { + // Computed members might invoke a stateful toString() + case AST.MemberExpression: + if (node.computed) { + return false; + } + break; + + // Unary always convert to primative + case AST.UnaryExpression: + return PURITY_ABSOLUTE; + + // The binary + operator can invoke a stateful toString(). + case AST.BinaryExpression: + return node.operator !== '+' ? PURITY_ABSOLUTE : false; + + // Functions / filters probably read state from within objects + case AST.CallExpression: + return false; + } + + return (undefined === parentIsPure) ? PURITY_RELATIVE : parentIsPure; +} + +function findConstantAndWatchExpressions(ast, $filter, parentIsPure) { + var allConstants; + var argsToWatch; + var isStatelessFilter; + + var astIsPure = ast.isPure = isPure(ast, parentIsPure); + + switch (ast.type) { + case AST.Program: + allConstants = true; + forEach(ast.body, function(expr) { + findConstantAndWatchExpressions(expr.expression, $filter, astIsPure); + allConstants = allConstants && expr.expression.constant; + }); + ast.constant = allConstants; + break; + case AST.Literal: + ast.constant = true; + ast.toWatch = []; + break; + case AST.UnaryExpression: + findConstantAndWatchExpressions(ast.argument, $filter, astIsPure); + ast.constant = ast.argument.constant; + ast.toWatch = ast.argument.toWatch; + break; + case AST.BinaryExpression: + findConstantAndWatchExpressions(ast.left, $filter, astIsPure); + findConstantAndWatchExpressions(ast.right, $filter, astIsPure); + ast.constant = ast.left.constant && ast.right.constant; + ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch); + break; + case AST.LogicalExpression: + findConstantAndWatchExpressions(ast.left, $filter, astIsPure); + findConstantAndWatchExpressions(ast.right, $filter, astIsPure); + ast.constant = ast.left.constant && ast.right.constant; + ast.toWatch = ast.constant ? [] : [ast]; + break; + case AST.ConditionalExpression: + findConstantAndWatchExpressions(ast.test, $filter, astIsPure); + findConstantAndWatchExpressions(ast.alternate, $filter, astIsPure); + findConstantAndWatchExpressions(ast.consequent, $filter, astIsPure); + ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant; + ast.toWatch = ast.constant ? [] : [ast]; + break; + case AST.Identifier: + ast.constant = false; + ast.toWatch = [ast]; + break; + case AST.MemberExpression: + findConstantAndWatchExpressions(ast.object, $filter, astIsPure); + if (ast.computed) { + findConstantAndWatchExpressions(ast.property, $filter, astIsPure); + } + ast.constant = ast.object.constant && (!ast.computed || ast.property.constant); + ast.toWatch = ast.constant ? [] : [ast]; + break; + case AST.CallExpression: + isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false; + allConstants = isStatelessFilter; + argsToWatch = []; + forEach(ast.arguments, function(expr) { + findConstantAndWatchExpressions(expr, $filter, astIsPure); + allConstants = allConstants && expr.constant; + argsToWatch.push.apply(argsToWatch, expr.toWatch); + }); + ast.constant = allConstants; + ast.toWatch = isStatelessFilter ? argsToWatch : [ast]; + break; + case AST.AssignmentExpression: + findConstantAndWatchExpressions(ast.left, $filter, astIsPure); + findConstantAndWatchExpressions(ast.right, $filter, astIsPure); + ast.constant = ast.left.constant && ast.right.constant; + ast.toWatch = [ast]; + break; + case AST.ArrayExpression: + allConstants = true; + argsToWatch = []; + forEach(ast.elements, function(expr) { + findConstantAndWatchExpressions(expr, $filter, astIsPure); + allConstants = allConstants && expr.constant; + argsToWatch.push.apply(argsToWatch, expr.toWatch); + }); + ast.constant = allConstants; + ast.toWatch = argsToWatch; + break; + case AST.ObjectExpression: + allConstants = true; + argsToWatch = []; + forEach(ast.properties, function(property) { + findConstantAndWatchExpressions(property.value, $filter, astIsPure); + allConstants = allConstants && property.value.constant; + argsToWatch.push.apply(argsToWatch, property.value.toWatch); + if (property.computed) { + //`{[key]: value}` implicitly does `key.toString()` which may be non-pure + findConstantAndWatchExpressions(property.key, $filter, /*parentIsPure=*/false); + allConstants = allConstants && property.key.constant; + argsToWatch.push.apply(argsToWatch, property.key.toWatch); + } + }); + ast.constant = allConstants; + ast.toWatch = argsToWatch; + break; + case AST.ThisExpression: + ast.constant = false; + ast.toWatch = []; + break; + case AST.LocalsExpression: + ast.constant = false; + ast.toWatch = []; + break; + } +} + +function getInputs(body) { + if (body.length !== 1) return; + var lastExpression = body[0].expression; + var candidate = lastExpression.toWatch; + if (candidate.length !== 1) return candidate; + return candidate[0] !== lastExpression ? candidate : undefined; +} + +function isAssignable(ast) { + return ast.type === AST.Identifier || ast.type === AST.MemberExpression; +} + +function assignableAST(ast) { + if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) { + return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='}; + } +} + +function isLiteral(ast) { + return ast.body.length === 0 || + ast.body.length === 1 && ( + ast.body[0].expression.type === AST.Literal || + ast.body[0].expression.type === AST.ArrayExpression || + ast.body[0].expression.type === AST.ObjectExpression); +} + +function isConstant(ast) { + return ast.constant; +} + +function ASTCompiler($filter) { + this.$filter = $filter; +} + +ASTCompiler.prototype = { + compile: function(ast) { + var self = this; + this.state = { + nextId: 0, + filters: {}, + fn: {vars: [], body: [], own: {}}, + assign: {vars: [], body: [], own: {}}, + inputs: [] + }; + findConstantAndWatchExpressions(ast, self.$filter); + var extra = ''; + var assignable; + this.stage = 'assign'; + if ((assignable = assignableAST(ast))) { + this.state.computing = 'assign'; + var result = this.nextId(); + this.recurse(assignable, result); + this.return_(result); + extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l'); + } + var toWatch = getInputs(ast.body); + self.stage = 'inputs'; + forEach(toWatch, function(watch, key) { + var fnKey = 'fn' + key; + self.state[fnKey] = {vars: [], body: [], own: {}}; + self.state.computing = fnKey; + var intoId = self.nextId(); + self.recurse(watch, intoId); + self.return_(intoId); + self.state.inputs.push({name: fnKey, isPure: watch.isPure}); + watch.watchId = key; + }); + this.state.computing = 'fn'; + this.stage = 'main'; + this.recurse(ast); + var fnString = + // The build and minification steps remove the string "use strict" from the code, but this is done using a regex. + // This is a workaround for this until we do a better job at only removing the prefix only when we should. + '"' + this.USE + ' ' + this.STRICT + '";\n' + + this.filterPrefix() + + 'var fn=' + this.generateFunction('fn', 's,l,a,i') + + extra + + this.watchFns() + + 'return fn;'; + + // eslint-disable-next-line no-new-func + var fn = (new Function('$filter', + 'getStringValue', + 'ifDefined', + 'plus', + fnString))( + this.$filter, + getStringValue, + ifDefined, + plusFn); + this.state = this.stage = undefined; + return fn; + }, + + USE: 'use', + + STRICT: 'strict', + + watchFns: function() { + var result = []; + var inputs = this.state.inputs; + var self = this; + forEach(inputs, function(input) { + result.push('var ' + input.name + '=' + self.generateFunction(input.name, 's')); + if (input.isPure) { + result.push(input.name, '.isPure=' + JSON.stringify(input.isPure) + ';'); + } + }); + if (inputs.length) { + result.push('fn.inputs=[' + inputs.map(function(i) { return i.name; }).join(',') + '];'); + } + return result.join(''); + }, + + generateFunction: function(name, params) { + return 'function(' + params + '){' + + this.varsPrefix(name) + + this.body(name) + + '};'; + }, + + filterPrefix: function() { + var parts = []; + var self = this; + forEach(this.state.filters, function(id, filter) { + parts.push(id + '=$filter(' + self.escape(filter) + ')'); + }); + if (parts.length) return 'var ' + parts.join(',') + ';'; + return ''; + }, + + varsPrefix: function(section) { + return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : ''; + }, + + body: function(section) { + return this.state[section].body.join(''); + }, + + recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { + var left, right, self = this, args, expression, computed; + recursionFn = recursionFn || noop; + if (!skipWatchIdCheck && isDefined(ast.watchId)) { + intoId = intoId || this.nextId(); + this.if_('i', + this.lazyAssign(intoId, this.computedMember('i', ast.watchId)), + this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true) + ); + return; + } + switch (ast.type) { + case AST.Program: + forEach(ast.body, function(expression, pos) { + self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; }); + if (pos !== ast.body.length - 1) { + self.current().body.push(right, ';'); + } else { + self.return_(right); + } + }); + break; + case AST.Literal: + expression = this.escape(ast.value); + this.assign(intoId, expression); + recursionFn(intoId || expression); + break; + case AST.UnaryExpression: + this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; }); + expression = ast.operator + '(' + this.ifDefined(right, 0) + ')'; + this.assign(intoId, expression); + recursionFn(expression); + break; + case AST.BinaryExpression: + this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; }); + this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; }); + if (ast.operator === '+') { + expression = this.plus(left, right); + } else if (ast.operator === '-') { + expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0); + } else { + expression = '(' + left + ')' + ast.operator + '(' + right + ')'; + } + this.assign(intoId, expression); + recursionFn(expression); + break; + case AST.LogicalExpression: + intoId = intoId || this.nextId(); + self.recurse(ast.left, intoId); + self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId)); + recursionFn(intoId); + break; + case AST.ConditionalExpression: + intoId = intoId || this.nextId(); + self.recurse(ast.test, intoId); + self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId)); + recursionFn(intoId); + break; + case AST.Identifier: + intoId = intoId || this.nextId(); + if (nameId) { + nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s'); + nameId.computed = false; + nameId.name = ast.name; + } + self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)), + function() { + self.if_(self.stage === 'inputs' || 's', function() { + if (create && create !== 1) { + self.if_( + self.isNull(self.nonComputedMember('s', ast.name)), + self.lazyAssign(self.nonComputedMember('s', ast.name), '{}')); + } + self.assign(intoId, self.nonComputedMember('s', ast.name)); + }); + }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name)) + ); + recursionFn(intoId); + break; + case AST.MemberExpression: + left = nameId && (nameId.context = this.nextId()) || this.nextId(); + intoId = intoId || this.nextId(); + self.recurse(ast.object, left, undefined, function() { + self.if_(self.notNull(left), function() { + if (ast.computed) { + right = self.nextId(); + self.recurse(ast.property, right); + self.getStringValue(right); + if (create && create !== 1) { + self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}')); + } + expression = self.computedMember(left, right); + self.assign(intoId, expression); + if (nameId) { + nameId.computed = true; + nameId.name = right; + } + } else { + if (create && create !== 1) { + self.if_(self.isNull(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}')); + } + expression = self.nonComputedMember(left, ast.property.name); + self.assign(intoId, expression); + if (nameId) { + nameId.computed = false; + nameId.name = ast.property.name; + } + } + }, function() { + self.assign(intoId, 'undefined'); + }); + recursionFn(intoId); + }, !!create); + break; + case AST.CallExpression: + intoId = intoId || this.nextId(); + if (ast.filter) { + right = self.filter(ast.callee.name); + args = []; + forEach(ast.arguments, function(expr) { + var argument = self.nextId(); + self.recurse(expr, argument); + args.push(argument); + }); + expression = right + '(' + args.join(',') + ')'; + self.assign(intoId, expression); + recursionFn(intoId); + } else { + right = self.nextId(); + left = {}; + args = []; + self.recurse(ast.callee, right, left, function() { + self.if_(self.notNull(right), function() { + forEach(ast.arguments, function(expr) { + self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) { + args.push(argument); + }); + }); + if (left.name) { + expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')'; + } else { + expression = right + '(' + args.join(',') + ')'; + } + self.assign(intoId, expression); + }, function() { + self.assign(intoId, 'undefined'); + }); + recursionFn(intoId); + }); + } + break; + case AST.AssignmentExpression: + right = this.nextId(); + left = {}; + this.recurse(ast.left, undefined, left, function() { + self.if_(self.notNull(left.context), function() { + self.recurse(ast.right, right); + expression = self.member(left.context, left.name, left.computed) + ast.operator + right; + self.assign(intoId, expression); + recursionFn(intoId || expression); + }); + }, 1); + break; + case AST.ArrayExpression: + args = []; + forEach(ast.elements, function(expr) { + self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) { + args.push(argument); + }); + }); + expression = '[' + args.join(',') + ']'; + this.assign(intoId, expression); + recursionFn(intoId || expression); + break; + case AST.ObjectExpression: + args = []; + computed = false; + forEach(ast.properties, function(property) { + if (property.computed) { + computed = true; + } + }); + if (computed) { + intoId = intoId || this.nextId(); + this.assign(intoId, '{}'); + forEach(ast.properties, function(property) { + if (property.computed) { + left = self.nextId(); + self.recurse(property.key, left); + } else { + left = property.key.type === AST.Identifier ? + property.key.name : + ('' + property.key.value); + } + right = self.nextId(); + self.recurse(property.value, right); + self.assign(self.member(intoId, left, property.computed), right); + }); + } else { + forEach(ast.properties, function(property) { + self.recurse(property.value, ast.constant ? undefined : self.nextId(), undefined, function(expr) { + args.push(self.escape( + property.key.type === AST.Identifier ? property.key.name : + ('' + property.key.value)) + + ':' + expr); + }); + }); + expression = '{' + args.join(',') + '}'; + this.assign(intoId, expression); + } + recursionFn(intoId || expression); + break; + case AST.ThisExpression: + this.assign(intoId, 's'); + recursionFn(intoId || 's'); + break; + case AST.LocalsExpression: + this.assign(intoId, 'l'); + recursionFn(intoId || 'l'); + break; + case AST.NGValueParameter: + this.assign(intoId, 'v'); + recursionFn(intoId || 'v'); + break; + } + }, + + getHasOwnProperty: function(element, property) { + var key = element + '.' + property; + var own = this.current().own; + if (!own.hasOwnProperty(key)) { + own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')'); + } + return own[key]; + }, + + assign: function(id, value) { + if (!id) return; + this.current().body.push(id, '=', value, ';'); + return id; + }, + + filter: function(filterName) { + if (!this.state.filters.hasOwnProperty(filterName)) { + this.state.filters[filterName] = this.nextId(true); + } + return this.state.filters[filterName]; + }, + + ifDefined: function(id, defaultValue) { + return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')'; + }, + + plus: function(left, right) { + return 'plus(' + left + ',' + right + ')'; + }, + + return_: function(id) { + this.current().body.push('return ', id, ';'); + }, + + if_: function(test, alternate, consequent) { + if (test === true) { + alternate(); + } else { + var body = this.current().body; + body.push('if(', test, '){'); + alternate(); + body.push('}'); + if (consequent) { + body.push('else{'); + consequent(); + body.push('}'); + } + } + }, + + not: function(expression) { + return '!(' + expression + ')'; + }, + + isNull: function(expression) { + return expression + '==null'; + }, + + notNull: function(expression) { + return expression + '!=null'; + }, + + nonComputedMember: function(left, right) { + var SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/; + var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g; + if (SAFE_IDENTIFIER.test(right)) { + return left + '.' + right; + } else { + return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]'; + } + }, + + computedMember: function(left, right) { + return left + '[' + right + ']'; + }, + + member: function(left, right, computed) { + if (computed) return this.computedMember(left, right); + return this.nonComputedMember(left, right); + }, + + getStringValue: function(item) { + this.assign(item, 'getStringValue(' + item + ')'); + }, + + lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) { + var self = this; + return function() { + self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck); + }; + }, + + lazyAssign: function(id, value) { + var self = this; + return function() { + self.assign(id, value); + }; + }, + + stringEscapeRegex: /[^ a-zA-Z0-9]/g, + + stringEscapeFn: function(c) { + return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); + }, + + escape: function(value) { + if (isString(value)) return '\'' + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + '\''; + if (isNumber(value)) return value.toString(); + if (value === true) return 'true'; + if (value === false) return 'false'; + if (value === null) return 'null'; + if (typeof value === 'undefined') return 'undefined'; + + throw $parseMinErr('esc', 'IMPOSSIBLE'); + }, + + nextId: function(skip, init) { + var id = 'v' + (this.state.nextId++); + if (!skip) { + this.current().vars.push(id + (init ? '=' + init : '')); + } + return id; + }, + + current: function() { + return this.state[this.state.computing]; + } +}; + + +function ASTInterpreter($filter) { + this.$filter = $filter; +} + +ASTInterpreter.prototype = { + compile: function(ast) { + var self = this; + findConstantAndWatchExpressions(ast, self.$filter); + var assignable; + var assign; + if ((assignable = assignableAST(ast))) { + assign = this.recurse(assignable); + } + var toWatch = getInputs(ast.body); + var inputs; + if (toWatch) { + inputs = []; + forEach(toWatch, function(watch, key) { + var input = self.recurse(watch); + input.isPure = watch.isPure; + watch.input = input; + inputs.push(input); + watch.watchId = key; + }); + } + var expressions = []; + forEach(ast.body, function(expression) { + expressions.push(self.recurse(expression.expression)); + }); + var fn = ast.body.length === 0 ? noop : + ast.body.length === 1 ? expressions[0] : + function(scope, locals) { + var lastValue; + forEach(expressions, function(exp) { + lastValue = exp(scope, locals); + }); + return lastValue; + }; + if (assign) { + fn.assign = function(scope, value, locals) { + return assign(scope, locals, value); + }; + } + if (inputs) { + fn.inputs = inputs; + } + return fn; + }, + + recurse: function(ast, context, create) { + var left, right, self = this, args; + if (ast.input) { + return this.inputs(ast.input, ast.watchId); + } + switch (ast.type) { + case AST.Literal: + return this.value(ast.value, context); + case AST.UnaryExpression: + right = this.recurse(ast.argument); + return this['unary' + ast.operator](right, context); + case AST.BinaryExpression: + left = this.recurse(ast.left); + right = this.recurse(ast.right); + return this['binary' + ast.operator](left, right, context); + case AST.LogicalExpression: + left = this.recurse(ast.left); + right = this.recurse(ast.right); + return this['binary' + ast.operator](left, right, context); + case AST.ConditionalExpression: + return this['ternary?:']( + this.recurse(ast.test), + this.recurse(ast.alternate), + this.recurse(ast.consequent), + context + ); + case AST.Identifier: + return self.identifier(ast.name, context, create); + case AST.MemberExpression: + left = this.recurse(ast.object, false, !!create); + if (!ast.computed) { + right = ast.property.name; + } + if (ast.computed) right = this.recurse(ast.property); + return ast.computed ? + this.computedMember(left, right, context, create) : + this.nonComputedMember(left, right, context, create); + case AST.CallExpression: + args = []; + forEach(ast.arguments, function(expr) { + args.push(self.recurse(expr)); + }); + if (ast.filter) right = this.$filter(ast.callee.name); + if (!ast.filter) right = this.recurse(ast.callee, true); + return ast.filter ? + function(scope, locals, assign, inputs) { + var values = []; + for (var i = 0; i < args.length; ++i) { + values.push(args[i](scope, locals, assign, inputs)); + } + var value = right.apply(undefined, values, inputs); + return context ? {context: undefined, name: undefined, value: value} : value; + } : + function(scope, locals, assign, inputs) { + var rhs = right(scope, locals, assign, inputs); + var value; + if (rhs.value != null) { + var values = []; + for (var i = 0; i < args.length; ++i) { + values.push(args[i](scope, locals, assign, inputs)); + } + value = rhs.value.apply(rhs.context, values); + } + return context ? {value: value} : value; + }; + case AST.AssignmentExpression: + left = this.recurse(ast.left, true, 1); + right = this.recurse(ast.right); + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs = right(scope, locals, assign, inputs); + lhs.context[lhs.name] = rhs; + return context ? {value: rhs} : rhs; + }; + case AST.ArrayExpression: + args = []; + forEach(ast.elements, function(expr) { + args.push(self.recurse(expr)); + }); + return function(scope, locals, assign, inputs) { + var value = []; + for (var i = 0; i < args.length; ++i) { + value.push(args[i](scope, locals, assign, inputs)); + } + return context ? {value: value} : value; + }; + case AST.ObjectExpression: + args = []; + forEach(ast.properties, function(property) { + if (property.computed) { + args.push({key: self.recurse(property.key), + computed: true, + value: self.recurse(property.value) + }); + } else { + args.push({key: property.key.type === AST.Identifier ? + property.key.name : + ('' + property.key.value), + computed: false, + value: self.recurse(property.value) + }); + } + }); + return function(scope, locals, assign, inputs) { + var value = {}; + for (var i = 0; i < args.length; ++i) { + if (args[i].computed) { + value[args[i].key(scope, locals, assign, inputs)] = args[i].value(scope, locals, assign, inputs); + } else { + value[args[i].key] = args[i].value(scope, locals, assign, inputs); + } + } + return context ? {value: value} : value; + }; + case AST.ThisExpression: + return function(scope) { + return context ? {value: scope} : scope; + }; + case AST.LocalsExpression: + return function(scope, locals) { + return context ? {value: locals} : locals; + }; + case AST.NGValueParameter: + return function(scope, locals, assign) { + return context ? {value: assign} : assign; + }; + } + }, + + 'unary+': function(argument, context) { + return function(scope, locals, assign, inputs) { + var arg = argument(scope, locals, assign, inputs); + if (isDefined(arg)) { + arg = +arg; + } else { + arg = 0; + } + return context ? {value: arg} : arg; + }; + }, + 'unary-': function(argument, context) { + return function(scope, locals, assign, inputs) { + var arg = argument(scope, locals, assign, inputs); + if (isDefined(arg)) { + arg = -arg; + } else { + arg = -0; + } + return context ? {value: arg} : arg; + }; + }, + 'unary!': function(argument, context) { + return function(scope, locals, assign, inputs) { + var arg = !argument(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary+': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs = right(scope, locals, assign, inputs); + var arg = plusFn(lhs, rhs); + return context ? {value: arg} : arg; + }; + }, + 'binary-': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs = right(scope, locals, assign, inputs); + var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0); + return context ? {value: arg} : arg; + }; + }, + 'binary*': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary/': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary%': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary===': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary!==': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary==': function(left, right, context) { + return function(scope, locals, assign, inputs) { + // eslint-disable-next-line eqeqeq + var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary!=': function(left, right, context) { + return function(scope, locals, assign, inputs) { + // eslint-disable-next-line eqeqeq + var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary<': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary>': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary<=': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary>=': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary&&': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'binary||': function(left, right, context) { + return function(scope, locals, assign, inputs) { + var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + 'ternary?:': function(test, alternate, consequent, context) { + return function(scope, locals, assign, inputs) { + var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs); + return context ? {value: arg} : arg; + }; + }, + value: function(value, context) { + return function() { return context ? {context: undefined, name: undefined, value: value} : value; }; + }, + identifier: function(name, context, create) { + return function(scope, locals, assign, inputs) { + var base = locals && (name in locals) ? locals : scope; + if (create && create !== 1 && base && base[name] == null) { + base[name] = {}; + } + var value = base ? base[name] : undefined; + if (context) { + return {context: base, name: name, value: value}; + } else { + return value; + } + }; + }, + computedMember: function(left, right, context, create) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + var rhs; + var value; + if (lhs != null) { + rhs = right(scope, locals, assign, inputs); + rhs = getStringValue(rhs); + if (create && create !== 1) { + if (lhs && !(lhs[rhs])) { + lhs[rhs] = {}; + } + } + value = lhs[rhs]; + } + if (context) { + return {context: lhs, name: rhs, value: value}; + } else { + return value; + } + }; + }, + nonComputedMember: function(left, right, context, create) { + return function(scope, locals, assign, inputs) { + var lhs = left(scope, locals, assign, inputs); + if (create && create !== 1) { + if (lhs && lhs[right] == null) { + lhs[right] = {}; + } + } + var value = lhs != null ? lhs[right] : undefined; + if (context) { + return {context: lhs, name: right, value: value}; + } else { + return value; + } + }; + }, + inputs: function(input, watchId) { + return function(scope, value, locals, inputs) { + if (inputs) return inputs[watchId]; + return input(scope, value, locals); + }; + } +}; + +/** + * @constructor + */ +function Parser(lexer, $filter, options) { + this.ast = new AST(lexer, options); + this.astCompiler = options.csp ? new ASTInterpreter($filter) : + new ASTCompiler($filter); +} + +Parser.prototype = { + constructor: Parser, + + parse: function(text) { + var ast = this.getAst(text); + var fn = this.astCompiler.compile(ast.ast); + fn.literal = isLiteral(ast.ast); + fn.constant = isConstant(ast.ast); + fn.oneTime = ast.oneTime; + return fn; + }, + + getAst: function(exp) { + var oneTime = false; + exp = exp.trim(); + + if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { + oneTime = true; + exp = exp.substring(2); + } + return { + ast: this.ast.ast(exp), + oneTime: oneTime + }; + } +}; + +function getValueOf(value) { + return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value); +} + +/////////////////////////////////// + +/** + * @ngdoc service + * @name $parse + * @kind function + * + * @description + * + * Converts AngularJS {@link guide/expression expression} into a function. + * + * ```js + * var getter = $parse('user.name'); + * var setter = getter.assign; + * var context = {user:{name:'AngularJS'}}; + * var locals = {user:{name:'local'}}; + * + * expect(getter(context)).toEqual('AngularJS'); + * setter(context, 'newValue'); + * expect(context.user.name).toEqual('newValue'); + * expect(getter(context, locals)).toEqual('local'); + * ``` + * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + * + * The returned function also has the following properties: + * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript + * literal. + * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript + * constant literals. + * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be + * set to a function to change its value on the given context. + * + */ + + +/** + * @ngdoc provider + * @name $parseProvider + * @this + * + * @description + * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} + * service. + */ +function $ParseProvider() { + var cache = createMap(); + var literals = { + 'true': true, + 'false': false, + 'null': null, + 'undefined': undefined + }; + var identStart, identContinue; + + /** + * @ngdoc method + * @name $parseProvider#addLiteral + * @description + * + * Configure $parse service to add literal values that will be present as literal at expressions. + * + * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name. + * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`. + * + **/ + this.addLiteral = function(literalName, literalValue) { + literals[literalName] = literalValue; + }; + + /** + * @ngdoc method + * @name $parseProvider#setIdentifierFns + * + * @description + * + * Allows defining the set of characters that are allowed in AngularJS expressions. The function + * `identifierStart` will get called to know if a given character is a valid character to be the + * first character for an identifier. The function `identifierContinue` will get called to know if + * a given character is a valid character to be a follow-up identifier character. The functions + * `identifierStart` and `identifierContinue` will receive as arguments the single character to be + * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in + * mind that the `string` parameter can be two characters long depending on the character + * representation. It is expected for the function to return `true` or `false`, whether that + * character is allowed or not. + * + * Since this function will be called extensively, keep the implementation of these functions fast, + * as the performance of these functions have a direct impact on the expressions parsing speed. + * + * @param {function=} identifierStart The function that will decide whether the given character is + * a valid identifier start character. + * @param {function=} identifierContinue The function that will decide whether the given character is + * a valid identifier continue character. + */ + this.setIdentifierFns = function(identifierStart, identifierContinue) { + identStart = identifierStart; + identContinue = identifierContinue; + return this; + }; + + this.$get = ['$filter', function($filter) { + var noUnsafeEval = csp().noUnsafeEval; + var $parseOptions = { + csp: noUnsafeEval, + literals: copy(literals), + isIdentifierStart: isFunction(identStart) && identStart, + isIdentifierContinue: isFunction(identContinue) && identContinue + }; + $parse.$$getAst = $$getAst; + return $parse; + + function $parse(exp, interceptorFn) { + var parsedExpression, cacheKey; + + switch (typeof exp) { + case 'string': + exp = exp.trim(); + cacheKey = exp; + + parsedExpression = cache[cacheKey]; + + if (!parsedExpression) { + var lexer = new Lexer($parseOptions); + var parser = new Parser(lexer, $filter, $parseOptions); + parsedExpression = parser.parse(exp); + if (parsedExpression.constant) { + parsedExpression.$$watchDelegate = constantWatchDelegate; + } else if (parsedExpression.oneTime) { + parsedExpression.$$watchDelegate = parsedExpression.literal ? + oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; + } else if (parsedExpression.inputs) { + parsedExpression.$$watchDelegate = inputsWatchDelegate; + } + cache[cacheKey] = parsedExpression; + } + return addInterceptor(parsedExpression, interceptorFn); + + case 'function': + return addInterceptor(exp, interceptorFn); + + default: + return addInterceptor(noop, interceptorFn); + } + } + + function $$getAst(exp) { + var lexer = new Lexer($parseOptions); + var parser = new Parser(lexer, $filter, $parseOptions); + return parser.getAst(exp).ast; + } + + function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) { + + if (newValue == null || oldValueOfValue == null) { // null/undefined + return newValue === oldValueOfValue; + } + + if (typeof newValue === 'object') { + + // attempt to convert the value to a primitive type + // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can + // be cheaply dirty-checked + newValue = getValueOf(newValue); + + if (typeof newValue === 'object' && !compareObjectIdentity) { + // objects/arrays are not supported - deep-watching them would be too expensive + return false; + } + + // fall-through to the primitive equality check + } + + //Primitive or NaN + // eslint-disable-next-line no-self-compare + return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue); + } + + function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { + var inputExpressions = parsedExpression.inputs; + var lastResult; + + if (inputExpressions.length === 1) { + var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails + inputExpressions = inputExpressions[0]; + return scope.$watch(function expressionInputWatch(scope) { + var newInputValue = inputExpressions(scope); + if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, inputExpressions.isPure)) { + lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]); + oldInputValueOf = newInputValue && getValueOf(newInputValue); + } + return lastResult; + }, listener, objectEquality, prettyPrintExpression); + } + + var oldInputValueOfValues = []; + var oldInputValues = []; + for (var i = 0, ii = inputExpressions.length; i < ii; i++) { + oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails + oldInputValues[i] = null; + } + + return scope.$watch(function expressionInputsWatch(scope) { + var changed = false; + + for (var i = 0, ii = inputExpressions.length; i < ii; i++) { + var newInputValue = inputExpressions[i](scope); + if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], inputExpressions[i].isPure))) { + oldInputValues[i] = newInputValue; + oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); + } + } + + if (changed) { + lastResult = parsedExpression(scope, undefined, undefined, oldInputValues); + } + + return lastResult; + }, listener, objectEquality, prettyPrintExpression); + } + + function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) { + var unwatch, lastValue; + if (parsedExpression.inputs) { + unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression); + } else { + unwatch = scope.$watch(oneTimeWatch, oneTimeListener, objectEquality); + } + return unwatch; + + function oneTimeWatch(scope) { + return parsedExpression(scope); + } + function oneTimeListener(value, old, scope) { + lastValue = value; + if (isFunction(listener)) { + listener(value, old, scope); + } + if (isDefined(value)) { + scope.$$postDigest(function() { + if (isDefined(lastValue)) { + unwatch(); + } + }); + } + } + } + + function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch, lastValue; + unwatch = scope.$watch(function oneTimeWatch(scope) { + return parsedExpression(scope); + }, function oneTimeListener(value, old, scope) { + lastValue = value; + if (isFunction(listener)) { + listener(value, old, scope); + } + if (isAllDefined(value)) { + scope.$$postDigest(function() { + if (isAllDefined(lastValue)) unwatch(); + }); + } + }, objectEquality); + + return unwatch; + + function isAllDefined(value) { + var allDefined = true; + forEach(value, function(val) { + if (!isDefined(val)) allDefined = false; + }); + return allDefined; + } + } + + function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch = scope.$watch(function constantWatch(scope) { + unwatch(); + return parsedExpression(scope); + }, listener, objectEquality); + return unwatch; + } + + function addInterceptor(parsedExpression, interceptorFn) { + if (!interceptorFn) return parsedExpression; + var watchDelegate = parsedExpression.$$watchDelegate; + var useInputs = false; + + var regularWatch = + watchDelegate !== oneTimeLiteralWatchDelegate && + watchDelegate !== oneTimeWatchDelegate; + + var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) { + var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs); + return interceptorFn(value, scope, locals); + } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) { + var value = parsedExpression(scope, locals, assign, inputs); + var result = interceptorFn(value, scope, locals); + // we only return the interceptor's result if the + // initial value is defined (for bind-once) + return isDefined(value) ? result : value; + }; + + // Propagate $$watchDelegates other then inputsWatchDelegate + useInputs = !parsedExpression.inputs; + if (watchDelegate && watchDelegate !== inputsWatchDelegate) { + fn.$$watchDelegate = watchDelegate; + fn.inputs = parsedExpression.inputs; + } else if (!interceptorFn.$stateful) { + // Treat interceptor like filters - assume non-stateful by default and use the inputsWatchDelegate + fn.$$watchDelegate = inputsWatchDelegate; + fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression]; + } + + if (fn.inputs) { + fn.inputs = fn.inputs.map(function(e) { + // Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a + // potentially non-pure interceptor function. + if (e.isPure === PURITY_RELATIVE) { + return function depurifier(s) { return e(s); }; + } + return e; + }); + } + + return fn; + } + }]; +} + +/** + * @ngdoc service + * @name $q + * @requires $rootScope + * + * @description + * A service that helps you run functions asynchronously, and use their return values (or exceptions) + * when they are done processing. + * + * This is a [Promises/A+](https://promisesaplus.com/)-compliant implementation of promises/deferred + * objects inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). + * + * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred + * implementations, and the other which resembles ES6 (ES2015) promises to some degree. + * + * ## $q constructor + * + * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver` + * function as the first argument. This is similar to the native Promise implementation from ES6, + * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + * + * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are + * available yet. + * + * It can be used like so: + * + * ```js + * // for the purpose of this example let's assume that variables `$q` and `okToGreet` + * // are available in the current lexical scope (they could have been injected or passed in). + * + * function asyncGreet(name) { + * // perform some asynchronous operation, resolve or reject the promise when appropriate. + * return $q(function(resolve, reject) { + * setTimeout(function() { + * if (okToGreet(name)) { + * resolve('Hello, ' + name + '!'); + * } else { + * reject('Greeting ' + name + ' is not allowed.'); + * } + * }, 1000); + * }); + * } + * + * var promise = asyncGreet('Robin Hood'); + * promise.then(function(greeting) { + * alert('Success: ' + greeting); + * }, function(reason) { + * alert('Failed: ' + reason); + * }); + * ``` + * + * Note: progress/notify callbacks are not currently supported via the ES6-style interface. + * + * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise. + * + * However, the more traditional CommonJS-style usage is still available, and documented below. + * + * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an + * interface for interacting with an object that represents the result of an action that is + * performed asynchronously, and may or may not be finished at any given point in time. + * + * From the perspective of dealing with error handling, deferred and promise APIs are to + * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. + * + * ```js + * // for the purpose of this example let's assume that variables `$q` and `okToGreet` + * // are available in the current lexical scope (they could have been injected or passed in). + * + * function asyncGreet(name) { + * var deferred = $q.defer(); + * + * setTimeout(function() { + * deferred.notify('About to greet ' + name + '.'); + * + * if (okToGreet(name)) { + * deferred.resolve('Hello, ' + name + '!'); + * } else { + * deferred.reject('Greeting ' + name + ' is not allowed.'); + * } + * }, 1000); + * + * return deferred.promise; + * } + * + * var promise = asyncGreet('Robin Hood'); + * promise.then(function(greeting) { + * alert('Success: ' + greeting); + * }, function(reason) { + * alert('Failed: ' + reason); + * }, function(update) { + * alert('Got notification: ' + update); + * }); + * ``` + * + * At first it might not be obvious why this extra complexity is worth the trouble. The payoff + * comes in the way of guarantees that promise and deferred APIs make, see + * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. + * + * Additionally the promise api allows for composition that is very hard to do with the + * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. + * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the + * section on serial or parallel joining of promises. + * + * ## The Deferred API + * + * A new instance of deferred is constructed by calling `$q.defer()`. + * + * The purpose of the deferred object is to expose the associated Promise instance as well as APIs + * that can be used for signaling the successful or unsuccessful completion, as well as the status + * of the task. + * + * **Methods** + * + * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection + * constructed via `$q.reject`, the promise will be rejected instead. + * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to + * resolving it with a rejection constructed via `$q.reject`. + * - `notify(value)` - provides updates on the status of the promise's execution. This may be called + * multiple times before the promise is either resolved or rejected. + * + * **Properties** + * + * - promise – `{Promise}` – promise object associated with this deferred. + * + * + * ## The Promise API + * + * A new promise instance is created when a deferred instance is created and can be retrieved by + * calling `deferred.promise`. + * + * The purpose of the promise object is to allow for interested parties to get access to the result + * of the deferred task when it completes. + * + * **Methods** + * + * - `then(successCallback, [errorCallback], [notifyCallback])` – regardless of when the promise was or + * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously + * as soon as the result is available. The callbacks are called with a single argument: the result + * or rejection reason. Additionally, the notify callback may be called zero or more times to + * provide a progress indication, before the promise is resolved or rejected. + * + * This method *returns a new promise* which is resolved or rejected via the return value of the + * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved + * with the value which is resolved in that promise using + * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)). + * It also notifies via the return value of the `notifyCallback` method. The promise cannot be + * resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback + * arguments are optional. + * + * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` + * + * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise, + * but to do so without modifying the final value. This is useful to release resources or do some + * clean-up that needs to be done whether the promise was rejected or resolved. See the [full + * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for + * more information. + * + * ## Chaining promises + * + * Because calling the `then` method of a promise returns a new derived promise, it is easily + * possible to create a chain of promises: + * + * ```js + * promiseB = promiseA.then(function(result) { + * return result + 1; + * }); + * + * // promiseB will be resolved immediately after promiseA is resolved and its value + * // will be the result of promiseA incremented by 1 + * ``` + * + * It is possible to create chains of any length and since a promise can be resolved with another + * promise (which will defer its resolution further), it is possible to pause/defer resolution of + * the promises at any point in the chain. This makes it possible to implement powerful APIs like + * $http's response interceptors. + * + * + * ## Differences between Kris Kowal's Q and $q + * + * There are two main differences: + * + * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation + * mechanism in AngularJS, which means faster propagation of resolution or rejection into your + * models and avoiding unnecessary browser repaints, which would result in flickering UI. + * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains + * all the important functionality needed for common async tasks. + * + * ## Testing + * + * ```js + * it('should simulate promise', inject(function($q, $rootScope) { + * var deferred = $q.defer(); + * var promise = deferred.promise; + * var resolvedValue; + * + * promise.then(function(value) { resolvedValue = value; }); + * expect(resolvedValue).toBeUndefined(); + * + * // Simulate resolving of promise + * deferred.resolve(123); + * // Note that the 'then' function does not get called synchronously. + * // This is because we want the promise API to always be async, whether or not + * // it got called synchronously or asynchronously. + * expect(resolvedValue).toBeUndefined(); + * + * // Propagate promise resolution to 'then' functions using $apply(). + * $rootScope.$apply(); + * expect(resolvedValue).toEqual(123); + * })); + * ``` + * + * @param {function(function, function)} resolver Function which is responsible for resolving or + * rejecting the newly created promise. The first parameter is a function which resolves the + * promise, the second parameter is a function which rejects the promise. + * + * @returns {Promise} The newly created promise. + */ +/** + * @ngdoc provider + * @name $qProvider + * @this + * + * @description + */ +function $QProvider() { + var errorOnUnhandledRejections = true; + this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { + return qFactory(function(callback) { + $rootScope.$evalAsync(callback); + }, $exceptionHandler, errorOnUnhandledRejections); + }]; + + /** + * @ngdoc method + * @name $qProvider#errorOnUnhandledRejections + * @kind function + * + * @description + * Retrieves or overrides whether to generate an error when a rejected promise is not handled. + * This feature is enabled by default. + * + * @param {boolean=} value Whether to generate an error when a rejected promise is not handled. + * @returns {boolean|ng.$qProvider} Current value when called without a new value or self for + * chaining otherwise. + */ + this.errorOnUnhandledRejections = function(value) { + if (isDefined(value)) { + errorOnUnhandledRejections = value; + return this; + } else { + return errorOnUnhandledRejections; + } + }; +} + +/** @this */ +function $$QProvider() { + var errorOnUnhandledRejections = true; + this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) { + return qFactory(function(callback) { + $browser.defer(callback); + }, $exceptionHandler, errorOnUnhandledRejections); + }]; + + this.errorOnUnhandledRejections = function(value) { + if (isDefined(value)) { + errorOnUnhandledRejections = value; + return this; + } else { + return errorOnUnhandledRejections; + } + }; +} + +/** + * Constructs a promise manager. + * + * @param {function(function)} nextTick Function for executing functions in the next turn. + * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + * debugging purposes. + * @param {boolean=} errorOnUnhandledRejections Whether an error should be generated on unhandled + * promises rejections. + * @returns {object} Promise manager. + */ +function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) { + var $qMinErr = minErr('$q', TypeError); + var queueSize = 0; + var checkQueue = []; + + /** + * @ngdoc method + * @name ng.$q#defer + * @kind function + * + * @description + * Creates a `Deferred` object which represents a task which will finish in the future. + * + * @returns {Deferred} Returns a new instance of deferred. + */ + function defer() { + return new Deferred(); + } + + function Deferred() { + var promise = this.promise = new Promise(); + //Non prototype methods necessary to support unbound execution :/ + this.resolve = function(val) { resolvePromise(promise, val); }; + this.reject = function(reason) { rejectPromise(promise, reason); }; + this.notify = function(progress) { notifyPromise(promise, progress); }; + } + + + function Promise() { + this.$$state = { status: 0 }; + } + + extend(Promise.prototype, { + then: function(onFulfilled, onRejected, progressBack) { + if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) { + return this; + } + var result = new Promise(); + + this.$$state.pending = this.$$state.pending || []; + this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]); + if (this.$$state.status > 0) scheduleProcessQueue(this.$$state); + + return result; + }, + + 'catch': function(callback) { + return this.then(null, callback); + }, + + 'finally': function(callback, progressBack) { + return this.then(function(value) { + return handleCallback(value, resolve, callback); + }, function(error) { + return handleCallback(error, reject, callback); + }, progressBack); + } + }); + + function processQueue(state) { + var fn, promise, pending; + + pending = state.pending; + state.processScheduled = false; + state.pending = undefined; + try { + for (var i = 0, ii = pending.length; i < ii; ++i) { + markQStateExceptionHandled(state); + promise = pending[i][0]; + fn = pending[i][state.status]; + try { + if (isFunction(fn)) { + resolvePromise(promise, fn(state.value)); + } else if (state.status === 1) { + resolvePromise(promise, state.value); + } else { + rejectPromise(promise, state.value); + } + } catch (e) { + rejectPromise(promise, e); + // This error is explicitly marked for being passed to the $exceptionHandler + if (e && e.$$passToExceptionHandler === true) { + exceptionHandler(e); + } + } + } + } finally { + --queueSize; + if (errorOnUnhandledRejections && queueSize === 0) { + nextTick(processChecks); + } + } + } + + function processChecks() { + // eslint-disable-next-line no-unmodified-loop-condition + while (!queueSize && checkQueue.length) { + var toCheck = checkQueue.shift(); + if (!isStateExceptionHandled(toCheck)) { + markQStateExceptionHandled(toCheck); + var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value); + if (isError(toCheck.value)) { + exceptionHandler(toCheck.value, errorMessage); + } else { + exceptionHandler(errorMessage); + } + } + } + } + + function scheduleProcessQueue(state) { + if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !isStateExceptionHandled(state)) { + if (queueSize === 0 && checkQueue.length === 0) { + nextTick(processChecks); + } + checkQueue.push(state); + } + if (state.processScheduled || !state.pending) return; + state.processScheduled = true; + ++queueSize; + nextTick(function() { processQueue(state); }); + } + + function resolvePromise(promise, val) { + if (promise.$$state.status) return; + if (val === promise) { + $$reject(promise, $qMinErr( + 'qcycle', + 'Expected promise to be resolved with value other than itself \'{0}\'', + val)); + } else { + $$resolve(promise, val); + } + + } + + function $$resolve(promise, val) { + var then; + var done = false; + try { + if (isObject(val) || isFunction(val)) then = val.then; + if (isFunction(then)) { + promise.$$state.status = -1; + then.call(val, doResolve, doReject, doNotify); + } else { + promise.$$state.value = val; + promise.$$state.status = 1; + scheduleProcessQueue(promise.$$state); + } + } catch (e) { + doReject(e); + } + + function doResolve(val) { + if (done) return; + done = true; + $$resolve(promise, val); + } + function doReject(val) { + if (done) return; + done = true; + $$reject(promise, val); + } + function doNotify(progress) { + notifyPromise(promise, progress); + } + } + + function rejectPromise(promise, reason) { + if (promise.$$state.status) return; + $$reject(promise, reason); + } + + function $$reject(promise, reason) { + promise.$$state.value = reason; + promise.$$state.status = 2; + scheduleProcessQueue(promise.$$state); + } + + function notifyPromise(promise, progress) { + var callbacks = promise.$$state.pending; + + if ((promise.$$state.status <= 0) && callbacks && callbacks.length) { + nextTick(function() { + var callback, result; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + result = callbacks[i][0]; + callback = callbacks[i][3]; + try { + notifyPromise(result, isFunction(callback) ? callback(progress) : progress); + } catch (e) { + exceptionHandler(e); + } + } + }); + } + } + + /** + * @ngdoc method + * @name $q#reject + * @kind function + * + * @description + * Creates a promise that is resolved as rejected with the specified `reason`. This api should be + * used to forward rejection in a chain of promises. If you are dealing with the last promise in + * a promise chain, you don't need to worry about it. + * + * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of + * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via + * a promise error callback and you want to forward the error to the promise derived from the + * current promise, you have to "rethrow" the error by returning a rejection constructed via + * `reject`. + * + * ```js + * promiseB = promiseA.then(function(result) { + * // success: do something and resolve promiseB + * // with the old or a new result + * return result; + * }, function(reason) { + * // error: handle the error if possible and + * // resolve promiseB with newPromiseOrValue, + * // otherwise forward the rejection to promiseB + * if (canHandle(reason)) { + * // handle the error and recover + * return newPromiseOrValue; + * } + * return $q.reject(reason); + * }); + * ``` + * + * @param {*} reason Constant, message, exception or an object representing the rejection reason. + * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. + */ + function reject(reason) { + var result = new Promise(); + rejectPromise(result, reason); + return result; + } + + function handleCallback(value, resolver, callback) { + var callbackOutput = null; + try { + if (isFunction(callback)) callbackOutput = callback(); + } catch (e) { + return reject(e); + } + if (isPromiseLike(callbackOutput)) { + return callbackOutput.then(function() { + return resolver(value); + }, reject); + } else { + return resolver(value); + } + } + + /** + * @ngdoc method + * @name $q#when + * @kind function + * + * @description + * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. + * This is useful when you are dealing with an object that might or might not be a promise, or if + * the promise comes from a source that can't be trusted. + * + * @param {*} value Value or a promise + * @param {Function=} successCallback + * @param {Function=} errorCallback + * @param {Function=} progressCallback + * @returns {Promise} Returns a promise of the passed value or promise + */ + + + function when(value, callback, errback, progressBack) { + var result = new Promise(); + resolvePromise(result, value); + return result.then(callback, errback, progressBack); + } + + /** + * @ngdoc method + * @name $q#resolve + * @kind function + * + * @description + * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6. + * + * @param {*} value Value or a promise + * @param {Function=} successCallback + * @param {Function=} errorCallback + * @param {Function=} progressCallback + * @returns {Promise} Returns a promise of the passed value or promise + */ + var resolve = when; + + /** + * @ngdoc method + * @name $q#all + * @kind function + * + * @description + * Combines multiple promises into a single promise that is resolved when all of the input + * promises are resolved. + * + * @param {Array.|Object.} promises An array or hash of promises. + * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, + * each value corresponding to the promise at the same index/key in the `promises` array/hash. + * If any of the promises is resolved with a rejection, this resulting promise will be rejected + * with the same rejection value. + */ + + function all(promises) { + var result = new Promise(), + counter = 0, + results = isArray(promises) ? [] : {}; + + forEach(promises, function(promise, key) { + counter++; + when(promise).then(function(value) { + results[key] = value; + if (!(--counter)) resolvePromise(result, results); + }, function(reason) { + rejectPromise(result, reason); + }); + }); + + if (counter === 0) { + resolvePromise(result, results); + } + + return result; + } + + /** + * @ngdoc method + * @name $q#race + * @kind function + * + * @description + * Returns a promise that resolves or rejects as soon as one of those promises + * resolves or rejects, with the value or reason from that promise. + * + * @param {Array.|Object.} promises An array or hash of promises. + * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises` + * resolves or rejects, with the value or reason from that promise. + */ + + function race(promises) { + var deferred = defer(); + + forEach(promises, function(promise) { + when(promise).then(deferred.resolve, deferred.reject); + }); + + return deferred.promise; + } + + function $Q(resolver) { + if (!isFunction(resolver)) { + throw $qMinErr('norslvr', 'Expected resolverFn, got \'{0}\'', resolver); + } + + var promise = new Promise(); + + function resolveFn(value) { + resolvePromise(promise, value); + } + + function rejectFn(reason) { + rejectPromise(promise, reason); + } + + resolver(resolveFn, rejectFn); + + return promise; + } + + // Let's make the instanceof operator work for promises, so that + // `new $q(fn) instanceof $q` would evaluate to true. + $Q.prototype = Promise.prototype; + + $Q.defer = defer; + $Q.reject = reject; + $Q.when = when; + $Q.resolve = resolve; + $Q.all = all; + $Q.race = race; + + return $Q; +} + +function isStateExceptionHandled(state) { + return !!state.pur; +} +function markQStateExceptionHandled(state) { + state.pur = true; +} +function markQExceptionHandled(q) { + markQStateExceptionHandled(q.$$state); +} + +/** @this */ +function $$RAFProvider() { //rAF + this.$get = ['$window', '$timeout', function($window, $timeout) { + var requestAnimationFrame = $window.requestAnimationFrame || + $window.webkitRequestAnimationFrame; + + var cancelAnimationFrame = $window.cancelAnimationFrame || + $window.webkitCancelAnimationFrame || + $window.webkitCancelRequestAnimationFrame; + + var rafSupported = !!requestAnimationFrame; + var raf = rafSupported + ? function(fn) { + var id = requestAnimationFrame(fn); + return function() { + cancelAnimationFrame(id); + }; + } + : function(fn) { + var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 + return function() { + $timeout.cancel(timer); + }; + }; + + raf.supported = rafSupported; + + return raf; + }]; +} + +/** + * DESIGN NOTES + * + * The design decisions behind the scope are heavily favored for speed and memory consumption. + * + * The typical use of scope is to watch the expressions, which most of the time return the same + * value as last time so we optimize the operation. + * + * Closures construction is expensive in terms of speed as well as memory: + * - No closures, instead use prototypical inheritance for API + * - Internal state needs to be stored on scope directly, which means that private state is + * exposed as $$____ properties + * + * Loop operations are optimized by using while(count--) { ... } + * - This means that in order to keep the same order of execution as addition we have to add + * items to the array at the beginning (unshift) instead of at the end (push) + * + * Child scopes are created and removed often + * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists + * + * There are fewer watches than observers. This is why you don't want the observer to be implemented + * in the same way as watch. Watch requires return of the initialization function which is expensive + * to construct. + */ + + +/** + * @ngdoc provider + * @name $rootScopeProvider + * @description + * + * Provider for the $rootScope service. + */ + +/** + * @ngdoc method + * @name $rootScopeProvider#digestTtl + * @description + * + * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * In complex applications it's possible that the dependencies between `$watch`s will result in + * several digest iterations. However if an application needs more than the default 10 digest + * iterations for its model to stabilize then you should investigate what is causing the model to + * continuously change during the digest. + * + * Increasing the TTL could have performance implications, so you should not change it without + * proper justification. + * + * @param {number} limit The number of digest iterations. + */ + + +/** + * @ngdoc service + * @name $rootScope + * @this + * + * @description + * + * Every application has a single root {@link ng.$rootScope.Scope scope}. + * All other scopes are descendant scopes of the root scope. Scopes provide separation + * between the model and the view, via a mechanism for watching the model for changes. + * They also provide event emission/broadcast and subscription facility. See the + * {@link guide/scope developer guide on scopes}. + */ +function $RootScopeProvider() { + var TTL = 10; + var $rootScopeMinErr = minErr('$rootScope'); + var lastDirtyWatch = null; + var applyAsyncId = null; + + this.digestTtl = function(value) { + if (arguments.length) { + TTL = value; + } + return TTL; + }; + + function createChildScopeClass(parent) { + function ChildScope() { + this.$$watchers = this.$$nextSibling = + this.$$childHead = this.$$childTail = null; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$$watchersCount = 0; + this.$id = nextUid(); + this.$$ChildScope = null; + } + ChildScope.prototype = parent; + return ChildScope; + } + + this.$get = ['$exceptionHandler', '$parse', '$browser', + function($exceptionHandler, $parse, $browser) { + + function destroyChildScope($event) { + $event.currentScope.$$destroyed = true; + } + + function cleanUpScope($scope) { + + // Support: IE 9 only + if (msie === 9) { + // There is a memory leak in IE9 if all child scopes are not disconnected + // completely when a scope is destroyed. So this code will recurse up through + // all this scopes children + // + // See issue https://github.com/angular/angular.js/issues/10706 + if ($scope.$$childHead) { + cleanUpScope($scope.$$childHead); + } + if ($scope.$$nextSibling) { + cleanUpScope($scope.$$nextSibling); + } + } + + // The code below works around IE9 and V8's memory leaks + // + // See: + // - https://code.google.com/p/v8/issues/detail?id=2073#c26 + // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 + // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + + $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead = + $scope.$$childTail = $scope.$root = $scope.$$watchers = null; + } + + /** + * @ngdoc type + * @name $rootScope.Scope + * + * @description + * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the + * {@link auto.$injector $injector}. Child scopes are created using the + * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when + * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for + * an in-depth introduction and usage examples. + * + * + * ## Inheritance + * A scope can inherit from a parent scope, as in this example: + * ```js + var parent = $rootScope; + var child = parent.$new(); + + parent.salutation = "Hello"; + expect(child.salutation).toEqual('Hello'); + + child.salutation = "Welcome"; + expect(child.salutation).toEqual('Welcome'); + expect(parent.salutation).toEqual('Hello'); + * ``` + * + * When interacting with `Scope` in tests, additional helper methods are available on the + * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional + * details. + * + * + * @param {Object.=} providers Map of service factory which need to be + * provided for the current scope. Defaults to {@link ng}. + * @param {Object.=} instanceCache Provides pre-instantiated services which should + * append/override services provided by `providers`. This is handy + * when unit-testing and having the need to override a default + * service. + * @returns {Object} Newly created scope. + * + */ + function Scope() { + this.$id = nextUid(); + this.$$phase = this.$parent = this.$$watchers = + this.$$nextSibling = this.$$prevSibling = + this.$$childHead = this.$$childTail = null; + this.$root = this; + this.$$destroyed = false; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$$watchersCount = 0; + this.$$isolateBindings = null; + } + + /** + * @ngdoc property + * @name $rootScope.Scope#$id + * + * @description + * Unique scope ID (monotonically increasing) useful for debugging. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$parent + * + * @description + * Reference to the parent scope. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$root + * + * @description + * Reference to the root scope. + */ + + Scope.prototype = { + constructor: Scope, + /** + * @ngdoc method + * @name $rootScope.Scope#$new + * @kind function + * + * @description + * Creates a new child {@link ng.$rootScope.Scope scope}. + * + * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. + * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. + * + * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is + * desired for the scope and its child scopes to be permanently detached from the parent and + * thus stop participating in model change detection and listener notification by invoking. + * + * @param {boolean} isolate If true, then the scope does not prototypically inherit from the + * parent scope. The scope is isolated, as it can not see parent scope properties. + * When creating widgets, it is useful for the widget to not accidentally read parent + * state. + * + * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent` + * of the newly created scope. Defaults to `this` scope if not provided. + * This is used when creating a transclude scope to correctly place it + * in the scope hierarchy while maintaining the correct prototypical + * inheritance. + * + * @returns {Object} The newly created child scope. + * + */ + $new: function(isolate, parent) { + var child; + + parent = parent || this; + + if (isolate) { + child = new Scope(); + child.$root = this.$root; + } else { + // Only create a child scope class if somebody asks for one, + // but cache it to allow the VM to optimize lookups. + if (!this.$$ChildScope) { + this.$$ChildScope = createChildScopeClass(this); + } + child = new this.$$ChildScope(); + } + child.$parent = parent; + child.$$prevSibling = parent.$$childTail; + if (parent.$$childHead) { + parent.$$childTail.$$nextSibling = child; + parent.$$childTail = child; + } else { + parent.$$childHead = parent.$$childTail = child; + } + + // When the new scope is not isolated or we inherit from `this`, and + // the parent scope is destroyed, the property `$$destroyed` is inherited + // prototypically. In all other cases, this property needs to be set + // when the parent scope is destroyed. + // The listener needs to be added after the parent is set + if (isolate || parent !== this) child.$on('$destroy', destroyChildScope); + + return child; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$watch + * @kind function + * + * @description + * Registers a `listener` callback to be executed whenever the `watchExpression` changes. + * + * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest + * $digest()} and should return the value that will be watched. (`watchExpression` should not change + * its value when executed multiple times with the same input because it may be executed multiple + * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be + * [idempotent](http://en.wikipedia.org/wiki/Idempotence).) + * - The `listener` is called only when the value from the current `watchExpression` and the + * previous call to `watchExpression` are not equal (with the exception of the initial run, + * see below). Inequality is determined according to reference inequality, + * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) + * via the `!==` Javascript operator, unless `objectEquality == true` + * (see next point) + * - When `objectEquality == true`, inequality of the `watchExpression` is determined + * according to the {@link angular.equals} function. To save the value of the object for + * later comparison, the {@link angular.copy} function is used. This therefore means that + * watching complex objects will have adverse memory and performance implications. + * - This should not be used to watch for changes in objects that are + * or contain [File](https://developer.mozilla.org/docs/Web/API/File) objects due to limitations with {@link angular.copy `angular.copy`}. + * - The watch `listener` may change the model, which may trigger other `listener`s to fire. + * This is achieved by rerunning the watchers until no changes are detected. The rerun + * iteration limit is 10 to prevent an infinite loop deadlock. + * + * + * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, + * you can register a `watchExpression` function with no `listener`. (Be prepared for + * multiple calls to your `watchExpression` because it will execute multiple times in a + * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.) + * + * After a watcher is registered with the scope, the `listener` fn is called asynchronously + * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the + * watcher. In rare cases, this is undesirable because the listener is called when the result + * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you + * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the + * listener was called due to initialization. + * + * + * + * @example + * ```js + // let's assume that scope was dependency injected as the $rootScope + var scope = $rootScope; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + + + + // Using a function as a watchExpression + var food; + scope.foodCounter = 0; + expect(scope.foodCounter).toEqual(0); + scope.$watch( + // This function returns the value being watched. It is called for each turn of the $digest loop + function() { return food; }, + // This is the change listener, called when the value returned from the above function changes + function(newValue, oldValue) { + if ( newValue !== oldValue ) { + // Only increment the counter if the value changed + scope.foodCounter = scope.foodCounter + 1; + } + } + ); + // No digest has been run so the counter will be zero + expect(scope.foodCounter).toEqual(0); + + // Run the digest but since food has not changed count will still be zero + scope.$digest(); + expect(scope.foodCounter).toEqual(0); + + // Update food and run digest. Now the counter will increment + food = 'cheeseburger'; + scope.$digest(); + expect(scope.foodCounter).toEqual(1); + + * ``` + * + * + * + * @param {(function()|string)} watchExpression Expression that is evaluated on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers + * a call to the `listener`. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(scope)`: called with current `scope` as a parameter. + * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value + * of `watchExpression` changes. + * + * - `newVal` contains the current value of the `watchExpression` + * - `oldVal` contains the previous value of the `watchExpression` + * - `scope` refers to the current scope + * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of + * comparing for reference equality. + * @returns {function()} Returns a deregistration function for this listener. + */ + $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) { + var get = $parse(watchExp); + var fn = isFunction(listener) ? listener : noop; + + if (get.$$watchDelegate) { + return get.$$watchDelegate(this, fn, objectEquality, get, watchExp); + } + var scope = this, + array = scope.$$watchers, + watcher = { + fn: fn, + last: initWatchVal, + get: get, + exp: prettyPrintExpression || watchExp, + eq: !!objectEquality + }; + + lastDirtyWatch = null; + + if (!array) { + array = scope.$$watchers = []; + array.$$digestWatchIndex = -1; + } + // we use unshift since we use a while loop in $digest for speed. + // the while loop reads in reverse order. + array.unshift(watcher); + array.$$digestWatchIndex++; + incrementWatchersCount(this, 1); + + return function deregisterWatch() { + var index = arrayRemove(array, watcher); + if (index >= 0) { + incrementWatchersCount(scope, -1); + if (index < array.$$digestWatchIndex) { + array.$$digestWatchIndex--; + } + } + lastDirtyWatch = null; + }; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$watchGroup + * @kind function + * + * @description + * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`. + * If any one expression in the collection changes the `listener` is executed. + * + * - The items in the `watchExpressions` array are observed via the standard `$watch` operation. Their return + * values are examined for changes on every call to `$digest`. + * - The `listener` is called whenever any expression in the `watchExpressions` array changes. + * + * `$watchGroup` is more performant than watching each expression individually, and should be + * used when the listener does not need to know which expression has changed. + * If the listener needs to know which expression has changed, + * {@link ng.$rootScope.Scope#$watch $watch()} or + * {@link ng.$rootScope.Scope#$watchCollection $watchCollection()} should be used. + * + * @param {Array.} watchExpressions Array of expressions that will be individually + * watched using {@link ng.$rootScope.Scope#$watch $watch()} + * + * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any + * expression in `watchExpressions` changes + * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching + * those of `watchExpression` + * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching + * those of `watchExpression`. + * + * Note that `newValues` and `oldValues` reflect the differences in each **individual** + * expression, and not the difference of the values between each call of the listener. + * That means the difference between `newValues` and `oldValues` cannot be used to determine + * which expression has changed / remained stable: + * + * ```js + * + * $scope.$watchGroup(['v1', 'v2'], function(newValues, oldValues) { + * console.log(newValues, oldValues); + * }); + * + * // newValues, oldValues initially + * // [undefined, undefined], [undefined, undefined] + * + * $scope.v1 = 'a'; + * $scope.v2 = 'a'; + * + * // ['a', 'a'], [undefined, undefined] + * + * $scope.v2 = 'b' + * + * // v1 hasn't changed since it became `'a'`, therefore its oldValue is still `undefined` + * // ['a', 'b'], [undefined, 'a'] + * + * ``` + * + * The `scope` refers to the current scope. + * @returns {function()} Returns a de-registration function for all listeners. + */ + $watchGroup: function(watchExpressions, listener) { + var oldValues = new Array(watchExpressions.length); + var newValues = new Array(watchExpressions.length); + var deregisterFns = []; + var self = this; + var changeReactionScheduled = false; + var firstRun = true; + + if (!watchExpressions.length) { + // No expressions means we call the listener ASAP + var shouldCall = true; + self.$evalAsync(function() { + if (shouldCall) listener(newValues, newValues, self); + }); + return function deregisterWatchGroup() { + shouldCall = false; + }; + } + + if (watchExpressions.length === 1) { + // Special case size of one + return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) { + newValues[0] = value; + oldValues[0] = oldValue; + listener(newValues, (value === oldValue) ? newValues : oldValues, scope); + }); + } + + forEach(watchExpressions, function(expr, i) { + var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { + newValues[i] = value; + oldValues[i] = oldValue; + if (!changeReactionScheduled) { + changeReactionScheduled = true; + self.$evalAsync(watchGroupAction); + } + }); + deregisterFns.push(unwatchFn); + }); + + function watchGroupAction() { + changeReactionScheduled = false; + + if (firstRun) { + firstRun = false; + listener(newValues, newValues, self); + } else { + listener(newValues, oldValues, self); + } + } + + return function deregisterWatchGroup() { + while (deregisterFns.length) { + deregisterFns.shift()(); + } + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$watchCollection + * @kind function + * + * @description + * Shallow watches the properties of an object and fires whenever any of the properties change + * (for arrays, this implies watching the array items; for object maps, this implies watching + * the properties). If a change is detected, the `listener` callback is fired. + * + * - The `obj` collection is observed via standard $watch operation and is examined on every + * call to $digest() to see if any items have been added, removed, or moved. + * - The `listener` is called whenever anything within the `obj` has changed. Examples include + * adding, removing, and moving items belonging to an object or array. + * + * + * @example + * ```js + $scope.names = ['igor', 'matias', 'misko', 'james']; + $scope.dataCount = 4; + + $scope.$watchCollection('names', function(newNames, oldNames) { + $scope.dataCount = newNames.length; + }); + + expect($scope.dataCount).toEqual(4); + $scope.$digest(); + + //still at 4 ... no changes + expect($scope.dataCount).toEqual(4); + + $scope.names.pop(); + $scope.$digest(); + + //now there's been a change + expect($scope.dataCount).toEqual(3); + * ``` + * + * + * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The + * expression value should evaluate to an object or an array which is observed on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the + * collection will trigger a call to the `listener`. + * + * @param {function(newCollection, oldCollection, scope)} listener a callback function called + * when a change is detected. + * - The `newCollection` object is the newly modified data obtained from the `obj` expression + * - The `oldCollection` object is a copy of the former collection data. + * Due to performance considerations, the`oldCollection` value is computed only if the + * `listener` function declares two or more arguments. + * - The `scope` argument refers to the current scope. + * + * @returns {function()} Returns a de-registration function for this listener. When the + * de-registration function is executed, the internal watch operation is terminated. + */ + $watchCollection: function(obj, listener) { + $watchCollectionInterceptor.$stateful = true; + + var self = this; + // the current value, updated on each dirty-check run + var newValue; + // a shallow copy of the newValue from the last dirty-check run, + // updated to match newValue during dirty-check run + var oldValue; + // a shallow copy of the newValue from when the last change happened + var veryOldValue; + // only track veryOldValue if the listener is asking for it + var trackVeryOldValue = (listener.length > 1); + var changeDetected = 0; + var changeDetector = $parse(obj, $watchCollectionInterceptor); + var internalArray = []; + var internalObject = {}; + var initRun = true; + var oldLength = 0; + + function $watchCollectionInterceptor(_value) { + newValue = _value; + var newLength, key, bothNaN, newItem, oldItem; + + // If the new value is undefined, then return undefined as the watch may be a one-time watch + if (isUndefined(newValue)) return; + + if (!isObject(newValue)) { // if primitive + if (oldValue !== newValue) { + oldValue = newValue; + changeDetected++; + } + } else if (isArrayLike(newValue)) { + if (oldValue !== internalArray) { + // we are transitioning from something which was not an array into array. + oldValue = internalArray; + oldLength = oldValue.length = 0; + changeDetected++; + } + + newLength = newValue.length; + + if (oldLength !== newLength) { + // if lengths do not match we need to trigger change notification + changeDetected++; + oldValue.length = oldLength = newLength; + } + // copy the items to oldValue and look for changes. + for (var i = 0; i < newLength; i++) { + oldItem = oldValue[i]; + newItem = newValue[i]; + + // eslint-disable-next-line no-self-compare + bothNaN = (oldItem !== oldItem) && (newItem !== newItem); + if (!bothNaN && (oldItem !== newItem)) { + changeDetected++; + oldValue[i] = newItem; + } + } + } else { + if (oldValue !== internalObject) { + // we are transitioning from something which was not an object into object. + oldValue = internalObject = {}; + oldLength = 0; + changeDetected++; + } + // copy the items to oldValue and look for changes. + newLength = 0; + for (key in newValue) { + if (hasOwnProperty.call(newValue, key)) { + newLength++; + newItem = newValue[key]; + oldItem = oldValue[key]; + + if (key in oldValue) { + // eslint-disable-next-line no-self-compare + bothNaN = (oldItem !== oldItem) && (newItem !== newItem); + if (!bothNaN && (oldItem !== newItem)) { + changeDetected++; + oldValue[key] = newItem; + } + } else { + oldLength++; + oldValue[key] = newItem; + changeDetected++; + } + } + } + if (oldLength > newLength) { + // we used to have more keys, need to find them and destroy them. + changeDetected++; + for (key in oldValue) { + if (!hasOwnProperty.call(newValue, key)) { + oldLength--; + delete oldValue[key]; + } + } + } + } + return changeDetected; + } + + function $watchCollectionAction() { + if (initRun) { + initRun = false; + listener(newValue, newValue, self); + } else { + listener(newValue, veryOldValue, self); + } + + // make a copy for the next time a collection is changed + if (trackVeryOldValue) { + if (!isObject(newValue)) { + //primitive + veryOldValue = newValue; + } else if (isArrayLike(newValue)) { + veryOldValue = new Array(newValue.length); + for (var i = 0; i < newValue.length; i++) { + veryOldValue[i] = newValue[i]; + } + } else { // if object + veryOldValue = {}; + for (var key in newValue) { + if (hasOwnProperty.call(newValue, key)) { + veryOldValue[key] = newValue[key]; + } + } + } + } + } + + return this.$watch(changeDetector, $watchCollectionAction); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$digest + * @kind function + * + * @description + * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and + * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change + * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} + * until no more listeners are firing. This means that it is possible to get into an infinite + * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of + * iterations exceeds 10. + * + * Usually, you don't call `$digest()` directly in + * {@link ng.directive:ngController controllers} or in + * {@link ng.$compileProvider#directive directives}. + * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within + * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`. + * + * If you want to be notified whenever `$digest()` is called, + * you can register a `watchExpression` function with + * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. + * + * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. + * + * @example + * ```js + var scope = ...; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + * ``` + * + */ + $digest: function() { + var watch, value, last, fn, get, + watchers, + dirty, ttl = TTL, + next, current, target = this, + watchLog = [], + logIdx, asyncTask; + + beginPhase('$digest'); + // Check for changes to browser url that happened in sync before the call to $digest + $browser.$$checkUrlChange(); + + if (this === $rootScope && applyAsyncId !== null) { + // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then + // cancel the scheduled $apply and flush the queue of expressions to be evaluated. + $browser.defer.cancel(applyAsyncId); + flushApplyAsync(); + } + + lastDirtyWatch = null; + + do { // "while dirty" loop + dirty = false; + current = target; + + // It's safe for asyncQueuePosition to be a local variable here because this loop can't + // be reentered recursively. Calling $digest from a function passed to $evalAsync would + // lead to a '$digest already in progress' error. + for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) { + try { + asyncTask = asyncQueue[asyncQueuePosition]; + fn = asyncTask.fn; + fn(asyncTask.scope, asyncTask.locals); + } catch (e) { + $exceptionHandler(e); + } + lastDirtyWatch = null; + } + asyncQueue.length = 0; + + traverseScopesLoop: + do { // "traverse the scopes" loop + if ((watchers = current.$$watchers)) { + // process our watches + watchers.$$digestWatchIndex = watchers.length; + while (watchers.$$digestWatchIndex--) { + try { + watch = watchers[watchers.$$digestWatchIndex]; + // Most common watches are on primitives, in which case we can short + // circuit it with === operator, only when === fails do we use .equals + if (watch) { + get = watch.get; + if ((value = get(current)) !== (last = watch.last) && + !(watch.eq + ? equals(value, last) + : (isNumberNaN(value) && isNumberNaN(last)))) { + dirty = true; + lastDirtyWatch = watch; + watch.last = watch.eq ? copy(value, null) : value; + fn = watch.fn; + fn(value, ((last === initWatchVal) ? value : last), current); + if (ttl < 5) { + logIdx = 4 - ttl; + if (!watchLog[logIdx]) watchLog[logIdx] = []; + watchLog[logIdx].push({ + msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, + newVal: value, + oldVal: last + }); + } + } else if (watch === lastDirtyWatch) { + // If the most recently dirty watcher is now clean, short circuit since the remaining watchers + // have already been tested. + dirty = false; + break traverseScopesLoop; + } + } + } catch (e) { + $exceptionHandler(e); + } + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $broadcast + if (!(next = ((current.$$watchersCount && current.$$childHead) || + (current !== target && current.$$nextSibling)))) { + while (current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } while ((current = next)); + + // `break traverseScopesLoop;` takes us to here + + if ((dirty || asyncQueue.length) && !(ttl--)) { + clearPhase(); + throw $rootScopeMinErr('infdig', + '{0} $digest() iterations reached. Aborting!\n' + + 'Watchers fired in the last 5 iterations: {1}', + TTL, watchLog); + } + + } while (dirty || asyncQueue.length); + + clearPhase(); + + // postDigestQueuePosition isn't local here because this loop can be reentered recursively. + while (postDigestQueuePosition < postDigestQueue.length) { + try { + postDigestQueue[postDigestQueuePosition++](); + } catch (e) { + $exceptionHandler(e); + } + } + postDigestQueue.length = postDigestQueuePosition = 0; + + // Check for changes to browser url that happened during the $digest + // (for which no event is fired; e.g. via `history.pushState()`) + $browser.$$checkUrlChange(); + }, + + + /** + * @ngdoc event + * @name $rootScope.Scope#$destroy + * @eventType broadcast on scope being destroyed + * + * @description + * Broadcasted when a scope and its children are being destroyed. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + + /** + * @ngdoc method + * @name $rootScope.Scope#$destroy + * @kind function + * + * @description + * Removes the current scope (and all of its children) from the parent scope. Removal implies + * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer + * propagate to the current scope and its children. Removal also implies that the current + * scope is eligible for garbage collection. + * + * The `$destroy()` is usually used by directives such as + * {@link ng.directive:ngRepeat ngRepeat} for managing the + * unrolling of the loop. + * + * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. + * Application code can register a `$destroy` event handler that will give it a chance to + * perform any necessary cleanup. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + $destroy: function() { + // We can't destroy a scope that has been already destroyed. + if (this.$$destroyed) return; + var parent = this.$parent; + + this.$broadcast('$destroy'); + this.$$destroyed = true; + + if (this === $rootScope) { + //Remove handlers attached to window when $rootScope is removed + $browser.$$applicationDestroyed(); + } + + incrementWatchersCount(this, -this.$$watchersCount); + for (var eventName in this.$$listenerCount) { + decrementListenerCount(this, this.$$listenerCount[eventName], eventName); + } + + // sever all the references to parent scopes (after this cleanup, the current scope should + // not be retained by any of our references and should be eligible for garbage collection) + if (parent && parent.$$childHead === this) parent.$$childHead = this.$$nextSibling; + if (parent && parent.$$childTail === this) parent.$$childTail = this.$$prevSibling; + if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; + if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; + + // Disable listeners, watchers and apply/digest methods + this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop; + this.$on = this.$watch = this.$watchGroup = function() { return noop; }; + this.$$listeners = {}; + + // Disconnect the next sibling to prevent `cleanUpScope` destroying those too + this.$$nextSibling = null; + cleanUpScope(this); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$eval + * @kind function + * + * @description + * Executes the `expression` on the current scope and returns the result. Any exceptions in + * the expression are propagated (uncaught). This is useful when evaluating AngularJS + * expressions. + * + * @example + * ```js + var scope = ng.$rootScope.Scope(); + scope.a = 1; + scope.b = 2; + + expect(scope.$eval('a+b')).toEqual(3); + expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); + * ``` + * + * @param {(string|function())=} expression An AngularJS expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. + * @returns {*} The result of evaluating the expression. + */ + $eval: function(expr, locals) { + return $parse(expr)(this, locals); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$evalAsync + * @kind function + * + * @description + * Executes the expression on the current scope at a later point in time. + * + * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only + * that: + * + * - it will execute after the function that scheduled the evaluation (preferably before DOM + * rendering). + * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after + * `expression` execution. + * + * Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle + * will be scheduled. However, it is encouraged to always call code that changes the model + * from within an `$apply` call. That includes code evaluated via `$evalAsync`. + * + * @param {(string|function())=} expression An AngularJS expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. + */ + $evalAsync: function(expr, locals) { + // if we are outside of an $digest loop and this is the first time we are scheduling async + // task also schedule async auto-flush + if (!$rootScope.$$phase && !asyncQueue.length) { + $browser.defer(function() { + if (asyncQueue.length) { + $rootScope.$digest(); + } + }); + } + + asyncQueue.push({scope: this, fn: $parse(expr), locals: locals}); + }, + + $$postDigest: function(fn) { + postDigestQueue.push(fn); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$apply + * @kind function + * + * @description + * `$apply()` is used to execute an expression in AngularJS from outside of the AngularJS + * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). + * Because we are calling into the AngularJS framework we need to perform proper scope life + * cycle of {@link ng.$exceptionHandler exception handling}, + * {@link ng.$rootScope.Scope#$digest executing watches}. + * + * **Life cycle: Pseudo-Code of `$apply()`** + * + * ```js + function $apply(expr) { + try { + return $eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + $root.$digest(); + } + } + * ``` + * + * + * Scope's `$apply()` method transitions through the following stages: + * + * 1. The {@link guide/expression expression} is executed using the + * {@link ng.$rootScope.Scope#$eval $eval()} method. + * 2. Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the + * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. + * + * + * @param {(string|function())=} exp An AngularJS expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $apply: function(expr) { + try { + beginPhase('$apply'); + try { + return this.$eval(expr); + } finally { + clearPhase(); + } + } catch (e) { + $exceptionHandler(e); + } finally { + try { + $rootScope.$digest(); + } catch (e) { + $exceptionHandler(e); + // eslint-disable-next-line no-unsafe-finally + throw e; + } + } + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$applyAsync + * @kind function + * + * @description + * Schedule the invocation of $apply to occur at a later time. The actual time difference + * varies across browsers, but is typically around ~10 milliseconds. + * + * This can be used to queue up multiple expressions which need to be evaluated in the same + * digest. + * + * @param {(string|function())=} exp An AngularJS expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + */ + $applyAsync: function(expr) { + var scope = this; + if (expr) { + applyAsyncQueue.push($applyAsyncExpression); + } + expr = $parse(expr); + scheduleApplyAsync(); + + function $applyAsyncExpression() { + scope.$eval(expr); + } + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$on + * @kind function + * + * @description + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for + * discussion of event life cycle. + * + * The event listener function format is: `function(event, args...)`. The `event` object + * passed into the listener has the following attributes: + * + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or + * `$broadcast`-ed. + * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the + * event propagates through the scope hierarchy, this property is set to null. + * - `name` - `{string}`: name of the event. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel + * further event propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag + * to true. + * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. + * + * @param {string} name Event name to listen on. + * @param {function(event, ...args)} listener Function to call when the event is emitted. + * @returns {function()} Returns a deregistration function for this listener. + */ + $on: function(name, listener) { + var namedListeners = this.$$listeners[name]; + if (!namedListeners) { + this.$$listeners[name] = namedListeners = []; + } + namedListeners.push(listener); + + var current = this; + do { + if (!current.$$listenerCount[name]) { + current.$$listenerCount[name] = 0; + } + current.$$listenerCount[name]++; + } while ((current = current.$parent)); + + var self = this; + return function() { + var indexOfListener = namedListeners.indexOf(listener); + if (indexOfListener !== -1) { + // Use delete in the hope of the browser deallocating the memory for the array entry, + // while not shifting the array indexes of other listeners. + // See issue https://github.com/angular/angular.js/issues/16135 + delete namedListeners[indexOfListener]; + decrementListenerCount(self, 1, name); + } + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$emit + * @kind function + * + * @description + * Dispatches an event `name` upwards through the scope hierarchy notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$emit` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event traverses upwards toward the root scope and calls all + * registered listeners along the way. The event will stop propagating if one of the listeners + * cancels it. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to emit. + * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. + * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). + */ + $emit: function(name, args) { + var empty = [], + namedListeners, + scope = this, + stopPropagation = false, + event = { + name: name, + targetScope: scope, + stopPropagation: function() {stopPropagation = true;}, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }, + listenerArgs = concat([event], arguments, 1), + i, length; + + do { + namedListeners = scope.$$listeners[name] || empty; + event.currentScope = scope; + for (i = 0, length = namedListeners.length; i < length; i++) { + + // if listeners were deregistered, defragment the array + if (!namedListeners[i]) { + namedListeners.splice(i, 1); + i--; + length--; + continue; + } + try { + //allow all listeners attached to the current scope to run + namedListeners[i].apply(null, listenerArgs); + } catch (e) { + $exceptionHandler(e); + } + } + //if any listener on the current scope stops propagation, prevent bubbling + if (stopPropagation) { + break; + } + //traverse upwards + scope = scope.$parent; + } while (scope); + + event.currentScope = null; + + return event; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$broadcast + * @kind function + * + * @description + * Dispatches an event `name` downwards to all child scopes (and their children) notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$broadcast` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event propagates to all direct and indirect scopes of the current + * scope and calls all registered listeners along the way. The event cannot be canceled. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to broadcast. + * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. + * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} + */ + $broadcast: function(name, args) { + var target = this, + current = target, + next = target, + event = { + name: name, + targetScope: target, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }; + + if (!target.$$listenerCount[name]) return event; + + var listenerArgs = concat([event], arguments, 1), + listeners, i, length; + + //down while you can, then up and next sibling or up and next sibling until back at root + while ((current = next)) { + event.currentScope = current; + listeners = current.$$listeners[name] || []; + for (i = 0, length = listeners.length; i < length; i++) { + // if listeners were deregistered, defragment the array + if (!listeners[i]) { + listeners.splice(i, 1); + i--; + length--; + continue; + } + + try { + listeners[i].apply(null, listenerArgs); + } catch (e) { + $exceptionHandler(e); + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $digest + // (though it differs due to having the extra check for $$listenerCount) + if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || + (current !== target && current.$$nextSibling)))) { + while (current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } + + event.currentScope = null; + return event; + } + }; + + var $rootScope = new Scope(); + + //The internal queues. Expose them on the $rootScope for debugging/testing purposes. + var asyncQueue = $rootScope.$$asyncQueue = []; + var postDigestQueue = $rootScope.$$postDigestQueue = []; + var applyAsyncQueue = $rootScope.$$applyAsyncQueue = []; + + var postDigestQueuePosition = 0; + + return $rootScope; + + + function beginPhase(phase) { + if ($rootScope.$$phase) { + throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase); + } + + $rootScope.$$phase = phase; + } + + function clearPhase() { + $rootScope.$$phase = null; + } + + function incrementWatchersCount(current, count) { + do { + current.$$watchersCount += count; + } while ((current = current.$parent)); + } + + function decrementListenerCount(current, count, name) { + do { + current.$$listenerCount[name] -= count; + + if (current.$$listenerCount[name] === 0) { + delete current.$$listenerCount[name]; + } + } while ((current = current.$parent)); + } + + /** + * function used as an initial value for watchers. + * because it's unique we can easily tell it apart from other values + */ + function initWatchVal() {} + + function flushApplyAsync() { + while (applyAsyncQueue.length) { + try { + applyAsyncQueue.shift()(); + } catch (e) { + $exceptionHandler(e); + } + } + applyAsyncId = null; + } + + function scheduleApplyAsync() { + if (applyAsyncId === null) { + applyAsyncId = $browser.defer(function() { + $rootScope.$apply(flushApplyAsync); + }); + } + } + }]; +} + +/** + * @ngdoc service + * @name $rootElement + * + * @description + * The root element of AngularJS application. This is either the element where {@link + * ng.directive:ngApp ngApp} was declared or the element passed into + * {@link angular.bootstrap}. The element represents the root element of application. It is also the + * location where the application's {@link auto.$injector $injector} service gets + * published, and can be retrieved using `$rootElement.injector()`. + */ + + +// the implementation is in angular.bootstrap + +/** + * @this + * @description + * Private service to sanitize uris for links and images. Used by $compile and $sanitize. + */ +function $$SanitizeUriProvider() { + var aHrefSanitizationWhitelist = /^\s*(https?|s?ftp|mailto|tel|file):/, + imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/; + + /** + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.aHrefSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + aHrefSanitizationWhitelist = regexp; + return this; + } + return aHrefSanitizationWhitelist; + }; + + + /** + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during img[src] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.imgSrcSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + imgSrcSanitizationWhitelist = regexp; + return this; + } + return imgSrcSanitizationWhitelist; + }; + + this.$get = function() { + return function sanitizeUri(uri, isImage) { + var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; + var normalizedVal; + normalizedVal = urlResolve(uri && uri.trim()).href; + if (normalizedVal !== '' && !normalizedVal.match(regex)) { + return 'unsafe:' + normalizedVal; + } + return uri; + }; + }; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* exported $SceProvider, $SceDelegateProvider */ + +var $sceMinErr = minErr('$sce'); + +var SCE_CONTEXTS = { + // HTML is used when there's HTML rendered (e.g. ng-bind-html, iframe srcdoc binding). + HTML: 'html', + + // Style statements or stylesheets. Currently unused in AngularJS. + CSS: 'css', + + // An URL used in a context where it does not refer to a resource that loads code. Currently + // unused in AngularJS. + URL: 'url', + + // RESOURCE_URL is a subtype of URL used where the referred-to resource could be interpreted as + // code. (e.g. ng-include, script src binding, templateUrl) + RESOURCE_URL: 'resourceUrl', + + // Script. Currently unused in AngularJS. + JS: 'js' +}; + +// Helper functions follow. + +var UNDERSCORE_LOWERCASE_REGEXP = /_([a-z])/g; + +function snakeToCamel(name) { + return name + .replace(UNDERSCORE_LOWERCASE_REGEXP, fnCamelCaseReplace); +} + +function adjustMatcher(matcher) { + if (matcher === 'self') { + return matcher; + } else if (isString(matcher)) { + // Strings match exactly except for 2 wildcards - '*' and '**'. + // '*' matches any character except those from the set ':/.?&'. + // '**' matches any character (like .* in a RegExp). + // More than 2 *'s raises an error as it's ill defined. + if (matcher.indexOf('***') > -1) { + throw $sceMinErr('iwcard', + 'Illegal sequence *** in string matcher. String: {0}', matcher); + } + matcher = escapeForRegexp(matcher). + replace(/\\\*\\\*/g, '.*'). + replace(/\\\*/g, '[^:/.?&;]*'); + return new RegExp('^' + matcher + '$'); + } else if (isRegExp(matcher)) { + // The only other type of matcher allowed is a Regexp. + // Match entire URL / disallow partial matches. + // Flags are reset (i.e. no global, ignoreCase or multiline) + return new RegExp('^' + matcher.source + '$'); + } else { + throw $sceMinErr('imatcher', + 'Matchers may only be "self", string patterns or RegExp objects'); + } +} + + +function adjustMatchers(matchers) { + var adjustedMatchers = []; + if (isDefined(matchers)) { + forEach(matchers, function(matcher) { + adjustedMatchers.push(adjustMatcher(matcher)); + }); + } + return adjustedMatchers; +} + + +/** + * @ngdoc service + * @name $sceDelegate + * @kind function + * + * @description + * + * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict + * Contextual Escaping (SCE)} services to AngularJS. + * + * For an overview of this service and the functionnality it provides in AngularJS, see the main + * page for {@link ng.$sce SCE}. The current page is targeted for developers who need to alter how + * SCE works in their application, which shouldn't be needed in most cases. + * + *
    + * AngularJS strongly relies on contextual escaping for the security of bindings: disabling or + * modifying this might cause cross site scripting (XSS) vulnerabilities. For libraries owners, + * changes to this service will also influence users, so be extra careful and document your changes. + *
    + * + * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of + * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is + * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to + * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things + * work because `$sce` delegates to `$sceDelegate` for these operations. + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. + * + * The default instance of `$sceDelegate` should work out of the box with little pain. While you + * can override it completely to change the behavior of `$sce`, the common case would + * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting + * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as + * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist + * $sceDelegateProvider.resourceUrlWhitelist} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + */ + +/** + * @ngdoc provider + * @name $sceDelegateProvider + * @this + * + * @description + * + * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate + * $sceDelegate service}, used as a delegate for {@link ng.$sce Strict Contextual Escaping (SCE)}. + * + * The `$sceDelegateProvider` allows one to get/set the whitelists and blacklists used to ensure + * that the URLs used for sourcing AngularJS templates and other script-running URLs are safe (all + * places that use the `$sce.RESOURCE_URL` context). See + * {@link ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} + * and + * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}, + * + * For the general details about this service in AngularJS, read the main page for {@link ng.$sce + * Strict Contextual Escaping (SCE)}. + * + * **Example**: Consider the following case.
    + * + * - your app is hosted at url `http://myapp.example.com/` + * - but some of your templates are hosted on other domains you control such as + * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc. + * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. + * + * Here is what a secure configuration for this scenario might look like: + * + * ``` + * angular.module('myApp', []).config(function($sceDelegateProvider) { + * $sceDelegateProvider.resourceUrlWhitelist([ + * // Allow same origin resource loads. + * 'self', + * // Allow loading from our assets domain. Notice the difference between * and **. + * 'http://srv*.assets.example.com/**' + * ]); + * + * // The blacklist overrides the whitelist so the open redirect here is blocked. + * $sceDelegateProvider.resourceUrlBlacklist([ + * 'http://myapp.example.com/clickThru**' + * ]); + * }); + * ``` + * Note that an empty whitelist will block every resource URL from being loaded, and will require + * you to manually mark each one as trusted with `$sce.trustAsResourceUrl`. However, templates + * requested by {@link ng.$templateRequest $templateRequest} that are present in + * {@link ng.$templateCache $templateCache} will not go through this check. If you have a mechanism + * to populate your templates in that cache at config time, then it is a good idea to remove 'self' + * from that whitelist. This helps to mitigate the security impact of certain types of issues, like + * for instance attacker-controlled `ng-includes`. + */ + +function $SceDelegateProvider() { + this.SCE_CONTEXTS = SCE_CONTEXTS; + + // Resource URLs can also be trusted by policy. + var resourceUrlWhitelist = ['self'], + resourceUrlBlacklist = []; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlWhitelist + * @kind function + * + * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. + * + * @return {Array} The currently set whitelist array. + * + * @description + * Sets/Gets the whitelist of trusted resource URLs. + * + * The **default value** when no whitelist has been explicitly set is `['self']` allowing only + * same origin resource requests. + * + *
    + * **Note:** the default whitelist of 'self' is not recommended if your app shares its origin + * with other apps! It is a good idea to limit it to only your application's directory. + *
    + */ + this.resourceUrlWhitelist = function(value) { + if (arguments.length) { + resourceUrlWhitelist = adjustMatchers(value); + } + return resourceUrlWhitelist; + }; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlBlacklist + * @kind function + * + * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored.

    + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array.

    + * The typical usage for the blacklist is to **block + * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as + * these would otherwise be trusted but actually return content from the redirected domain. + *

    + * Finally, **the blacklist overrides the whitelist** and has the final say. + * + * @return {Array} The currently set blacklist array. + * + * @description + * Sets/Gets the blacklist of trusted resource URLs. + * + * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there + * is no blacklist.) + */ + + this.resourceUrlBlacklist = function(value) { + if (arguments.length) { + resourceUrlBlacklist = adjustMatchers(value); + } + return resourceUrlBlacklist; + }; + + this.$get = ['$injector', function($injector) { + + var htmlSanitizer = function htmlSanitizer(html) { + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + }; + + if ($injector.has('$sanitize')) { + htmlSanitizer = $injector.get('$sanitize'); + } + + + function matchUrl(matcher, parsedUrl) { + if (matcher === 'self') { + return urlIsSameOrigin(parsedUrl); + } else { + // definitely a regex. See adjustMatchers() + return !!matcher.exec(parsedUrl.href); + } + } + + function isResourceUrlAllowedByPolicy(url) { + var parsedUrl = urlResolve(url.toString()); + var i, n, allowed = false; + // Ensure that at least one item from the whitelist allows this url. + for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { + if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { + allowed = true; + break; + } + } + if (allowed) { + // Ensure that no item from the blacklist blocked this url. + for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { + if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { + allowed = false; + break; + } + } + } + return allowed; + } + + function generateHolderType(Base) { + var holderType = function TrustedValueHolderType(trustedValue) { + this.$$unwrapTrustedValue = function() { + return trustedValue; + }; + }; + if (Base) { + holderType.prototype = new Base(); + } + holderType.prototype.valueOf = function sceValueOf() { + return this.$$unwrapTrustedValue(); + }; + holderType.prototype.toString = function sceToString() { + return this.$$unwrapTrustedValue().toString(); + }; + return holderType; + } + + var trustedValueHolderBase = generateHolderType(), + byType = {}; + + byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); + + /** + * @ngdoc method + * @name $sceDelegate#trustAs + * + * @description + * Returns a trusted representation of the parameter for the specified context. This trusted + * object will later on be used as-is, without any security check, by bindings or directives + * that require this security context. + * For instance, marking a string as trusted for the `$sce.HTML` context will entirely bypass + * the potential `$sanitize` call in corresponding `$sce.HTML` bindings or directives, such as + * `ng-bind-html`. Note that in most cases you won't need to call this function: if you have the + * sanitizer loaded, passing the value itself will render all the HTML that does not pose a + * security risk. + * + * See {@link ng.$sceDelegate#getTrusted getTrusted} for the function that will consume those + * trusted values, and {@link ng.$sce $sce} for general documentation about strict contextual + * escaping. + * + * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`, + * `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`. + * + * @param {*} value The value that should be considered trusted. + * @return {*} A trusted representation of value, that can be used in the given context. + */ + function trustAs(type, trustedValue) { + var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (!Constructor) { + throw $sceMinErr('icontext', + 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', + type, trustedValue); + } + if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') { + return trustedValue; + } + // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting + // mutable objects, we ensure here that the value passed in is actually a string. + if (typeof trustedValue !== 'string') { + throw $sceMinErr('itype', + 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', + type); + } + return new Constructor(trustedValue); + } + + /** + * @ngdoc method + * @name $sceDelegate#valueOf + * + * @description + * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. + * + * If the passed parameter is not a value that had been returned by {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, it must be returned as-is. + * + * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} + * call or anything else. + * @return {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns + * `value` unchanged. + */ + function valueOf(maybeTrusted) { + if (maybeTrusted instanceof trustedValueHolderBase) { + return maybeTrusted.$$unwrapTrustedValue(); + } else { + return maybeTrusted; + } + } + + /** + * @ngdoc method + * @name $sceDelegate#getTrusted + * + * @description + * Takes any input, and either returns a value that's safe to use in the specified context, or + * throws an exception. + * + * In practice, there are several cases. When given a string, this function runs checks + * and sanitization to make it safe without prior assumptions. When given the result of a {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call, it returns the originally supplied + * value if that value's context is valid for this call's context. Finally, this function can + * also throw when there is no way to turn `maybeTrusted` in a safe value (e.g., no sanitization + * is available or possible.) + * + * @param {string} type The context in which this value is to be used (such as `$sce.HTML`). + * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} call, or anything else (which will not be considered trusted.) + * @return {*} A version of the value that's safe to use in the given context, or throws an + * exception if this is impossible. + */ + function getTrusted(type, maybeTrusted) { + if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') { + return maybeTrusted; + } + var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + // If maybeTrusted is a trusted class instance or subclass instance, then unwrap and return + // as-is. + if (constructor && maybeTrusted instanceof constructor) { + return maybeTrusted.$$unwrapTrustedValue(); + } + // Otherwise, if we get here, then we may either make it safe, or throw an exception. This + // depends on the context: some are sanitizatible (HTML), some use whitelists (RESOURCE_URL), + // some are impossible to do (JS). This step isn't implemented for CSS and URL, as AngularJS + // has no corresponding sinks. + if (type === SCE_CONTEXTS.RESOURCE_URL) { + // RESOURCE_URL uses a whitelist. + if (isResourceUrlAllowedByPolicy(maybeTrusted)) { + return maybeTrusted; + } else { + throw $sceMinErr('insecurl', + 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', + maybeTrusted.toString()); + } + } else if (type === SCE_CONTEXTS.HTML) { + // htmlSanitizer throws its own error when no sanitizer is available. + return htmlSanitizer(maybeTrusted); + } + // Default error when the $sce service has no way to make the input safe. + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + } + + return { trustAs: trustAs, + getTrusted: getTrusted, + valueOf: valueOf }; + }]; +} + + +/** + * @ngdoc provider + * @name $sceProvider + * @this + * + * @description + * + * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. + * - enable/disable Strict Contextual Escaping (SCE) in a module + * - override the default implementation with a custom delegate + * + * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. + */ + +/** + * @ngdoc service + * @name $sce + * @kind function + * + * @description + * + * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. + * + * ## Strict Contextual Escaping + * + * Strict Contextual Escaping (SCE) is a mode in which AngularJS constrains bindings to only render + * trusted values. Its goal is to assist in writing code in a way that (a) is secure by default, and + * (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier. + * + * ### Overview + * + * To systematically block XSS security bugs, AngularJS treats all values as untrusted by default in + * HTML or sensitive URL bindings. When binding untrusted values, AngularJS will automatically + * run security checks on them (sanitizations, whitelists, depending on context), or throw when it + * cannot guarantee the security of the result. That behavior depends strongly on contexts: HTML + * can be sanitized, but template URLs cannot, for instance. + * + * To illustrate this, consider the `ng-bind-html` directive. It renders its value directly as HTML: + * we call that the *context*. When given an untrusted input, AngularJS will attempt to sanitize it + * before rendering if a sanitizer is available, and throw otherwise. To bypass sanitization and + * render the input as-is, you will need to mark it as trusted for that context before attempting + * to bind it. + * + * As of version 1.2, AngularJS ships with SCE enabled by default. + * + * ### In practice + * + * Here's an example of a binding in a privileged context: + * + * ``` + * + *

    + * ``` + * + * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE + * disabled, this application allows the user to render arbitrary HTML into the DIV, which would + * be an XSS security bug. In a more realistic example, one may be rendering user comments, blog + * articles, etc. via bindings. (HTML is just one example of a context where rendering user + * controlled input creates security vulnerabilities.) + * + * For the case of HTML, you might use a library, either on the client side, or on the server side, + * to sanitize unsafe HTML before binding to the value and rendering it in the document. + * + * How would you ensure that every place that used these types of bindings was bound to a value that + * was sanitized by your library (or returned as safe for rendering by your server?) How can you + * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some + * properties/fields and forgot to update the binding to the sanitized value? + * + * To be secure by default, AngularJS makes sure bindings go through that sanitization, or + * any similar validation process, unless there's a good reason to trust the given value in this + * context. That trust is formalized with a function call. This means that as a developer, you + * can assume all untrusted bindings are safe. Then, to audit your code for binding security issues, + * you just need to ensure the values you mark as trusted indeed are safe - because they were + * received from your server, sanitized by your library, etc. You can organize your codebase to + * help with this - perhaps allowing only the files in a specific directory to do this. + * Ensuring that the internal API exposed by that code doesn't markup arbitrary values as safe then + * becomes a more manageable task. + * + * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} + * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to + * build the trusted versions of your values. + * + * ### How does it work? + * + * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted + * $sce.getTrusted(context, value)} rather than to the value directly. Think of this function as + * a way to enforce the required security context in your data sink. Directives use {@link + * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs + * the {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. Also, + * when binding without directives, AngularJS will understand the context of your bindings + * automatically. + * + * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link + * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly + * simplified): + * + * ``` + * var ngBindHtmlDirective = ['$sce', function($sce) { + * return function(scope, element, attr) { + * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { + * element.html(value || ''); + * }); + * }; + * }]; + * ``` + * + * ### Impact on loading templates + * + * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as + * `templateUrl`'s specified by {@link guide/directive directives}. + * + * By default, AngularJS only loads templates from the same domain and protocol as the application + * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or + * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist + * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. + * + * *Please note*: + * The browser's + * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) + * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) + * policy apply in addition to this and may further restrict whether the template is successfully + * loaded. This means that without the right CORS policy, loading templates from a different domain + * won't work on all browsers. Also, loading templates from `file://` URL does not work on some + * browsers. + * + * ### This feels like too much overhead + * + * It's important to remember that SCE only applies to interpolation expressions. + * + * If your expressions are constant literals, they're automatically trusted and you don't need to + * call `$sce.trustAs` on them (e.g. + * `
    `) just works. The `$sceDelegate` will + * also use the `$sanitize` service if it is available when binding untrusted values to + * `$sce.HTML` context. AngularJS provides an implementation in `angular-sanitize.js`, and if you + * wish to use it, you will also need to depend on the {@link ngSanitize `ngSanitize`} module in + * your application. + * + * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load + * templates in `ng-include` from your application's domain without having to even know about SCE. + * It blocks loading templates from other domains or loading templates over http from an https + * served document. You can change these by setting your own custom {@link + * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. + * + * This significantly reduces the overhead. It is far easier to pay the small overhead and have an + * application that's secure and can be audited to verify that with much more ease than bolting + * security onto an application later. + * + * + * ### What trusted context types are supported? + * + * | Context | Notes | + * |---------------------|----------------| + * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered, and the {@link ngSanitize.$sanitize $sanitize} service is available (implemented by the {@link ngSanitize ngSanitize} module) this will sanitize the value instead of throwing an error. | + * | `$sce.CSS` | For CSS that's safe to source into the application. Currently, no bindings require this context. Feel free to use it in your own directives. | + * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`
    Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does (it's not just the URL that matters, but also what is at the end of it), and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | + * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently, no bindings require this context. Feel free to use it in your own directives. | + * + * + * Be aware that `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them + * through {@link ng.$sce#getTrusted $sce.getTrusted}. There's no CSS-, URL-, or JS-context bindings + * in AngularJS currently, so their corresponding `$sce.trustAs` functions aren't useful yet. This + * might evolve. + * + * ### Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist}
    + * + * Each element in these arrays must be one of the following: + * + * - **'self'** + * - The special **string**, `'self'`, can be used to match against all URLs of the **same + * domain** as the application document using the **same protocol**. + * - **String** (except the special value `'self'`) + * - The string is matched against the full *normalized / absolute URL* of the resource + * being tested (substring matches are not good enough.) + * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters + * match themselves. + * - `*`: matches zero or more occurrences of any character other than one of the following 6 + * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use + * in a whitelist. + * - `**`: matches zero or more occurrences of *any* character. As such, it's not + * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g. + * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might + * not have been the intention.) Its usage at the very end of the path is ok. (e.g. + * http://foo.example.com/templates/**). + * - **RegExp** (*see caveat below*) + * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax + * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to + * accidentally introduce a bug when one updates a complex expression (imho, all regexes should + * have good test coverage). For instance, the use of `.` in the regex is correct only in a + * small number of cases. A `.` character in the regex used when matching the scheme or a + * subdomain could be matched against a `:` or literal `.` that was likely not intended. It + * is highly recommended to use the string patterns and only fall back to regular expressions + * as a last resort. + * - The regular expression must be an instance of RegExp (i.e. not a string.) It is + * matched against the **entire** *normalized / absolute URL* of the resource being tested + * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags + * present on the RegExp (such as multiline, global, ignoreCase) are ignored. + * - If you are generating your JavaScript from some other templating engine (not + * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), + * remember to escape your regular expression (and be aware that you might need more than + * one level of escaping depending on your templating engine and the way you interpolated + * the value.) Do make use of your platform's escaping mechanism as it might be good + * enough before coding your own. E.g. Ruby has + * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) + * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). + * Javascript lacks a similar built in function for escaping. Take a look at Google + * Closure library's [goog.string.regExpEscape(s)]( + * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. + * + * ### Show me an example using SCE. + * + * + * + *
    + *

    + * User comments
    + * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when + * $sanitize is available. If $sanitize isn't available, this results in an error instead of an + * exploit. + *
    + *
    + * {{userComment.name}}: + * + *
    + *
    + *
    + *
    + *
    + * + * + * angular.module('mySceApp', ['ngSanitize']) + * .controller('AppController', ['$http', '$templateCache', '$sce', + * function AppController($http, $templateCache, $sce) { + * var self = this; + * $http.get('test_data.json', {cache: $templateCache}).then(function(response) { + * self.userComments = response.data; + * }); + * self.explicitlyTrustedHtml = $sce.trustAsHtml( + * 'Hover over this text.'); + * }]); + * + * + * + * [ + * { "name": "Alice", + * "htmlComment": + * "Is anyone reading this?" + * }, + * { "name": "Bob", + * "htmlComment": "Yes! Am I the only other one?" + * } + * ] + * + * + * + * describe('SCE doc demo', function() { + * it('should sanitize untrusted values', function() { + * expect(element.all(by.css('.htmlComment')).first().getAttribute('innerHTML')) + * .toBe('Is anyone reading this?'); + * }); + * + * it('should NOT sanitize explicitly trusted values', function() { + * expect(element(by.id('explicitlyTrustedHtml')).getAttribute('innerHTML')).toBe( + * 'Hover over this text.'); + * }); + * }); + * + *
    + * + * + * + * ## Can I disable SCE completely? + * + * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits + * for little coding overhead. It will be much harder to take an SCE disabled application and + * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE + * for cases where you have a lot of existing code that was written before SCE was introduced and + * you're migrating them a module at a time. Also do note that this is an app-wide setting, so if + * you are writing a library, you will cause security bugs applications using it. + * + * That said, here's how you can completely disable SCE: + * + * ``` + * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { + * // Completely disable SCE. For demonstration purposes only! + * // Do not use in new projects or libraries. + * $sceProvider.enabled(false); + * }); + * ``` + * + */ + +function $SceProvider() { + var enabled = true; + + /** + * @ngdoc method + * @name $sceProvider#enabled + * @kind function + * + * @param {boolean=} value If provided, then enables/disables SCE application-wide. + * @return {boolean} True if SCE is enabled, false otherwise. + * + * @description + * Enables/disables SCE and returns the current value. + */ + this.enabled = function(value) { + if (arguments.length) { + enabled = !!value; + } + return enabled; + }; + + + /* Design notes on the default implementation for SCE. + * + * The API contract for the SCE delegate + * ------------------------------------- + * The SCE delegate object must provide the following 3 methods: + * + * - trustAs(contextEnum, value) + * This method is used to tell the SCE service that the provided value is OK to use in the + * contexts specified by contextEnum. It must return an object that will be accepted by + * getTrusted() for a compatible contextEnum and return this value. + * + * - valueOf(value) + * For values that were not produced by trustAs(), return them as is. For values that were + * produced by trustAs(), return the corresponding input value to trustAs. Basically, if + * trustAs is wrapping the given values into some type, this operation unwraps it when given + * such a value. + * + * - getTrusted(contextEnum, value) + * This function should return the a value that is safe to use in the context specified by + * contextEnum or throw and exception otherwise. + * + * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be + * opaque or wrapped in some holder object. That happens to be an implementation detail. For + * instance, an implementation could maintain a registry of all trusted objects by context. In + * such a case, trustAs() would return the same object that was passed in. getTrusted() would + * return the same object passed in if it was found in the registry under a compatible context or + * throw an exception otherwise. An implementation might only wrap values some of the time based + * on some criteria. getTrusted() might return a value and not throw an exception for special + * constants or objects even if not wrapped. All such implementations fulfill this contract. + * + * + * A note on the inheritance model for SCE contexts + * ------------------------------------------------ + * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This + * is purely an implementation details. + * + * The contract is simply this: + * + * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) + * will also succeed. + * + * Inheritance happens to capture this in a natural way. In some future, we may not use + * inheritance anymore. That is OK because no code outside of sce.js and sceSpecs.js would need to + * be aware of this detail. + */ + + this.$get = ['$parse', '$sceDelegate', function( + $parse, $sceDelegate) { + // Support: IE 9-11 only + // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow + // the "expression(javascript expression)" syntax which is insecure. + if (enabled && msie < 8) { + throw $sceMinErr('iequirks', + 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' + + 'mode. You can fix this by adding the text to the top of your HTML ' + + 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); + } + + var sce = shallowCopy(SCE_CONTEXTS); + + /** + * @ngdoc method + * @name $sce#isEnabled + * @kind function + * + * @return {Boolean} True if SCE is enabled, false otherwise. If you want to set the value, you + * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. + * + * @description + * Returns a boolean indicating if SCE is enabled. + */ + sce.isEnabled = function() { + return enabled; + }; + sce.trustAs = $sceDelegate.trustAs; + sce.getTrusted = $sceDelegate.getTrusted; + sce.valueOf = $sceDelegate.valueOf; + + if (!enabled) { + sce.trustAs = sce.getTrusted = function(type, value) { return value; }; + sce.valueOf = identity; + } + + /** + * @ngdoc method + * @name $sce#parseAs + * + * @description + * Converts AngularJS {@link guide/expression expression} into a function. This is like {@link + * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it + * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, + * *result*)} + * + * @param {string} type The SCE context in which this result will be used. + * @param {string} expression String expression to compile. + * @return {function(context, locals)} A function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. + */ + sce.parseAs = function sceParseAs(type, expr) { + var parsed = $parse(expr); + if (parsed.literal && parsed.constant) { + return parsed; + } else { + return $parse(expr, function(value) { + return sce.getTrusted(type, value); + }); + } + }; + + /** + * @ngdoc method + * @name $sce#trustAs + * + * @description + * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns a + * wrapped object that represents your value, and the trust you have in its safety for the given + * context. AngularJS can then use that value as-is in bindings of the specified secure context. + * This is used in bindings for `ng-bind-html`, `ng-include`, and most `src` attribute + * interpolations. See {@link ng.$sce $sce} for strict contextual escaping. + * + * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`, + * `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`. + * + * @param {*} value The value that that should be considered trusted. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in the context you specified. + */ + + /** + * @ngdoc method + * @name $sce#trustAsHtml + * + * @description + * Shorthand method. `$sce.trustAsHtml(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} + * + * @param {*} value The value to mark as trusted for `$sce.HTML` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.HTML` context (like `ng-bind-html`). + */ + + /** + * @ngdoc method + * @name $sce#trustAsCss + * + * @description + * Shorthand method. `$sce.trustAsCss(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.CSS, value)`} + * + * @param {*} value The value to mark as trusted for `$sce.CSS` context. + * @return {*} A wrapped version of value that can be used as a trusted variant + * of your `value` in `$sce.CSS` context. This context is currently unused, so there are + * almost no reasons to use this function so far. + */ + + /** + * @ngdoc method + * @name $sce#trustAsUrl + * + * @description + * Shorthand method. `$sce.trustAsUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} + * + * @param {*} value The value to mark as trusted for `$sce.URL` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.URL` context. That context is currently unused, so there are almost no reasons + * to use this function so far. + */ + + /** + * @ngdoc method + * @name $sce#trustAsResourceUrl + * + * @description + * Shorthand method. `$sce.trustAsResourceUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to mark as trusted for `$sce.RESOURCE_URL` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.RESOURCE_URL` context (template URLs in `ng-include`, most `src` attribute + * bindings, ...) + */ + + /** + * @ngdoc method + * @name $sce#trustAsJs + * + * @description + * Shorthand method. `$sce.trustAsJs(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} + * + * @param {*} value The value to mark as trusted for `$sce.JS` context. + * @return {*} A wrapped version of value that can be used as a trusted variant of your `value` + * in `$sce.JS` context. That context is currently unused, so there are almost no reasons to + * use this function so far. + */ + + /** + * @ngdoc method + * @name $sce#getTrusted + * + * @description + * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, + * takes any input, and either returns a value that's safe to use in the specified context, + * or throws an exception. This function is aware of trusted values created by the `trustAs` + * function and its shorthands, and when contexts are appropriate, returns the unwrapped value + * as-is. Finally, this function can also throw when there is no way to turn `maybeTrusted` in a + * safe value (e.g., no sanitization is available or possible.) + * + * @param {string} type The context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs + * `$sce.trustAs`} call, or anything else (which will not be considered trusted.) + * @return {*} A version of the value that's safe to use in the given context, or throws an + * exception if this is impossible. + */ + + /** + * @ngdoc method + * @name $sce#getTrustedHtml + * + * @description + * Shorthand method. `$sce.getTrustedHtml(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @return {*} The return value of `$sce.getTrusted($sce.HTML, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedCss + * + * @description + * Shorthand method. `$sce.getTrustedCss(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @return {*} The return value of `$sce.getTrusted($sce.CSS, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedUrl + * + * @description + * Shorthand method. `$sce.getTrustedUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @return {*} The return value of `$sce.getTrusted($sce.URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedResourceUrl + * + * @description + * Shorthand method. `$sce.getTrustedResourceUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to pass to `$sceDelegate.getTrusted`. + * @return {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedJs + * + * @description + * Shorthand method. `$sce.getTrustedJs(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @return {*} The return value of `$sce.getTrusted($sce.JS, value)` + */ + + /** + * @ngdoc method + * @name $sce#parseAsHtml + * + * @description + * Shorthand method. `$sce.parseAsHtml(expression string)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} + * + * @param {string} expression String expression to compile. + * @return {function(context, locals)} A function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsCss + * + * @description + * Shorthand method. `$sce.parseAsCss(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} + * + * @param {string} expression String expression to compile. + * @return {function(context, locals)} A function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsUrl + * + * @description + * Shorthand method. `$sce.parseAsUrl(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} + * + * @param {string} expression String expression to compile. + * @return {function(context, locals)} A function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsResourceUrl + * + * @description + * Shorthand method. `$sce.parseAsResourceUrl(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} + * + * @param {string} expression String expression to compile. + * @return {function(context, locals)} A function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsJs + * + * @description + * Shorthand method. `$sce.parseAsJs(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} + * + * @param {string} expression String expression to compile. + * @return {function(context, locals)} A function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the + * strings are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values + * in `context`. + */ + + // Shorthand delegations. + var parse = sce.parseAs, + getTrusted = sce.getTrusted, + trustAs = sce.trustAs; + + forEach(SCE_CONTEXTS, function(enumValue, name) { + var lName = lowercase(name); + sce[snakeToCamel('parse_as_' + lName)] = function(expr) { + return parse(enumValue, expr); + }; + sce[snakeToCamel('get_trusted_' + lName)] = function(value) { + return getTrusted(enumValue, value); + }; + sce[snakeToCamel('trust_as_' + lName)] = function(value) { + return trustAs(enumValue, value); + }; + }); + + return sce; + }]; +} + +/* exported $SnifferProvider */ + +/** + * !!! This is an undocumented "private" service !!! + * + * @name $sniffer + * @requires $window + * @requires $document + * @this + * + * @property {boolean} history Does the browser support html5 history api ? + * @property {boolean} transitions Does the browser support CSS transition events ? + * @property {boolean} animations Does the browser support CSS animation events ? + * + * @description + * This is very simple implementation of testing browser's features. + */ +function $SnifferProvider() { + this.$get = ['$window', '$document', function($window, $document) { + var eventSupport = {}, + // Chrome Packaged Apps are not allowed to access `history.pushState`. + // If not sandboxed, they can be detected by the presence of `chrome.app.runtime` + // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by + // the presence of an extension runtime ID and the absence of other Chrome runtime APIs + // (see https://developer.chrome.com/apps/manifest/sandbox). + // (NW.js apps have access to Chrome APIs, but do support `history`.) + isNw = $window.nw && $window.nw.process, + isChromePackagedApp = + !isNw && + $window.chrome && + ($window.chrome.app && $window.chrome.app.runtime || + !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id), + hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState, + android = + toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), + boxee = /Boxee/i.test(($window.navigator || {}).userAgent), + document = $document[0] || {}, + bodyStyle = document.body && document.body.style, + transitions = false, + animations = false; + + if (bodyStyle) { + // Support: Android <5, Blackberry Browser 10, default Chrome in Android 4.4.x + // Mentioned browsers need a -webkit- prefix for transitions & animations. + transitions = !!('transition' in bodyStyle || 'webkitTransition' in bodyStyle); + animations = !!('animation' in bodyStyle || 'webkitAnimation' in bodyStyle); + } + + + return { + // Android has history.pushState, but it does not update location correctly + // so let's not use the history API at all. + // http://code.google.com/p/android/issues/detail?id=17471 + // https://github.com/angular/angular.js/issues/904 + + // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has + // so let's not use the history API also + // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined + history: !!(hasHistoryPushState && !(android < 4) && !boxee), + hasEvent: function(event) { + // Support: IE 9-11 only + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + // IE10+ implements 'input' event but it erroneously fires under various situations, + // e.g. when placeholder changes, or a form is focused. + if (event === 'input' && msie) return false; + + if (isUndefined(eventSupport[event])) { + var divElm = document.createElement('div'); + eventSupport[event] = 'on' + event in divElm; + } + + return eventSupport[event]; + }, + csp: csp(), + transitions: transitions, + animations: animations, + android: android + }; + }]; +} + +var $templateRequestMinErr = minErr('$compile'); + +/** + * @ngdoc provider + * @name $templateRequestProvider + * @this + * + * @description + * Used to configure the options passed to the {@link $http} service when making a template request. + * + * For example, it can be used for specifying the "Accept" header that is sent to the server, when + * requesting a template. + */ +function $TemplateRequestProvider() { + + var httpOptions; + + /** + * @ngdoc method + * @name $templateRequestProvider#httpOptions + * @description + * The options to be passed to the {@link $http} service when making the request. + * You can use this to override options such as the "Accept" header for template requests. + * + * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the + * options if not overridden here. + * + * @param {string=} value new value for the {@link $http} options. + * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter. + */ + this.httpOptions = function(val) { + if (val) { + httpOptions = val; + return this; + } + return httpOptions; + }; + + /** + * @ngdoc service + * @name $templateRequest + * + * @description + * The `$templateRequest` service runs security checks then downloads the provided template using + * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request + * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the + * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the + * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted + * when `tpl` is of type string and `$templateCache` has the matching entry. + * + * If you want to pass custom options to the `$http` service, such as setting the Accept header you + * can configure this via {@link $templateRequestProvider#httpOptions}. + * + * `$templateRequest` is used internally by {@link $compile}, {@link ngRoute.$route}, and directives such + * as {@link ngInclude} to download and cache templates. + * + * 3rd party modules should use `$templateRequest` if their services or directives are loading + * templates. + * + * @param {string|TrustedResourceUrl} tpl The HTTP request template URL + * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty + * + * @return {Promise} a promise for the HTTP response data of the given URL. + * + * @property {number} totalPendingRequests total amount of pending template requests being downloaded. + */ + this.$get = ['$exceptionHandler', '$templateCache', '$http', '$q', '$sce', + function($exceptionHandler, $templateCache, $http, $q, $sce) { + + function handleRequestFn(tpl, ignoreRequestError) { + handleRequestFn.totalPendingRequests++; + + // We consider the template cache holds only trusted templates, so + // there's no need to go through whitelisting again for keys that already + // are included in there. This also makes AngularJS accept any script + // directive, no matter its name. However, we still need to unwrap trusted + // types. + if (!isString(tpl) || isUndefined($templateCache.get(tpl))) { + tpl = $sce.getTrustedResourceUrl(tpl); + } + + var transformResponse = $http.defaults && $http.defaults.transformResponse; + + if (isArray(transformResponse)) { + transformResponse = transformResponse.filter(function(transformer) { + return transformer !== defaultHttpResponseTransform; + }); + } else if (transformResponse === defaultHttpResponseTransform) { + transformResponse = null; + } + + return $http.get(tpl, extend({ + cache: $templateCache, + transformResponse: transformResponse + }, httpOptions)) + .finally(function() { + handleRequestFn.totalPendingRequests--; + }) + .then(function(response) { + $templateCache.put(tpl, response.data); + return response.data; + }, handleError); + + function handleError(resp) { + if (!ignoreRequestError) { + resp = $templateRequestMinErr('tpload', + 'Failed to load template: {0} (HTTP status: {1} {2})', + tpl, resp.status, resp.statusText); + + $exceptionHandler(resp); + } + + return $q.reject(resp); + } + } + + handleRequestFn.totalPendingRequests = 0; + + return handleRequestFn; + } + ]; +} + +/** @this */ +function $$TestabilityProvider() { + this.$get = ['$rootScope', '$browser', '$location', + function($rootScope, $browser, $location) { + + /** + * @name $testability + * + * @description + * The private $$testability service provides a collection of methods for use when debugging + * or by automated test and debugging tools. + */ + var testability = {}; + + /** + * @name $$testability#findBindings + * + * @description + * Returns an array of elements that are bound (via ng-bind or {{}}) + * to expressions matching the input. + * + * @param {Element} element The element root to search from. + * @param {string} expression The binding expression to match. + * @param {boolean} opt_exactMatch If true, only returns exact matches + * for the expression. Filters and whitespace are ignored. + */ + testability.findBindings = function(element, expression, opt_exactMatch) { + var bindings = element.getElementsByClassName('ng-binding'); + var matches = []; + forEach(bindings, function(binding) { + var dataBinding = angular.element(binding).data('$binding'); + if (dataBinding) { + forEach(dataBinding, function(bindingName) { + if (opt_exactMatch) { + var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)'); + if (matcher.test(bindingName)) { + matches.push(binding); + } + } else { + if (bindingName.indexOf(expression) !== -1) { + matches.push(binding); + } + } + }); + } + }); + return matches; + }; + + /** + * @name $$testability#findModels + * + * @description + * Returns an array of elements that are two-way found via ng-model to + * expressions matching the input. + * + * @param {Element} element The element root to search from. + * @param {string} expression The model expression to match. + * @param {boolean} opt_exactMatch If true, only returns exact matches + * for the expression. + */ + testability.findModels = function(element, expression, opt_exactMatch) { + var prefixes = ['ng-', 'data-ng-', 'ng\\:']; + for (var p = 0; p < prefixes.length; ++p) { + var attributeEquals = opt_exactMatch ? '=' : '*='; + var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]'; + var elements = element.querySelectorAll(selector); + if (elements.length) { + return elements; + } + } + }; + + /** + * @name $$testability#getLocation + * + * @description + * Shortcut for getting the location in a browser agnostic way. Returns + * the path, search, and hash. (e.g. /path?a=b#hash) + */ + testability.getLocation = function() { + return $location.url(); + }; + + /** + * @name $$testability#setLocation + * + * @description + * Shortcut for navigating to a location without doing a full page reload. + * + * @param {string} url The location url (path, search and hash, + * e.g. /path?a=b#hash) to go to. + */ + testability.setLocation = function(url) { + if (url !== $location.url()) { + $location.url(url); + $rootScope.$digest(); + } + }; + + /** + * @name $$testability#whenStable + * + * @description + * Calls the callback when $timeout and $http requests are completed. + * + * @param {function} callback + */ + testability.whenStable = function(callback) { + $browser.notifyWhenNoOutstandingRequests(callback); + }; + + return testability; + }]; +} + +/** @this */ +function $TimeoutProvider() { + this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler', + function($rootScope, $browser, $q, $$q, $exceptionHandler) { + + var deferreds = {}; + + + /** + * @ngdoc service + * @name $timeout + * + * @description + * AngularJS's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch + * block and delegates any exceptions to + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * The return value of calling `$timeout` is a promise, which will be resolved when + * the delay has passed and the timeout function, if provided, is executed. + * + * To cancel a timeout request, call `$timeout.cancel(promise)`. + * + * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to + * synchronously flush the queue of deferred functions. + * + * If you only want a promise that will be resolved after some specified delay + * then you can call `$timeout` without the `fn` function. + * + * @param {function()=} fn A function, whose execution should be delayed. + * @param {number=} [delay=0] Delay in milliseconds. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @param {...*=} Pass additional parameters to the executed function. + * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise + * will be resolved with the return value of the `fn` function. + * + */ + function timeout(fn, delay, invokeApply) { + if (!isFunction(fn)) { + invokeApply = delay; + delay = fn; + fn = noop; + } + + var args = sliceArgs(arguments, 3), + skipApply = (isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise, + timeoutId; + + timeoutId = $browser.defer(function() { + try { + deferred.resolve(fn.apply(null, args)); + } catch (e) { + deferred.reject(e); + $exceptionHandler(e); + } finally { + delete deferreds[promise.$$timeoutId]; + } + + if (!skipApply) $rootScope.$apply(); + }, delay); + + promise.$$timeoutId = timeoutId; + deferreds[timeoutId] = deferred; + + return promise; + } + + + /** + * @ngdoc method + * @name $timeout#cancel + * + * @description + * Cancels a task associated with the `promise`. As a result of this, the promise will be + * resolved with a rejection. + * + * @param {Promise=} promise Promise returned by the `$timeout` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + timeout.cancel = function(promise) { + if (promise && promise.$$timeoutId in deferreds) { + // Timeout cancels should not report an unhandled promise. + markQExceptionHandled(deferreds[promise.$$timeoutId].promise); + deferreds[promise.$$timeoutId].reject('canceled'); + delete deferreds[promise.$$timeoutId]; + return $browser.defer.cancel(promise.$$timeoutId); + } + return false; + }; + + return timeout; + }]; +} + +// NOTE: The usage of window and document instead of $window and $document here is +// deliberate. This service depends on the specific behavior of anchor nodes created by the +// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and +// cause us to break tests. In addition, when the browser resolves a URL for XHR, it +// doesn't know about mocked locations and resolves URLs to the real document - which is +// exactly the behavior needed here. There is little value is mocking these out for this +// service. +var urlParsingNode = window.document.createElement('a'); +var originUrl = urlResolve(window.location.href); + + +/** + * + * Implementation Notes for non-IE browsers + * ---------------------------------------- + * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, + * results both in the normalizing and parsing of the URL. Normalizing means that a relative + * URL will be resolved into an absolute URL in the context of the application document. + * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related + * properties are all populated to reflect the normalized URL. This approach has wide + * compatibility - Safari 1+, Mozilla 1+ etc. See + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * + * Implementation Notes for IE + * --------------------------- + * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other + * browsers. However, the parsed components will not be set if the URL assigned did not specify + * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We + * work around that by performing the parsing in a 2nd step by taking a previously normalized + * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the + * properties such as protocol, hostname, port, etc. + * + * References: + * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * http://url.spec.whatwg.org/#urlutils + * https://github.com/angular/angular.js/pull/2902 + * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ + * + * @kind function + * @param {string} url The URL to be parsed. + * @description Normalizes and parses a URL. + * @returns {object} Returns the normalized URL as a dictionary. + * + * | member name | Description | + * |---------------|----------------| + * | href | A normalized version of the provided URL if it was not an absolute URL | + * | protocol | The protocol including the trailing colon | + * | host | The host and port (if the port is non-default) of the normalizedUrl | + * | search | The search params, minus the question mark | + * | hash | The hash string, minus the hash symbol + * | hostname | The hostname + * | port | The port, without ":" + * | pathname | The pathname, beginning with "/" + * + */ +function urlResolve(url) { + var href = url; + + // Support: IE 9-11 only + if (msie) { + // Normalize before parse. Refer Implementation Notes on why this is + // done in two steps on IE. + urlParsingNode.setAttribute('href', href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') + ? urlParsingNode.pathname + : '/' + urlParsingNode.pathname + }; +} + +/** + * Parse a request URL and determine whether this is a same-origin request as the application document. + * + * @param {string|object} requestUrl The url of the request as a string that will be resolved + * or a parsed URL object. + * @returns {boolean} Whether the request is for the same origin as the application document. + */ +function urlIsSameOrigin(requestUrl) { + var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; + return (parsed.protocol === originUrl.protocol && + parsed.host === originUrl.host); +} + +/** + * @ngdoc service + * @name $window + * @this + * + * @description + * A reference to the browser's `window` object. While `window` + * is globally available in JavaScript, it causes testability problems, because + * it is a global variable. In AngularJS we always refer to it through the + * `$window` service, so it may be overridden, removed or mocked for testing. + * + * Expressions, like the one defined for the `ngClick` directive in the example + * below, are evaluated with respect to the current scope. Therefore, there is + * no risk of inadvertently coding in a dependency on a global value in such an + * expression. + * + * @example + + + +
    + + +
    +
    + + it('should display the greeting in the input box', function() { + element(by.model('greeting')).sendKeys('Hello, E2E Tests'); + // If we click the button it will block the test runner + // element(':button').click(); + }); + +
    + */ +function $WindowProvider() { + this.$get = valueFn(window); +} + +/** + * @name $$cookieReader + * @requires $document + * + * @description + * This is a private service for reading cookies used by $http and ngCookies + * + * @return {Object} a key/value map of the current cookies + */ +function $$CookieReader($document) { + var rawDocument = $document[0] || {}; + var lastCookies = {}; + var lastCookieString = ''; + + function safeGetCookie(rawDocument) { + try { + return rawDocument.cookie || ''; + } catch (e) { + return ''; + } + } + + function safeDecodeURIComponent(str) { + try { + return decodeURIComponent(str); + } catch (e) { + return str; + } + } + + return function() { + var cookieArray, cookie, i, index, name; + var currentCookieString = safeGetCookie(rawDocument); + + if (currentCookieString !== lastCookieString) { + lastCookieString = currentCookieString; + cookieArray = lastCookieString.split('; '); + lastCookies = {}; + + for (i = 0; i < cookieArray.length; i++) { + cookie = cookieArray[i]; + index = cookie.indexOf('='); + if (index > 0) { //ignore nameless cookies + name = safeDecodeURIComponent(cookie.substring(0, index)); + // the first value that is seen for a cookie is the most + // specific one. values for the same cookie name that + // follow are for less specific paths. + if (isUndefined(lastCookies[name])) { + lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1)); + } + } + } + } + return lastCookies; + }; +} + +$$CookieReader.$inject = ['$document']; + +/** @this */ +function $$CookieReaderProvider() { + this.$get = $$CookieReader; +} + +/* global currencyFilter: true, + dateFilter: true, + filterFilter: true, + jsonFilter: true, + limitToFilter: true, + lowercaseFilter: true, + numberFilter: true, + orderByFilter: true, + uppercaseFilter: true, + */ + +/** + * @ngdoc provider + * @name $filterProvider + * @description + * + * Filters are just functions which transform input to an output. However filters need to be + * Dependency Injected. To achieve this a filter definition consists of a factory function which is + * annotated with dependencies and is responsible for creating a filter function. + * + *
    + * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`. + * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace + * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores + * (`myapp_subsection_filterx`). + *
    + * + * ```js + * // Filter registration + * function MyModule($provide, $filterProvider) { + * // create a service to demonstrate injection (not always needed) + * $provide.value('greet', function(name){ + * return 'Hello ' + name + '!'; + * }); + * + * // register a filter factory which uses the + * // greet service to demonstrate DI. + * $filterProvider.register('greet', function(greet){ + * // return the filter function which uses the greet service + * // to generate salutation + * return function(text) { + * // filters need to be forgiving so check input validity + * return text && greet(text) || text; + * }; + * }); + * } + * ``` + * + * The filter function is registered with the `$injector` under the filter name suffix with + * `Filter`. + * + * ```js + * it('should be the same instance', inject( + * function($filterProvider) { + * $filterProvider.register('reverse', function(){ + * return ...; + * }); + * }, + * function($filter, reverseFilter) { + * expect($filter('reverse')).toBe(reverseFilter); + * }); + * ``` + * + * + * For more information about how AngularJS filters work, and how to create your own filters, see + * {@link guide/filter Filters} in the AngularJS Developer Guide. + */ + +/** + * @ngdoc service + * @name $filter + * @kind function + * @description + * Filters are used for formatting data displayed to the user. + * + * They can be used in view templates, controllers or services. AngularJS comes + * with a collection of [built-in filters](api/ng/filter), but it is easy to + * define your own as well. + * + * The general syntax in templates is as follows: + * + * ```html + * {{ expression [| filter_name[:parameter_value] ... ] }} + * ``` + * + * @param {String} name Name of the filter function to retrieve + * @return {Function} the filter function + * @example + + +
    +

    {{ originalText }}

    +

    {{ filteredText }}

    +
    +
    + + + angular.module('filterExample', []) + .controller('MainCtrl', function($scope, $filter) { + $scope.originalText = 'hello'; + $scope.filteredText = $filter('uppercase')($scope.originalText); + }); + +
    + */ +$FilterProvider.$inject = ['$provide']; +/** @this */ +function $FilterProvider($provide) { + var suffix = 'Filter'; + + /** + * @ngdoc method + * @name $filterProvider#register + * @param {string|Object} name Name of the filter function, or an object map of filters where + * the keys are the filter names and the values are the filter factories. + * + *
    + * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`. + * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace + * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores + * (`myapp_subsection_filterx`). + *
    + * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered. + * @returns {Object} Registered filter instance, or if a map of filters was provided then a map + * of the registered filter instances. + */ + function register(name, factory) { + if (isObject(name)) { + var filters = {}; + forEach(name, function(filter, key) { + filters[key] = register(key, filter); + }); + return filters; + } else { + return $provide.factory(name + suffix, factory); + } + } + this.register = register; + + this.$get = ['$injector', function($injector) { + return function(name) { + return $injector.get(name + suffix); + }; + }]; + + //////////////////////////////////////// + + /* global + currencyFilter: false, + dateFilter: false, + filterFilter: false, + jsonFilter: false, + limitToFilter: false, + lowercaseFilter: false, + numberFilter: false, + orderByFilter: false, + uppercaseFilter: false + */ + + register('currency', currencyFilter); + register('date', dateFilter); + register('filter', filterFilter); + register('json', jsonFilter); + register('limitTo', limitToFilter); + register('lowercase', lowercaseFilter); + register('number', numberFilter); + register('orderBy', orderByFilter); + register('uppercase', uppercaseFilter); +} + +/** + * @ngdoc filter + * @name filter + * @kind function + * + * @description + * Selects a subset of items from `array` and returns it as a new array. + * + * @param {Array} array The source array. + *
    + * **Note**: If the array contains objects that reference themselves, filtering is not possible. + *
    + * @param {string|Object|function()} expression The predicate to be used for selecting items from + * `array`. + * + * Can be one of: + * + * - `string`: The string is used for matching against the contents of the `array`. All strings or + * objects with string properties in `array` that match this string will be returned. This also + * applies to nested object properties. + * The predicate can be negated by prefixing the string with `!`. + * + * - `Object`: A pattern object can be used to filter specific properties on objects contained + * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items + * which have property `name` containing "M" and property `phone` containing "1". A special + * property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match + * against any property of the object or its nested object properties. That's equivalent to the + * simple substring match with a `string` as described above. The special property name can be + * overwritten, using the `anyPropertyKey` parameter. + * The predicate can be negated by prefixing the string with `!`. + * For example `{name: "!M"}` predicate will return an array of items which have property `name` + * not containing "M". + * + * Note that a named property will match properties on the same level only, while the special + * `$` property will match properties on the same level or deeper. E.g. an array item like + * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but + * **will** be matched by `{$: 'John'}`. + * + * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters. + * The function is called for each element of the array, with the element, its index, and + * the entire array itself as arguments. + * + * The final result is an array of those elements that the predicate returned true for. + * + * @param {function(actual, expected)|true|false} [comparator] Comparator which is used in + * determining if values retrieved using `expression` (when it is not a function) should be + * considered a match based on the expected value (from the filter expression) and actual + * value (from the object in the array). + * + * Can be one of: + * + * - `function(actual, expected)`: + * The function will be given the object value and the predicate value to compare and + * should return true if both values should be considered equal. + * + * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. + * This is essentially strict comparison of expected and actual. + * + * - `false`: A short hand for a function which will look for a substring match in a case + * insensitive way. Primitive values are converted to strings. Objects are not compared against + * primitives, unless they have a custom `toString` method (e.g. `Date` objects). + * + * + * Defaults to `false`. + * + * @param {string} [anyPropertyKey] The special property name that matches against any property. + * By default `$`. + * + * @example + + +
    + + + + + + + + +
    NamePhone
    {{friend.name}}{{friend.phone}}
    +
    +
    +
    +
    +
    + + + + + + +
    NamePhone
    {{friendObj.name}}{{friendObj.phone}}
    +
    + + var expectFriendNames = function(expectedNames, key) { + element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { + arr.forEach(function(wd, i) { + expect(wd.getText()).toMatch(expectedNames[i]); + }); + }); + }; + + it('should search across all fields when filtering with a string', function() { + var searchText = element(by.model('searchText')); + searchText.clear(); + searchText.sendKeys('m'); + expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); + + searchText.clear(); + searchText.sendKeys('76'); + expectFriendNames(['John', 'Julie'], 'friend'); + }); + + it('should search in specific fields when filtering with a predicate object', function() { + var searchAny = element(by.model('search.$')); + searchAny.clear(); + searchAny.sendKeys('i'); + expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); + }); + it('should use a equal comparison when comparator is true', function() { + var searchName = element(by.model('search.name')); + var strict = element(by.model('strict')); + searchName.clear(); + searchName.sendKeys('Julie'); + strict.click(); + expectFriendNames(['Julie'], 'friendObj'); + }); + +
    + */ + +function filterFilter() { + return function(array, expression, comparator, anyPropertyKey) { + if (!isArrayLike(array)) { + if (array == null) { + return array; + } else { + throw minErr('filter')('notarray', 'Expected array but received: {0}', array); + } + } + + anyPropertyKey = anyPropertyKey || '$'; + var expressionType = getTypeForFilter(expression); + var predicateFn; + var matchAgainstAnyProp; + + switch (expressionType) { + case 'function': + predicateFn = expression; + break; + case 'boolean': + case 'null': + case 'number': + case 'string': + matchAgainstAnyProp = true; + // falls through + case 'object': + predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp); + break; + default: + return array; + } + + return Array.prototype.filter.call(array, predicateFn); + }; +} + +// Helper functions for `filterFilter` +function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) { + var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression); + var predicateFn; + + if (comparator === true) { + comparator = equals; + } else if (!isFunction(comparator)) { + comparator = function(actual, expected) { + if (isUndefined(actual)) { + // No substring matching against `undefined` + return false; + } + if ((actual === null) || (expected === null)) { + // No substring matching against `null`; only match against `null` + return actual === expected; + } + if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) { + // Should not compare primitives against objects, unless they have custom `toString` method + return false; + } + + actual = lowercase('' + actual); + expected = lowercase('' + expected); + return actual.indexOf(expected) !== -1; + }; + } + + predicateFn = function(item) { + if (shouldMatchPrimitives && !isObject(item)) { + return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false); + } + return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp); + }; + + return predicateFn; +} + +function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) { + var actualType = getTypeForFilter(actual); + var expectedType = getTypeForFilter(expected); + + if ((expectedType === 'string') && (expected.charAt(0) === '!')) { + return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp); + } else if (isArray(actual)) { + // In case `actual` is an array, consider it a match + // if ANY of it's items matches `expected` + return actual.some(function(item) { + return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp); + }); + } + + switch (actualType) { + case 'object': + var key; + if (matchAgainstAnyProp) { + for (key in actual) { + // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined + // See: https://github.com/angular/angular.js/issues/15644 + if (key.charAt && (key.charAt(0) !== '$') && + deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) { + return true; + } + } + return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false); + } else if (expectedType === 'object') { + for (key in expected) { + var expectedVal = expected[key]; + if (isFunction(expectedVal) || isUndefined(expectedVal)) { + continue; + } + + var matchAnyProperty = key === anyPropertyKey; + var actualVal = matchAnyProperty ? actual : actual[key]; + if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) { + return false; + } + } + return true; + } else { + return comparator(actual, expected); + } + case 'function': + return false; + default: + return comparator(actual, expected); + } +} + +// Used for easily differentiating between `null` and actual `object` +function getTypeForFilter(val) { + return (val === null) ? 'null' : typeof val; +} + +var MAX_DIGITS = 22; +var DECIMAL_SEP = '.'; +var ZERO_CHAR = '0'; + +/** + * @ngdoc filter + * @name currency + * @kind function + * + * @description + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. + * + * @param {number} amount Input to filter. + * @param {string=} symbol Currency symbol or identifier to be displayed. + * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale + * @returns {string} Formatted number. + * + * + * @example + + + +
    +
    + default currency symbol ($): {{amount | currency}}
    + custom currency identifier (USD$): {{amount | currency:"USD$"}}
    + no fractions (0): {{amount | currency:"USD$":0}} +
    +
    + + it('should init with 1234.56', function() { + expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); + expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56'); + expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235'); + }); + it('should update', function() { + if (browser.params.browser === 'safari') { + // Safari does not understand the minus key. See + // https://github.com/angular/protractor/issues/481 + return; + } + element(by.model('amount')).clear(); + element(by.model('amount')).sendKeys('-1234'); + expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00'); + expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00'); + expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234'); + }); + +
    + */ +currencyFilter.$inject = ['$locale']; +function currencyFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(amount, currencySymbol, fractionSize) { + if (isUndefined(currencySymbol)) { + currencySymbol = formats.CURRENCY_SYM; + } + + if (isUndefined(fractionSize)) { + fractionSize = formats.PATTERNS[1].maxFrac; + } + + // If the currency symbol is empty, trim whitespace around the symbol + var currencySymbolRe = !currencySymbol ? /\s*\u00A4\s*/g : /\u00A4/g; + + // if null or undefined pass it through + return (amount == null) + ? amount + : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize). + replace(currencySymbolRe, currencySymbol); + }; +} + +/** + * @ngdoc filter + * @name number + * @kind function + * + * @description + * Formats a number as text. + * + * If the input is null or undefined, it will just be returned. + * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively. + * If the input is not a number an empty string is returned. + * + * + * @param {number|string} number Number to format. + * @param {(number|string)=} fractionSize Number of decimal places to round the number to. + * If this is not provided then the fraction size is computed from the current locale's number + * formatting pattern. In the case of the default locale, it will be 3. + * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current + * locale (e.g., in the en_US locale it will have "." as the decimal separator and + * include "," group separators after each third digit). + * + * @example + + + +
    +
    + Default formatting: {{val | number}}
    + No fractions: {{val | number:0}}
    + Negative number: {{-val | number:4}} +
    +
    + + it('should format numbers', function() { + expect(element(by.id('number-default')).getText()).toBe('1,234.568'); + expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); + }); + + it('should update', function() { + element(by.model('val')).clear(); + element(by.model('val')).sendKeys('3374.333'); + expect(element(by.id('number-default')).getText()).toBe('3,374.333'); + expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); + }); + +
    + */ +numberFilter.$inject = ['$locale']; +function numberFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(number, fractionSize) { + + // if null or undefined pass it through + return (number == null) + ? number + : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, + fractionSize); + }; +} + +/** + * Parse a number (as a string) into three components that can be used + * for formatting the number. + * + * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/) + * + * @param {string} numStr The number to parse + * @return {object} An object describing this number, containing the following keys: + * - d : an array of digits containing leading zeros as necessary + * - i : the number of the digits in `d` that are to the left of the decimal point + * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d` + * + */ +function parse(numStr) { + var exponent = 0, digits, numberOfIntegerDigits; + var i, j, zeros; + + // Decimal point? + if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) { + numStr = numStr.replace(DECIMAL_SEP, ''); + } + + // Exponential form? + if ((i = numStr.search(/e/i)) > 0) { + // Work out the exponent. + if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i; + numberOfIntegerDigits += +numStr.slice(i + 1); + numStr = numStr.substring(0, i); + } else if (numberOfIntegerDigits < 0) { + // There was no decimal point or exponent so it is an integer. + numberOfIntegerDigits = numStr.length; + } + + // Count the number of leading zeros. + for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */ } + + if (i === (zeros = numStr.length)) { + // The digits are all zero. + digits = [0]; + numberOfIntegerDigits = 1; + } else { + // Count the number of trailing zeros + zeros--; + while (numStr.charAt(zeros) === ZERO_CHAR) zeros--; + + // Trailing zeros are insignificant so ignore them + numberOfIntegerDigits -= i; + digits = []; + // Convert string to array of digits without leading/trailing zeros. + for (j = 0; i <= zeros; i++, j++) { + digits[j] = +numStr.charAt(i); + } + } + + // If the number overflows the maximum allowed digits then use an exponent. + if (numberOfIntegerDigits > MAX_DIGITS) { + digits = digits.splice(0, MAX_DIGITS - 1); + exponent = numberOfIntegerDigits - 1; + numberOfIntegerDigits = 1; + } + + return { d: digits, e: exponent, i: numberOfIntegerDigits }; +} + +/** + * Round the parsed number to the specified number of decimal places + * This function changed the parsedNumber in-place + */ +function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) { + var digits = parsedNumber.d; + var fractionLen = digits.length - parsedNumber.i; + + // determine fractionSize if it is not specified; `+fractionSize` converts it to a number + fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize; + + // The index of the digit to where rounding is to occur + var roundAt = fractionSize + parsedNumber.i; + var digit = digits[roundAt]; + + if (roundAt > 0) { + // Drop fractional digits beyond `roundAt` + digits.splice(Math.max(parsedNumber.i, roundAt)); + + // Set non-fractional digits beyond `roundAt` to 0 + for (var j = roundAt; j < digits.length; j++) { + digits[j] = 0; + } + } else { + // We rounded to zero so reset the parsedNumber + fractionLen = Math.max(0, fractionLen); + parsedNumber.i = 1; + digits.length = Math.max(1, roundAt = fractionSize + 1); + digits[0] = 0; + for (var i = 1; i < roundAt; i++) digits[i] = 0; + } + + if (digit >= 5) { + if (roundAt - 1 < 0) { + for (var k = 0; k > roundAt; k--) { + digits.unshift(0); + parsedNumber.i++; + } + digits.unshift(1); + parsedNumber.i++; + } else { + digits[roundAt - 1]++; + } + } + + // Pad out with zeros to get the required fraction length + for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0); + + + // Do any carrying, e.g. a digit was rounded up to 10 + var carry = digits.reduceRight(function(carry, d, i, digits) { + d = d + carry; + digits[i] = d % 10; + return Math.floor(d / 10); + }, 0); + if (carry) { + digits.unshift(carry); + parsedNumber.i++; + } +} + +/** + * Format a number into a string + * @param {number} number The number to format + * @param {{ + * minFrac, // the minimum number of digits required in the fraction part of the number + * maxFrac, // the maximum number of digits required in the fraction part of the number + * gSize, // number of digits in each group of separated digits + * lgSize, // number of digits in the last group of digits before the decimal separator + * negPre, // the string to go in front of a negative number (e.g. `-` or `(`)) + * posPre, // the string to go in front of a positive number + * negSuf, // the string to go after a negative number (e.g. `)`) + * posSuf // the string to go after a positive number + * }} pattern + * @param {string} groupSep The string to separate groups of number (e.g. `,`) + * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`) + * @param {[type]} fractionSize The size of the fractional part of the number + * @return {string} The number formatted as a string + */ +function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { + + if (!(isString(number) || isNumber(number)) || isNaN(number)) return ''; + + var isInfinity = !isFinite(number); + var isZero = false; + var numStr = Math.abs(number) + '', + formattedText = '', + parsedNumber; + + if (isInfinity) { + formattedText = '\u221e'; + } else { + parsedNumber = parse(numStr); + + roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac); + + var digits = parsedNumber.d; + var integerLen = parsedNumber.i; + var exponent = parsedNumber.e; + var decimals = []; + isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true); + + // pad zeros for small numbers + while (integerLen < 0) { + digits.unshift(0); + integerLen++; + } + + // extract decimals digits + if (integerLen > 0) { + decimals = digits.splice(integerLen, digits.length); + } else { + decimals = digits; + digits = [0]; + } + + // format the integer digits with grouping separators + var groups = []; + if (digits.length >= pattern.lgSize) { + groups.unshift(digits.splice(-pattern.lgSize, digits.length).join('')); + } + while (digits.length > pattern.gSize) { + groups.unshift(digits.splice(-pattern.gSize, digits.length).join('')); + } + if (digits.length) { + groups.unshift(digits.join('')); + } + formattedText = groups.join(groupSep); + + // append the decimal digits + if (decimals.length) { + formattedText += decimalSep + decimals.join(''); + } + + if (exponent) { + formattedText += 'e+' + exponent; + } + } + if (number < 0 && !isZero) { + return pattern.negPre + formattedText + pattern.negSuf; + } else { + return pattern.posPre + formattedText + pattern.posSuf; + } +} + +function padNumber(num, digits, trim, negWrap) { + var neg = ''; + if (num < 0 || (negWrap && num <= 0)) { + if (negWrap) { + num = -num + 1; + } else { + num = -num; + neg = '-'; + } + } + num = '' + num; + while (num.length < digits) num = ZERO_CHAR + num; + if (trim) { + num = num.substr(num.length - digits); + } + return neg + num; +} + + +function dateGetter(name, size, offset, trim, negWrap) { + offset = offset || 0; + return function(date) { + var value = date['get' + name](); + if (offset > 0 || value > -offset) { + value += offset; + } + if (value === 0 && offset === -12) value = 12; + return padNumber(value, size, trim, negWrap); + }; +} + +function dateStrGetter(name, shortForm, standAlone) { + return function(date, formats) { + var value = date['get' + name](); + var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : ''); + var get = uppercase(propPrefix + name); + + return formats[get][value]; + }; +} + +function timeZoneGetter(date, formats, offset) { + var zone = -1 * offset; + var paddedZone = (zone >= 0) ? '+' : ''; + + paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + + padNumber(Math.abs(zone % 60), 2); + + return paddedZone; +} + +function getFirstThursdayOfYear(year) { + // 0 = index of January + var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay(); + // 4 = index of Thursday (+1 to account for 1st = 5) + // 11 = index of *next* Thursday (+1 account for 1st = 12) + return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst); +} + +function getThursdayThisWeek(datetime) { + return new Date(datetime.getFullYear(), datetime.getMonth(), + // 4 = index of Thursday + datetime.getDate() + (4 - datetime.getDay())); +} + +function weekGetter(size) { + return function(date) { + var firstThurs = getFirstThursdayOfYear(date.getFullYear()), + thisThurs = getThursdayThisWeek(date); + + var diff = +thisThurs - +firstThurs, + result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week + + return padNumber(result, size); + }; +} + +function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; +} + +function eraGetter(date, formats) { + return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1]; +} + +function longEraGetter(date, formats) { + return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1]; +} + +var DATE_FORMATS = { + yyyy: dateGetter('FullYear', 4, 0, false, true), + yy: dateGetter('FullYear', 2, 0, true, true), + y: dateGetter('FullYear', 1, 0, false, true), + MMMM: dateStrGetter('Month'), + MMM: dateStrGetter('Month', true), + MM: dateGetter('Month', 2, 1), + M: dateGetter('Month', 1, 1), + LLLL: dateStrGetter('Month', false, true), + dd: dateGetter('Date', 2), + d: dateGetter('Date', 1), + HH: dateGetter('Hours', 2), + H: dateGetter('Hours', 1), + hh: dateGetter('Hours', 2, -12), + h: dateGetter('Hours', 1, -12), + mm: dateGetter('Minutes', 2), + m: dateGetter('Minutes', 1), + ss: dateGetter('Seconds', 2), + s: dateGetter('Seconds', 1), + // while ISO 8601 requires fractions to be prefixed with `.` or `,` + // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions + sss: dateGetter('Milliseconds', 3), + EEEE: dateStrGetter('Day'), + EEE: dateStrGetter('Day', true), + a: ampmGetter, + Z: timeZoneGetter, + ww: weekGetter(2), + w: weekGetter(1), + G: eraGetter, + GG: eraGetter, + GGG: eraGetter, + GGGG: longEraGetter +}; + +var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/, + NUMBER_STRING = /^-?\d+$/; + +/** + * @ngdoc filter + * @name date + * @kind function + * + * @description + * Formats `date` to a string based on the requested `format`. + * + * `format` string can be composed of the following elements: + * + * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + * * `'MMMM'`: Month in year (January-December) + * * `'MMM'`: Month in year (Jan-Dec) + * * `'MM'`: Month in year, padded (01-12) + * * `'M'`: Month in year (1-12) + * * `'LLLL'`: Stand-alone month in year (January-December) + * * `'dd'`: Day in month, padded (01-31) + * * `'d'`: Day in month (1-31) + * * `'EEEE'`: Day in Week,(Sunday-Saturday) + * * `'EEE'`: Day in Week, (Sun-Sat) + * * `'HH'`: Hour in day, padded (00-23) + * * `'H'`: Hour in day (0-23) + * * `'hh'`: Hour in AM/PM, padded (01-12) + * * `'h'`: Hour in AM/PM, (1-12) + * * `'mm'`: Minute in hour, padded (00-59) + * * `'m'`: Minute in hour (0-59) + * * `'ss'`: Second in minute, padded (00-59) + * * `'s'`: Second in minute (0-59) + * * `'sss'`: Millisecond in second, padded (000-999) + * * `'a'`: AM/PM marker + * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) + * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year + * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year + * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD') + * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini') + * + * `format` string can also be one of the following predefined + * {@link guide/i18n localizable formats}: + * + * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale + * (e.g. Sep 3, 2010 12:05:08 PM) + * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM) + * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale + * (e.g. Friday, September 3, 2010) + * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) + * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) + * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) + * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM) + * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM) + * + * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. + * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence + * (e.g. `"h 'o''clock'"`). + * + * Any other characters in the `format` string will be output as-is. + * + * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or + * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its + * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is + * specified in the string input, the time is considered to be in the local timezone. + * @param {string=} format Formatting rules (see Description). If not specified, + * `mediumDate` is used. + * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the + * continental US time zone abbreviations, but for general use, use a time zone offset, for + * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) + * If not specified, the timezone of the browser will be used. + * @returns {string} Formatted string or the input if input is not recognized as date/millis. + * + * @example + + + {{1288323623006 | date:'medium'}}: + {{1288323623006 | date:'medium'}}
    + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: + {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
    + {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: + {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
    + {{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}: + {{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}
    +
    + + it('should format date', function() { + expect(element(by.binding("1288323623006 | date:'medium'")).getText()). + toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); + expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). + toMatch(/2010-10-2\d \d{2}:\d{2}:\d{2} (-|\+)?\d{4}/); + expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). + toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); + expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). + toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); + }); + +
    + */ +dateFilter.$inject = ['$locale']; +function dateFilter($locale) { + + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + // 1 2 3 4 5 6 7 8 9 10 11 + function jsonStringToDate(string) { + var match; + if ((match = string.match(R_ISO8601_STR))) { + var date = new Date(0), + tzHour = 0, + tzMin = 0, + dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, + timeSetter = match[8] ? date.setUTCHours : date.setHours; + + if (match[9]) { + tzHour = toInt(match[9] + match[10]); + tzMin = toInt(match[9] + match[11]); + } + dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3])); + var h = toInt(match[4] || 0) - tzHour; + var m = toInt(match[5] || 0) - tzMin; + var s = toInt(match[6] || 0); + var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + return string; + } + + + return function(date, format, timezone) { + var text = '', + parts = [], + fn, match; + + format = format || 'mediumDate'; + format = $locale.DATETIME_FORMATS[format] || format; + if (isString(date)) { + date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date); + } + + if (isNumber(date)) { + date = new Date(date); + } + + if (!isDate(date) || !isFinite(date.getTime())) { + return date; + } + + while (format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + var dateTimezoneOffset = date.getTimezoneOffset(); + if (timezone) { + dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + date = convertTimezoneToLocal(date, timezone, true); + } + forEach(parts, function(value) { + fn = DATE_FORMATS[value]; + text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset) + : value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\''); + }); + + return text; + }; +} + + +/** + * @ngdoc filter + * @name json + * @kind function + * + * @description + * Allows you to convert a JavaScript object into JSON string. + * + * This filter is mostly useful for debugging. When using the double curly {{value}} notation + * the binding is automatically converted to JSON. + * + * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. + * @param {number=} spacing The number of spaces to use per indentation, defaults to 2. + * @returns {string} JSON string. + * + * + * @example + + +
    {{ {'name':'value'} | json }}
    +
    {{ {'name':'value'} | json:4 }}
    +
    + + it('should jsonify filtered objects', function() { + expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n {2}"name": ?"value"\n}/); + expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n {4}"name": ?"value"\n}/); + }); + +
    + * + */ +function jsonFilter() { + return function(object, spacing) { + if (isUndefined(spacing)) { + spacing = 2; + } + return toJson(object, spacing); + }; +} + + +/** + * @ngdoc filter + * @name lowercase + * @kind function + * @description + * Converts string to lowercase. + * + * See the {@link ng.uppercase uppercase filter documentation} for a functionally identical example. + * + * @see angular.lowercase + */ +var lowercaseFilter = valueFn(lowercase); + + +/** + * @ngdoc filter + * @name uppercase + * @kind function + * @description + * Converts string to uppercase. + * @example + + + +
    + +

    {{title}}

    + +

    {{title | uppercase}}

    +
    +
    +
    + */ +var uppercaseFilter = valueFn(uppercase); + +/** + * @ngdoc filter + * @name limitTo + * @kind function + * + * @description + * Creates a new array or string containing only a specified number of elements. The elements are + * taken from either the beginning or the end of the source array, string or number, as specified by + * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported + * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input, + * it is converted to a string. + * + * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited. + * @param {string|number} limit - The length of the returned array or string. If the `limit` number + * is positive, `limit` number of items from the beginning of the source array/string are copied. + * If the number is negative, `limit` number of items from the end of the source array/string + * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined, + * the input will be returned unchanged. + * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index, + * `begin` indicates an offset from the end of `input`. Defaults to `0`. + * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had + * less than `limit` elements. + * + * @example + + + +
    + +

    Output numbers: {{ numbers | limitTo:numLimit }}

    + +

    Output letters: {{ letters | limitTo:letterLimit }}

    + +

    Output long number: {{ longNumber | limitTo:longNumberLimit }}

    +
    +
    + + var numLimitInput = element(by.model('numLimit')); + var letterLimitInput = element(by.model('letterLimit')); + var longNumberLimitInput = element(by.model('longNumberLimit')); + var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); + var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); + var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit')); + + it('should limit the number array to first three items', function() { + expect(numLimitInput.getAttribute('value')).toBe('3'); + expect(letterLimitInput.getAttribute('value')).toBe('3'); + expect(longNumberLimitInput.getAttribute('value')).toBe('3'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); + expect(limitedLetters.getText()).toEqual('Output letters: abc'); + expect(limitedLongNumber.getText()).toEqual('Output long number: 234'); + }); + + // There is a bug in safari and protractor that doesn't like the minus key + // it('should update the output when -3 is entered', function() { + // numLimitInput.clear(); + // numLimitInput.sendKeys('-3'); + // letterLimitInput.clear(); + // letterLimitInput.sendKeys('-3'); + // longNumberLimitInput.clear(); + // longNumberLimitInput.sendKeys('-3'); + // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); + // expect(limitedLetters.getText()).toEqual('Output letters: ghi'); + // expect(limitedLongNumber.getText()).toEqual('Output long number: 342'); + // }); + + it('should not exceed the maximum size of input array', function() { + numLimitInput.clear(); + numLimitInput.sendKeys('100'); + letterLimitInput.clear(); + letterLimitInput.sendKeys('100'); + longNumberLimitInput.clear(); + longNumberLimitInput.sendKeys('100'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); + expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); + expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); + }); + +
    +*/ +function limitToFilter() { + return function(input, limit, begin) { + if (Math.abs(Number(limit)) === Infinity) { + limit = Number(limit); + } else { + limit = toInt(limit); + } + if (isNumberNaN(limit)) return input; + + if (isNumber(input)) input = input.toString(); + if (!isArrayLike(input)) return input; + + begin = (!begin || isNaN(begin)) ? 0 : toInt(begin); + begin = (begin < 0) ? Math.max(0, input.length + begin) : begin; + + if (limit >= 0) { + return sliceFn(input, begin, begin + limit); + } else { + if (begin === 0) { + return sliceFn(input, limit, input.length); + } else { + return sliceFn(input, Math.max(0, begin + limit), begin); + } + } + }; +} + +function sliceFn(input, begin, end) { + if (isString(input)) return input.slice(begin, end); + + return slice.call(input, begin, end); +} + +/** + * @ngdoc filter + * @name orderBy + * @kind function + * + * @description + * Returns an array containing the items from the specified `collection`, ordered by a `comparator` + * function based on the values computed using the `expression` predicate. + * + * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in + * `[{id: 'bar'}, {id: 'foo'}]`. + * + * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray, + * String, etc). + * + * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker + * for the preceding one. The `expression` is evaluated against each item and the output is used + * for comparing with other items. + * + * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in + * ascending order. + * + * The comparison is done using the `comparator` function. If none is specified, a default, built-in + * comparator is used (see below for details - in a nutshell, it compares numbers numerically and + * strings alphabetically). + * + * ### Under the hood + * + * Ordering the specified `collection` happens in two phases: + * + * 1. All items are passed through the predicate (or predicates), and the returned values are saved + * along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed + * through a predicate that extracts the value of the `label` property, would be transformed to: + * ``` + * { + * value: 'foo', + * type: 'string', + * index: ... + * } + * ``` + * 2. The comparator function is used to sort the items, based on the derived values, types and + * indices. + * + * If you use a custom comparator, it will be called with pairs of objects of the form + * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal + * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the + * second, or `1` otherwise. + * + * In order to ensure that the sorting will be deterministic across platforms, if none of the + * specified predicates can distinguish between two items, `orderBy` will automatically introduce a + * dummy predicate that returns the item's index as `value`. + * (If you are using a custom comparator, make sure it can handle this predicate as well.) + * + * If a custom comparator still can't distinguish between two items, then they will be sorted based + * on their index using the built-in comparator. + * + * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted + * value for an item, `orderBy` will try to convert that object to a primitive value, before passing + * it to the comparator. The following rules govern the conversion: + * + * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be + * used instead.
    + * (If the object has a `valueOf()` method that returns another object, then the returned object + * will be used in subsequent steps.) + * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that + * returns a primitive, its return value will be used instead.
    + * (If the object has a `toString()` method that returns another object, then the returned object + * will be used in subsequent steps.) + * 3. No conversion; the object itself is used. + * + * ### The default comparator + * + * The default, built-in comparator should be sufficient for most usecases. In short, it compares + * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to + * using their index in the original collection, and sorts values of different types by type. + * + * More specifically, it follows these steps to determine the relative order of items: + * + * 1. If the compared values are of different types, compare the types themselves alphabetically. + * 2. If both values are of type `string`, compare them alphabetically in a case- and + * locale-insensitive way. + * 3. If both values are objects, compare their indices instead. + * 4. Otherwise, return: + * - `0`, if the values are equal (by strict equality comparison, i.e. using `===`). + * - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator). + * - `1`, otherwise. + * + * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being + * saved as numbers and not strings. + * **Note:** For the purpose of sorting, `null` values are treated as the string `'null'` (i.e. + * `type: 'string'`, `value: 'null'`). This may cause unexpected sort order relative to + * other values. + * + * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort. + * @param {(Function|string|Array.)=} expression - A predicate (or list of + * predicates) to be used by the comparator to determine the order of elements. + * + * Can be one of: + * + * - `Function`: A getter function. This function will be called with each item as argument and + * the return value will be used for sorting. + * - `string`: An AngularJS expression. This expression will be evaluated against each item and the + * result will be used for sorting. For example, use `'label'` to sort by a property called + * `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label` + * property.
    + * (The result of a constant expression is interpreted as a property name to be used for + * comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a + * property called `special name`.)
    + * An expression can be optionally prefixed with `+` or `-` to control the sorting direction, + * ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided, + * (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons. + * - `Array`: An array of function and/or string predicates. If a predicate cannot determine the + * relative order of two items, the next predicate is used as a tie-breaker. + * + * **Note:** If the predicate is missing or empty then it defaults to `'+'`. + * + * @param {boolean=} reverse - If `true`, reverse the sorting order. + * @param {(Function)=} comparator - The comparator function used to determine the relative order of + * value pairs. If omitted, the built-in comparator will be used. + * + * @returns {Array} - The sorted array. + * + * + * @example + * ### Ordering a table with `ngRepeat` + * + * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by + * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means + * it defaults to the built-in comparator. + * + + +
    + + + + + + + + + + + +
    NamePhone NumberAge
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + angular.module('orderByExample1', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.friends = [ + {name: 'John', phone: '555-1212', age: 10}, + {name: 'Mary', phone: '555-9876', age: 19}, + {name: 'Mike', phone: '555-4321', age: 21}, + {name: 'Adam', phone: '555-5678', age: 35}, + {name: 'Julie', phone: '555-8765', age: 29} + ]; + }]); + + + .friends { + border-collapse: collapse; + } + + .friends th { + border-bottom: 1px solid; + } + .friends td, .friends th { + border-left: 1px solid; + padding: 5px 10px; + } + .friends td:first-child, .friends th:first-child { + border-left: none; + } + + + // Element locators + var names = element.all(by.repeater('friends').column('friend.name')); + + it('should sort friends by age in reverse order', function() { + expect(names.get(0).getText()).toBe('Adam'); + expect(names.get(1).getText()).toBe('Julie'); + expect(names.get(2).getText()).toBe('Mike'); + expect(names.get(3).getText()).toBe('Mary'); + expect(names.get(4).getText()).toBe('John'); + }); + +
    + *
    + * + * @example + * ### Changing parameters dynamically + * + * All parameters can be changed dynamically. The next example shows how you can make the columns of + * a table sortable, by binding the `expression` and `reverse` parameters to scope properties. + * + + +
    +
    Sort by = {{propertyName}}; reverse = {{reverse}}
    +
    + +
    + + + + + + + + + + + +
    + + + + + + + + +
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + angular.module('orderByExample2', []) + .controller('ExampleController', ['$scope', function($scope) { + var friends = [ + {name: 'John', phone: '555-1212', age: 10}, + {name: 'Mary', phone: '555-9876', age: 19}, + {name: 'Mike', phone: '555-4321', age: 21}, + {name: 'Adam', phone: '555-5678', age: 35}, + {name: 'Julie', phone: '555-8765', age: 29} + ]; + + $scope.propertyName = 'age'; + $scope.reverse = true; + $scope.friends = friends; + + $scope.sortBy = function(propertyName) { + $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false; + $scope.propertyName = propertyName; + }; + }]); + + + .friends { + border-collapse: collapse; + } + + .friends th { + border-bottom: 1px solid; + } + .friends td, .friends th { + border-left: 1px solid; + padding: 5px 10px; + } + .friends td:first-child, .friends th:first-child { + border-left: none; + } + + .sortorder:after { + content: '\25b2'; // BLACK UP-POINTING TRIANGLE + } + .sortorder.reverse:after { + content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE + } + + + // Element locators + var unsortButton = element(by.partialButtonText('unsorted')); + var nameHeader = element(by.partialButtonText('Name')); + var phoneHeader = element(by.partialButtonText('Phone')); + var ageHeader = element(by.partialButtonText('Age')); + var firstName = element(by.repeater('friends').column('friend.name').row(0)); + var lastName = element(by.repeater('friends').column('friend.name').row(4)); + + it('should sort friends by some property, when clicking on the column header', function() { + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + + phoneHeader.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Mary'); + + nameHeader.click(); + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('Mike'); + + ageHeader.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Adam'); + }); + + it('should sort friends in reverse order, when clicking on the same column', function() { + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + + ageHeader.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Adam'); + + ageHeader.click(); + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + }); + + it('should restore the original order, when clicking "Set to unsorted"', function() { + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + + unsortButton.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Julie'); + }); + +
    + *
    + * + * @example + * ### Using `orderBy` inside a controller + * + * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and + * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory + * and retrieve the `orderBy` filter with `$filter('orderBy')`.) + * + + +
    +
    Sort by = {{propertyName}}; reverse = {{reverse}}
    +
    + +
    + + + + + + + + + + + +
    + + + + + + + + +
    {{friend.name}}{{friend.phone}}{{friend.age}}
    +
    +
    + + angular.module('orderByExample3', []) + .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) { + var friends = [ + {name: 'John', phone: '555-1212', age: 10}, + {name: 'Mary', phone: '555-9876', age: 19}, + {name: 'Mike', phone: '555-4321', age: 21}, + {name: 'Adam', phone: '555-5678', age: 35}, + {name: 'Julie', phone: '555-8765', age: 29} + ]; + + $scope.propertyName = 'age'; + $scope.reverse = true; + $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse); + + $scope.sortBy = function(propertyName) { + $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName) + ? !$scope.reverse : false; + $scope.propertyName = propertyName; + $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse); + }; + }]); + + + .friends { + border-collapse: collapse; + } + + .friends th { + border-bottom: 1px solid; + } + .friends td, .friends th { + border-left: 1px solid; + padding: 5px 10px; + } + .friends td:first-child, .friends th:first-child { + border-left: none; + } + + .sortorder:after { + content: '\25b2'; // BLACK UP-POINTING TRIANGLE + } + .sortorder.reverse:after { + content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE + } + + + // Element locators + var unsortButton = element(by.partialButtonText('unsorted')); + var nameHeader = element(by.partialButtonText('Name')); + var phoneHeader = element(by.partialButtonText('Phone')); + var ageHeader = element(by.partialButtonText('Age')); + var firstName = element(by.repeater('friends').column('friend.name').row(0)); + var lastName = element(by.repeater('friends').column('friend.name').row(4)); + + it('should sort friends by some property, when clicking on the column header', function() { + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + + phoneHeader.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Mary'); + + nameHeader.click(); + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('Mike'); + + ageHeader.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Adam'); + }); + + it('should sort friends in reverse order, when clicking on the same column', function() { + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + + ageHeader.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Adam'); + + ageHeader.click(); + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + }); + + it('should restore the original order, when clicking "Set to unsorted"', function() { + expect(firstName.getText()).toBe('Adam'); + expect(lastName.getText()).toBe('John'); + + unsortButton.click(); + expect(firstName.getText()).toBe('John'); + expect(lastName.getText()).toBe('Julie'); + }); + +
    + *
    + * + * @example + * ### Using a custom comparator + * + * If you have very specific requirements about the way items are sorted, you can pass your own + * comparator function. For example, you might need to compare some strings in a locale-sensitive + * way. (When specifying a custom comparator, you also need to pass a value for the `reverse` + * argument - passing `false` retains the default sorting order, i.e. ascending.) + * + + +
    +
    +

    Locale-sensitive Comparator

    + + + + + + + + + +
    NameFavorite Letter
    {{friend.name}}{{friend.favoriteLetter}}
    +
    +
    +

    Default Comparator

    + + + + + + + + + +
    NameFavorite Letter
    {{friend.name}}{{friend.favoriteLetter}}
    +
    +
    +
    + + angular.module('orderByExample4', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.friends = [ + {name: 'John', favoriteLetter: 'Ä'}, + {name: 'Mary', favoriteLetter: 'Ü'}, + {name: 'Mike', favoriteLetter: 'Ö'}, + {name: 'Adam', favoriteLetter: 'H'}, + {name: 'Julie', favoriteLetter: 'Z'} + ]; + + $scope.localeSensitiveComparator = function(v1, v2) { + // If we don't get strings, just compare by index + if (v1.type !== 'string' || v2.type !== 'string') { + return (v1.index < v2.index) ? -1 : 1; + } + + // Compare strings alphabetically, taking locale into account + return v1.value.localeCompare(v2.value); + }; + }]); + + + .friends-container { + display: inline-block; + margin: 0 30px; + } + + .friends { + border-collapse: collapse; + } + + .friends th { + border-bottom: 1px solid; + } + .friends td, .friends th { + border-left: 1px solid; + padding: 5px 10px; + } + .friends td:first-child, .friends th:first-child { + border-left: none; + } + + + // Element locators + var container = element(by.css('.custom-comparator')); + var names = container.all(by.repeater('friends').column('friend.name')); + + it('should sort friends by favorite letter (in correct alphabetical order)', function() { + expect(names.get(0).getText()).toBe('John'); + expect(names.get(1).getText()).toBe('Adam'); + expect(names.get(2).getText()).toBe('Mike'); + expect(names.get(3).getText()).toBe('Mary'); + expect(names.get(4).getText()).toBe('Julie'); + }); + +
    + * + */ +orderByFilter.$inject = ['$parse']; +function orderByFilter($parse) { + return function(array, sortPredicate, reverseOrder, compareFn) { + + if (array == null) return array; + if (!isArrayLike(array)) { + throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array); + } + + if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; } + if (sortPredicate.length === 0) { sortPredicate = ['+']; } + + var predicates = processPredicates(sortPredicate); + + var descending = reverseOrder ? -1 : 1; + + // Define the `compare()` function. Use a default comparator if none is specified. + var compare = isFunction(compareFn) ? compareFn : defaultCompare; + + // The next three lines are a version of a Swartzian Transform idiom from Perl + // (sometimes called the Decorate-Sort-Undecorate idiom) + // See https://en.wikipedia.org/wiki/Schwartzian_transform + var compareValues = Array.prototype.map.call(array, getComparisonObject); + compareValues.sort(doComparison); + array = compareValues.map(function(item) { return item.value; }); + + return array; + + function getComparisonObject(value, index) { + // NOTE: We are adding an extra `tieBreaker` value based on the element's index. + // This will be used to keep the sort stable when none of the input predicates can + // distinguish between two elements. + return { + value: value, + tieBreaker: {value: index, type: 'number', index: index}, + predicateValues: predicates.map(function(predicate) { + return getPredicateValue(predicate.get(value), index); + }) + }; + } + + function doComparison(v1, v2) { + for (var i = 0, ii = predicates.length; i < ii; i++) { + var result = compare(v1.predicateValues[i], v2.predicateValues[i]); + if (result) { + return result * predicates[i].descending * descending; + } + } + + return (compare(v1.tieBreaker, v2.tieBreaker) || defaultCompare(v1.tieBreaker, v2.tieBreaker)) * descending; + } + }; + + function processPredicates(sortPredicates) { + return sortPredicates.map(function(predicate) { + var descending = 1, get = identity; + + if (isFunction(predicate)) { + get = predicate; + } else if (isString(predicate)) { + if ((predicate.charAt(0) === '+' || predicate.charAt(0) === '-')) { + descending = predicate.charAt(0) === '-' ? -1 : 1; + predicate = predicate.substring(1); + } + if (predicate !== '') { + get = $parse(predicate); + if (get.constant) { + var key = get(); + get = function(value) { return value[key]; }; + } + } + } + return {get: get, descending: descending}; + }); + } + + function isPrimitive(value) { + switch (typeof value) { + case 'number': /* falls through */ + case 'boolean': /* falls through */ + case 'string': + return true; + default: + return false; + } + } + + function objectValue(value) { + // If `valueOf` is a valid function use that + if (isFunction(value.valueOf)) { + value = value.valueOf(); + if (isPrimitive(value)) return value; + } + // If `toString` is a valid function and not the one from `Object.prototype` use that + if (hasCustomToString(value)) { + value = value.toString(); + if (isPrimitive(value)) return value; + } + + return value; + } + + function getPredicateValue(value, index) { + var type = typeof value; + if (value === null) { + type = 'string'; + value = 'null'; + } else if (type === 'object') { + value = objectValue(value); + } + return {value: value, type: type, index: index}; + } + + function defaultCompare(v1, v2) { + var result = 0; + var type1 = v1.type; + var type2 = v2.type; + + if (type1 === type2) { + var value1 = v1.value; + var value2 = v2.value; + + if (type1 === 'string') { + // Compare strings case-insensitively + value1 = value1.toLowerCase(); + value2 = value2.toLowerCase(); + } else if (type1 === 'object') { + // For basic objects, use the position of the object + // in the collection instead of the value + if (isObject(value1)) value1 = v1.index; + if (isObject(value2)) value2 = v2.index; + } + + if (value1 !== value2) { + result = value1 < value2 ? -1 : 1; + } + } else { + result = type1 < type2 ? -1 : 1; + } + + return result; + } +} + +function ngDirective(directive) { + if (isFunction(directive)) { + directive = { + link: directive + }; + } + directive.restrict = directive.restrict || 'AC'; + return valueFn(directive); +} + +/** + * @ngdoc directive + * @name a + * @restrict E + * + * @description + * Modifies the default behavior of the html a tag so that the default action is prevented when + * the href attribute is empty. + * + * For dynamically creating `href` attributes for a tags, see the {@link ng.ngHref `ngHref`} directive. + */ +var htmlAnchorDirective = valueFn({ + restrict: 'E', + compile: function(element, attr) { + if (!attr.href && !attr.xlinkHref) { + return function(scope, element) { + // If the linked element is not an anchor tag anymore, do nothing + if (element[0].nodeName.toLowerCase() !== 'a') return; + + // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. + var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? + 'xlink:href' : 'href'; + element.on('click', function(event) { + // if we have no href url, then don't navigate anywhere. + if (!element.attr(href)) { + event.preventDefault(); + } + }); + }; + } + } +}); + +/** + * @ngdoc directive + * @name ngHref + * @restrict A + * @priority 99 + * + * @description + * Using AngularJS markup like `{{hash}}` in an href attribute will + * make the link go to the wrong URL if the user clicks it before + * AngularJS has a chance to replace the `{{hash}}` markup with its + * value. Until AngularJS replaces the markup the link will be broken + * and will most likely return a 404 error. The `ngHref` directive + * solves this problem. + * + * The wrong way to write it: + * ```html + * link1 + * ``` + * + * The correct way to write it: + * ```html + * link1 + * ``` + * + * @element A + * @param {template} ngHref any string which can contain `{{}}` markup. + * + * @example + * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes + * in links and their different behaviors: + + +
    + link 1 (link, don't reload)
    + link 2 (link, don't reload)
    + link 3 (link, reload!)
    + anchor (link, don't reload)
    + anchor (no link)
    + link (link, change location) +
    + + it('should execute ng-click but not reload when href without value', function() { + element(by.id('link-1')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('1'); + expect(element(by.id('link-1')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when href empty string', function() { + element(by.id('link-2')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('2'); + expect(element(by.id('link-2')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click and change url when ng-href specified', function() { + expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); + + element(by.id('link-3')).click(); + + // At this point, we navigate away from an AngularJS page, so we need + // to use browser.driver to get the base webdriver. + + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/123$/); + }); + }, 5000, 'page should navigate to /123'); + }); + + it('should execute ng-click but not reload when href empty string and name specified', function() { + element(by.id('link-4')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('4'); + expect(element(by.id('link-4')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when no href but name specified', function() { + element(by.id('link-5')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('5'); + expect(element(by.id('link-5')).getAttribute('href')).toBe(null); + }); + + it('should only change url when only ng-href', function() { + element(by.model('value')).clear(); + element(by.model('value')).sendKeys('6'); + expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); + + element(by.id('link-6')).click(); + + // At this point, we navigate away from an AngularJS page, so we need + // to use browser.driver to get the base webdriver. + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/6$/); + }); + }, 5000, 'page should navigate to /6'); + }); + +
    + */ + +/** + * @ngdoc directive + * @name ngSrc + * @restrict A + * @priority 99 + * + * @description + * Using AngularJS markup like `{{hash}}` in a `src` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until AngularJS replaces the expression inside + * `{{hash}}`. The `ngSrc` directive solves this problem. + * + * The buggy way to write it: + * ```html + * Description + * ``` + * + * The correct way to write it: + * ```html + * Description + * ``` + * + * @element IMG + * @param {template} ngSrc any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngSrcset + * @restrict A + * @priority 99 + * + * @description + * Using AngularJS markup like `{{hash}}` in a `srcset` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until AngularJS replaces the expression inside + * `{{hash}}`. The `ngSrcset` directive solves this problem. + * + * The buggy way to write it: + * ```html + * Description + * ``` + * + * The correct way to write it: + * ```html + * Description + * ``` + * + * @element IMG + * @param {template} ngSrcset any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngDisabled + * @restrict A + * @priority 100 + * + * @description + * + * This directive sets the `disabled` attribute on the element (typically a form control, + * e.g. `input`, `button`, `select` etc.) if the + * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `disabled` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    + +
    + + it('should toggle button', function() { + expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, + * then the `disabled` attribute will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngChecked + * @restrict A + * @priority 100 + * + * @description + * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy. + * + * Note that this directive should not be used together with {@link ngModel `ngModel`}, + * as this can lead to unexpected behavior. + * + * A special directive is necessary because we cannot use interpolation inside the `checked` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    + +
    + + it('should check both checkBoxes', function() { + expect(element(by.id('checkFollower')).getAttribute('checked')).toBeFalsy(); + element(by.model('leader')).click(); + expect(element(by.id('checkFollower')).getAttribute('checked')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, + * then the `checked` attribute will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngReadonly + * @restrict A + * @priority 100 + * + * @description + * + * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy. + * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on + * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information. + * + * A special directive is necessary because we cannot use interpolation inside the `readonly` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * @example + + +
    + +
    + + it('should toggle readonly attr', function() { + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); + }); + +
    + * + * @element INPUT + * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, + * then special attribute "readonly" will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngSelected + * @restrict A + * @priority 100 + * + * @description + * + * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `selected` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + *
    + * **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only + * sets the `selected` attribute on the element. If you are using `ngModel` on the select, you + * should not use `ngSelected` on the options, as `ngModel` will set the select value and + * selected options. + *
    + * + * @example + + +
    + +
    + + it('should select Greetings!', function() { + expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); + element(by.model('selected')).click(); + expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); + }); + +
    + * + * @element OPTION + * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, + * then special attribute "selected" will be set on the element + */ + +/** + * @ngdoc directive + * @name ngOpen + * @restrict A + * @priority 100 + * + * @description + * + * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `open` + * attribute. See the {@link guide/interpolation interpolation guide} for more info. + * + * ## A note about browser compatibility + * + * Internet Explorer and Edge do not support the `details` element, it is + * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead. + * + * @example + + +
    +
    + List +
      +
    • Apple
    • +
    • Orange
    • +
    • Durian
    • +
    +
    +
    + + it('should toggle open', function() { + expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); + element(by.model('open')).click(); + expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); + }); + +
    + * + * @element DETAILS + * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, + * then special attribute "open" will be set on the element + */ + +var ngAttributeAliasDirectives = {}; + +// boolean attrs are evaluated +forEach(BOOLEAN_ATTR, function(propName, attrName) { + // binding to multiple is not supported + if (propName === 'multiple') return; + + function defaultLinkFn(scope, element, attr) { + scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { + attr.$set(attrName, !!value); + }); + } + + var normalized = directiveNormalize('ng-' + attrName); + var linkFn = defaultLinkFn; + + if (propName === 'checked') { + linkFn = function(scope, element, attr) { + // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input + if (attr.ngModel !== attr[normalized]) { + defaultLinkFn(scope, element, attr); + } + }; + } + + ngAttributeAliasDirectives[normalized] = function() { + return { + restrict: 'A', + priority: 100, + link: linkFn + }; + }; +}); + +// aliased input attrs are evaluated +forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) { + ngAttributeAliasDirectives[ngAttr] = function() { + return { + priority: 100, + link: function(scope, element, attr) { + //special case ngPattern when a literal regular expression value + //is used as the expression (this way we don't have to watch anything). + if (ngAttr === 'ngPattern' && attr.ngPattern.charAt(0) === '/') { + var match = attr.ngPattern.match(REGEX_STRING_REGEXP); + if (match) { + attr.$set('ngPattern', new RegExp(match[1], match[2])); + return; + } + } + + scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) { + attr.$set(ngAttr, value); + }); + } + }; + }; +}); + +// ng-src, ng-srcset, ng-href are interpolated +forEach(['src', 'srcset', 'href'], function(attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 99, // it needs to run after the attributes are interpolated + link: function(scope, element, attr) { + var propName = attrName, + name = attrName; + + if (attrName === 'href' && + toString.call(element.prop('href')) === '[object SVGAnimatedString]') { + name = 'xlinkHref'; + attr.$attr[name] = 'xlink:href'; + propName = null; + } + + attr.$observe(normalized, function(value) { + if (!value) { + if (attrName === 'href') { + attr.$set(name, null); + } + return; + } + + attr.$set(name, value); + + // Support: IE 9-11 only + // On IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect. + // We use attr[attrName] value since $set can sanitize the url. + if (msie && propName) element.prop(propName, attr[name]); + }); + } + }; + }; +}); + +/* global -nullFormCtrl, -PENDING_CLASS, -SUBMITTED_CLASS + */ +var nullFormCtrl = { + $addControl: noop, + $$renameControl: nullFormRenameControl, + $removeControl: noop, + $setValidity: noop, + $setDirty: noop, + $setPristine: noop, + $setSubmitted: noop +}, +PENDING_CLASS = 'ng-pending', +SUBMITTED_CLASS = 'ng-submitted'; + +function nullFormRenameControl(control, name) { + control.$name = name; +} + +/** + * @ngdoc type + * @name form.FormController + * + * @property {boolean} $pristine True if user has not interacted with the form yet. + * @property {boolean} $dirty True if user has already interacted with the form. + * @property {boolean} $valid True if all of the containing forms and controls are valid. + * @property {boolean} $invalid True if at least one containing control or form is invalid. + * @property {boolean} $submitted True if user has submitted the form even if its invalid. + * + * @property {Object} $pending An object hash, containing references to controls or forms with + * pending validators, where: + * + * - keys are validations tokens (error names). + * - values are arrays of controls or forms that have a pending validator for the given error name. + * + * See {@link form.FormController#$error $error} for a list of built-in validation tokens. + * + * @property {Object} $error An object hash, containing references to controls or forms with failing + * validators, where: + * + * - keys are validation tokens (error names), + * - values are arrays of controls or forms that have a failing validator for the given error name. + * + * Built-in validation tokens: + * - `email` + * - `max` + * - `maxlength` + * - `min` + * - `minlength` + * - `number` + * - `pattern` + * - `required` + * - `url` + * - `date` + * - `datetimelocal` + * - `time` + * - `week` + * - `month` + * + * @description + * `FormController` keeps track of all its controls and nested forms as well as the state of them, + * such as being valid/invalid or dirty/pristine. + * + * Each {@link ng.directive:form form} directive creates an instance + * of `FormController`. + * + */ +//asks for $scope to fool the BC controller module +FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate']; +function FormController($element, $attrs, $scope, $animate, $interpolate) { + this.$$controls = []; + + // init state + this.$error = {}; + this.$$success = {}; + this.$pending = undefined; + this.$name = $interpolate($attrs.name || $attrs.ngForm || '')($scope); + this.$dirty = false; + this.$pristine = true; + this.$valid = true; + this.$invalid = false; + this.$submitted = false; + this.$$parentForm = nullFormCtrl; + + this.$$element = $element; + this.$$animate = $animate; + + setupValidity(this); +} + +FormController.prototype = { + /** + * @ngdoc method + * @name form.FormController#$rollbackViewValue + * + * @description + * Rollback all form controls pending updates to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. This method is typically needed by the reset button of + * a form that uses `ng-model-options` to pend updates. + */ + $rollbackViewValue: function() { + forEach(this.$$controls, function(control) { + control.$rollbackViewValue(); + }); + }, + + /** + * @ngdoc method + * @name form.FormController#$commitViewValue + * + * @description + * Commit all form controls pending updates to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. This method is rarely needed as `NgModelController` + * usually handles calling this in response to input events. + */ + $commitViewValue: function() { + forEach(this.$$controls, function(control) { + control.$commitViewValue(); + }); + }, + + /** + * @ngdoc method + * @name form.FormController#$addControl + * @param {object} control control object, either a {@link form.FormController} or an + * {@link ngModel.NgModelController} + * + * @description + * Register a control with the form. Input elements using ngModelController do this automatically + * when they are linked. + * + * Note that the current state of the control will not be reflected on the new parent form. This + * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine` + * state. + * + * However, if the method is used programmatically, for example by adding dynamically created controls, + * or controls that have been previously removed without destroying their corresponding DOM element, + * it's the developers responsibility to make sure the current state propagates to the parent form. + * + * For example, if an input control is added that is already `$dirty` and has `$error` properties, + * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form. + */ + $addControl: function(control) { + // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored + // and not added to the scope. Now we throw an error. + assertNotHasOwnProperty(control.$name, 'input'); + this.$$controls.push(control); + + if (control.$name) { + this[control.$name] = control; + } + + control.$$parentForm = this; + }, + + // Private API: rename a form control + $$renameControl: function(control, newName) { + var oldName = control.$name; + + if (this[oldName] === control) { + delete this[oldName]; + } + this[newName] = control; + control.$name = newName; + }, + + /** + * @ngdoc method + * @name form.FormController#$removeControl + * @param {object} control control object, either a {@link form.FormController} or an + * {@link ngModel.NgModelController} + * + * @description + * Deregister a control from the form. + * + * Input elements using ngModelController do this automatically when they are destroyed. + * + * Note that only the removed control's validation state (`$errors`etc.) will be removed from the + * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be + * different from case to case. For example, removing the only `$dirty` control from a form may or + * may not mean that the form is still `$dirty`. + */ + $removeControl: function(control) { + if (control.$name && this[control.$name] === control) { + delete this[control.$name]; + } + forEach(this.$pending, function(value, name) { + // eslint-disable-next-line no-invalid-this + this.$setValidity(name, null, control); + }, this); + forEach(this.$error, function(value, name) { + // eslint-disable-next-line no-invalid-this + this.$setValidity(name, null, control); + }, this); + forEach(this.$$success, function(value, name) { + // eslint-disable-next-line no-invalid-this + this.$setValidity(name, null, control); + }, this); + + arrayRemove(this.$$controls, control); + control.$$parentForm = nullFormCtrl; + }, + + /** + * @ngdoc method + * @name form.FormController#$setDirty + * + * @description + * Sets the form to a dirty state. + * + * This method can be called to add the 'ng-dirty' class and set the form to a dirty + * state (ng-dirty class). This method will also propagate to parent forms. + */ + $setDirty: function() { + this.$$animate.removeClass(this.$$element, PRISTINE_CLASS); + this.$$animate.addClass(this.$$element, DIRTY_CLASS); + this.$dirty = true; + this.$pristine = false; + this.$$parentForm.$setDirty(); + }, + + /** + * @ngdoc method + * @name form.FormController#$setPristine + * + * @description + * Sets the form to its pristine state. + * + * This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes + * the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted` + * state to false. + * + * This method will also propagate to all the controls contained in this form. + * + * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after + * saving or resetting it. + */ + $setPristine: function() { + this.$$animate.setClass(this.$$element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); + this.$dirty = false; + this.$pristine = true; + this.$submitted = false; + forEach(this.$$controls, function(control) { + control.$setPristine(); + }); + }, + + /** + * @ngdoc method + * @name form.FormController#$setUntouched + * + * @description + * Sets the form to its untouched state. + * + * This method can be called to remove the 'ng-touched' class and set the form controls to their + * untouched state (ng-untouched class). + * + * Setting a form controls back to their untouched state is often useful when setting the form + * back to its pristine state. + */ + $setUntouched: function() { + forEach(this.$$controls, function(control) { + control.$setUntouched(); + }); + }, + + /** + * @ngdoc method + * @name form.FormController#$setSubmitted + * + * @description + * Sets the form to its submitted state. + */ + $setSubmitted: function() { + this.$$animate.addClass(this.$$element, SUBMITTED_CLASS); + this.$submitted = true; + this.$$parentForm.$setSubmitted(); + } +}; + +/** + * @ngdoc method + * @name form.FormController#$setValidity + * + * @description + * Change the validity state of the form, and notify the parent form (if any). + * + * Application developers will rarely need to call this method directly. It is used internally, by + * {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a + * control's validity state to the parent `FormController`. + * + * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be + * assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for + * unfulfilled `$asyncValidators`), so that it is available for data-binding. The + * `validationErrorKey` should be in camelCase and will get converted into dash-case for + * class name. Example: `myError` will result in `ng-valid-my-error` and + * `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`. + * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending + * (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`. + * Skipped is used by AngularJS when validators do not run because of parse errors and when + * `$asyncValidators` do not run because any of the `$validators` failed. + * @param {NgModelController | FormController} controller - The controller whose validity state is + * triggering the change. + */ +addSetValidityMethod({ + clazz: FormController, + set: function(object, property, controller) { + var list = object[property]; + if (!list) { + object[property] = [controller]; + } else { + var index = list.indexOf(controller); + if (index === -1) { + list.push(controller); + } + } + }, + unset: function(object, property, controller) { + var list = object[property]; + if (!list) { + return; + } + arrayRemove(list, controller); + if (list.length === 0) { + delete object[property]; + } + } +}); + +/** + * @ngdoc directive + * @name ngForm + * @restrict EAC + * + * @description + * Nestable alias of {@link ng.directive:form `form`} directive. HTML + * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a + * sub-group of controls needs to be determined. + * + * Note: the purpose of `ngForm` is to group controls, + * but not to be a replacement for the `
    ` tag with all of its capabilities + * (e.g. posting to the server, ...). + * + * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + */ + + /** + * @ngdoc directive + * @name form + * @restrict E + * + * @description + * Directive that instantiates + * {@link form.FormController FormController}. + * + * If the `name` attribute is specified, the form controller is published onto the current scope under + * this name. + * + * ## Alias: {@link ng.directive:ngForm `ngForm`} + * + * In AngularJS, forms can be nested. This means that the outer form is valid when all of the child + * forms are valid as well. However, browsers do not allow nesting of `` elements, so + * AngularJS provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to + * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group + * of controls needs to be determined. + * + * ## CSS classes + * - `ng-valid` is set if the form is valid. + * - `ng-invalid` is set if the form is invalid. + * - `ng-pending` is set if the form is pending. + * - `ng-pristine` is set if the form is pristine. + * - `ng-dirty` is set if the form is dirty. + * - `ng-submitted` is set if the form was submitted. + * + * Keep in mind that ngAnimate can detect each of these classes when added and removed. + * + * + * ## Submitting a form and preventing the default action + * + * Since the role of forms in client-side AngularJS applications is different than in classical + * roundtrip apps, it is desirable for the browser not to translate the form submission into a full + * page reload that sends the data to the server. Instead some javascript logic should be triggered + * to handle the form submission in an application-specific way. + * + * For this reason, AngularJS prevents the default action (form submission to the server) unless the + * `` element has an `action` attribute specified. + * + * You can use one of the following two ways to specify what javascript method should be called when + * a form is submitted: + * + * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element + * - {@link ng.directive:ngClick ngClick} directive on the first + * button or input field of type submit (input[type=submit]) + * + * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} + * or {@link ng.directive:ngClick ngClick} directives. + * This is because of the following form submission rules in the HTML specification: + * + * - If a form has only one input field then hitting enter in this field triggers form submit + * (`ngSubmit`) + * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter + * doesn't trigger submit + * - if a form has one or more input fields and one or more buttons or input[type=submit] then + * hitting enter in any of the input fields will trigger the click handler on the *first* button or + * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) + * + * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is + * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` + * to have access to the updated model. + * + * @animations + * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. + * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any + * other validations that are performed within the form. Animations in ngForm are similar to how + * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well + * as JS animations. + * + * The following example shows a simple way to utilize CSS transitions to style a form element + * that has been rendered as invalid after it has been validated: + * + *
    + * //be sure to include ngAnimate as a module to hook into more
    + * //advanced animations
    + * .my-form {
    + *   transition:0.5s linear all;
    + *   background: white;
    + * }
    + * .my-form.ng-invalid {
    + *   background: red;
    + *   color:white;
    + * }
    + * 
    + * + * @example + + + + + + userType: + Required!
    + userType = {{userType}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + +
    + + it('should initialize to model', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + + expect(userType.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + var userInput = element(by.model('userType')); + + userInput.clear(); + userInput.sendKeys(''); + + expect(userType.getText()).toEqual('userType ='); + expect(valid.getText()).toContain('false'); + }); + +
    + * + * @param {string=} name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + */ +var formDirectiveFactory = function(isNgForm) { + return ['$timeout', '$parse', function($timeout, $parse) { + var formDirective = { + name: 'form', + restrict: isNgForm ? 'EAC' : 'E', + require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form + controller: FormController, + compile: function ngFormCompile(formElement, attr) { + // Setup initial state of the control + formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS); + + var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false); + + return { + pre: function ngFormPreLink(scope, formElement, attr, ctrls) { + var controller = ctrls[0]; + + // if `action` attr is not present on the form, prevent the default action (submission) + if (!('action' in attr)) { + // we can't use jq events because if a form is destroyed during submission the default + // action is not prevented. see #1238 + // + // IE 9 is not affected because it doesn't fire a submit event and try to do a full + // page reload if the form was destroyed by submission of the form via a click handler + // on a button in the form. Looks like an IE9 specific bug. + var handleFormSubmission = function(event) { + scope.$apply(function() { + controller.$commitViewValue(); + controller.$setSubmitted(); + }); + + event.preventDefault(); + }; + + formElement[0].addEventListener('submit', handleFormSubmission); + + // unregister the preventDefault listener so that we don't not leak memory but in a + // way that will achieve the prevention of the default action. + formElement.on('$destroy', function() { + $timeout(function() { + formElement[0].removeEventListener('submit', handleFormSubmission); + }, 0, false); + }); + } + + var parentFormCtrl = ctrls[1] || controller.$$parentForm; + parentFormCtrl.$addControl(controller); + + var setter = nameAttr ? getSetter(controller.$name) : noop; + + if (nameAttr) { + setter(scope, controller); + attr.$observe(nameAttr, function(newValue) { + if (controller.$name === newValue) return; + setter(scope, undefined); + controller.$$parentForm.$$renameControl(controller, newValue); + setter = getSetter(controller.$name); + setter(scope, controller); + }); + } + formElement.on('$destroy', function() { + controller.$$parentForm.$removeControl(controller); + setter(scope, undefined); + extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards + }); + } + }; + } + }; + + return formDirective; + + function getSetter(expression) { + if (expression === '') { + //create an assignable expression, so forms with an empty name can be renamed later + return $parse('this[""]').assign; + } + return $parse(expression).assign || noop; + } + }]; +}; + +var formDirective = formDirectiveFactory(); +var ngFormDirective = formDirectiveFactory(true); + + + +// helper methods +function setupValidity(instance) { + instance.$$classCache = {}; + instance.$$classCache[INVALID_CLASS] = !(instance.$$classCache[VALID_CLASS] = instance.$$element.hasClass(VALID_CLASS)); +} +function addSetValidityMethod(context) { + var clazz = context.clazz, + set = context.set, + unset = context.unset; + + clazz.prototype.$setValidity = function(validationErrorKey, state, controller) { + if (isUndefined(state)) { + createAndSet(this, '$pending', validationErrorKey, controller); + } else { + unsetAndCleanup(this, '$pending', validationErrorKey, controller); + } + if (!isBoolean(state)) { + unset(this.$error, validationErrorKey, controller); + unset(this.$$success, validationErrorKey, controller); + } else { + if (state) { + unset(this.$error, validationErrorKey, controller); + set(this.$$success, validationErrorKey, controller); + } else { + set(this.$error, validationErrorKey, controller); + unset(this.$$success, validationErrorKey, controller); + } + } + if (this.$pending) { + cachedToggleClass(this, PENDING_CLASS, true); + this.$valid = this.$invalid = undefined; + toggleValidationCss(this, '', null); + } else { + cachedToggleClass(this, PENDING_CLASS, false); + this.$valid = isObjectEmpty(this.$error); + this.$invalid = !this.$valid; + toggleValidationCss(this, '', this.$valid); + } + + // re-read the state as the set/unset methods could have + // combined state in this.$error[validationError] (used for forms), + // where setting/unsetting only increments/decrements the value, + // and does not replace it. + var combinedState; + if (this.$pending && this.$pending[validationErrorKey]) { + combinedState = undefined; + } else if (this.$error[validationErrorKey]) { + combinedState = false; + } else if (this.$$success[validationErrorKey]) { + combinedState = true; + } else { + combinedState = null; + } + + toggleValidationCss(this, validationErrorKey, combinedState); + this.$$parentForm.$setValidity(validationErrorKey, combinedState, this); + }; + + function createAndSet(ctrl, name, value, controller) { + if (!ctrl[name]) { + ctrl[name] = {}; + } + set(ctrl[name], value, controller); + } + + function unsetAndCleanup(ctrl, name, value, controller) { + if (ctrl[name]) { + unset(ctrl[name], value, controller); + } + if (isObjectEmpty(ctrl[name])) { + ctrl[name] = undefined; + } + } + + function cachedToggleClass(ctrl, className, switchValue) { + if (switchValue && !ctrl.$$classCache[className]) { + ctrl.$$animate.addClass(ctrl.$$element, className); + ctrl.$$classCache[className] = true; + } else if (!switchValue && ctrl.$$classCache[className]) { + ctrl.$$animate.removeClass(ctrl.$$element, className); + ctrl.$$classCache[className] = false; + } + } + + function toggleValidationCss(ctrl, validationErrorKey, isValid) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + + cachedToggleClass(ctrl, VALID_CLASS + validationErrorKey, isValid === true); + cachedToggleClass(ctrl, INVALID_CLASS + validationErrorKey, isValid === false); + } +} + +function isObjectEmpty(obj) { + if (obj) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + return false; + } + } + } + return true; +} + +/* global + VALID_CLASS: false, + INVALID_CLASS: false, + PRISTINE_CLASS: false, + DIRTY_CLASS: false, + ngModelMinErr: false +*/ + +// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 +var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/; +// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) +// Note: We are being more lenient, because browsers are too. +// 1. Scheme +// 2. Slashes +// 3. Username +// 4. Password +// 5. Hostname +// 6. Port +// 7. Path +// 8. Query +// 9. Fragment +// 1111111111111111 222 333333 44444 55555555555555555555555 666 77777777 8888888 999 +var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i; +// eslint-disable-next-line max-len +var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/; +var NUMBER_REGEXP = /^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/; +var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/; +var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; +var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/; +var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/; +var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; + +var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown'; +var PARTIAL_VALIDATION_TYPES = createMap(); +forEach('date,datetime-local,month,time,week'.split(','), function(type) { + PARTIAL_VALIDATION_TYPES[type] = true; +}); + +var inputType = { + + /** + * @ngdoc input + * @name input[text] + * + * @description + * Standard HTML text input with AngularJS data binding, inherited by most of the `input` elements. + * + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input. + * This parameter is ignored for input[type=password] controls, which will never trim the + * input. + * + * @example + + + +
    + +
    + + Required! + + Single word only! +
    + text = {{example.text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var text = element(by.binding('example.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if multi word', function() { + input.clear(); + input.sendKeys('hello world'); + + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'text': textInputType, + + /** + * @ngdoc input + * @name input[date] + * + * @description + * Input with date validation and transformation. In browsers that do not yet support + * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 + * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many + * modern browsers do not yet support this input type, it is important to provide cues to users on the + * expected input format via a placeholder or label. + * + * The model must always be a Date object, otherwise AngularJS will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a + * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute + * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5 + * constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be + * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute + * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5 + * constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "yyyy-MM-dd"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-MM-dd"')); + var valid = element(by.binding('myForm.input.$valid')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (see https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-10-22'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01-01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'date': createDateInputType('date', DATE_REGEXP, + createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), + 'yyyy-MM-dd'), + + /** + * @ngdoc input + * @name input[datetime-local] + * + * @description + * Input with datetime validation and transformation. In browsers that do not yet support + * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. + * + * The model must always be a Date object, otherwise AngularJS will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation + * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). + * Note that `min` will also add native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation + * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`). + * Note that `max` will also add native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"')); + var valid = element(by.binding('myForm.input.$valid')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2010-12-28T14:57:00'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01-01T23:59:00'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP, + createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']), + 'yyyy-MM-ddTHH:mm:ss.sss'), + + /** + * @ngdoc input + * @name input[time] + * + * @description + * Input with time validation and transformation. In browsers that do not yet support + * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a + * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. + * + * The model must always be a Date object, otherwise AngularJS will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this + * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add + * native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this + * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add + * native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the + * `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the + * `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "HH:mm:ss"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "HH:mm:ss"')); + var valid = element(by.binding('myForm.input.$valid')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('14:57:00'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('23:59:00'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'time': createDateInputType('time', TIME_REGEXP, + createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']), + 'HH:mm:ss.sss'), + + /** + * @ngdoc input + * @name input[week] + * + * @description + * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support + * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * week format (yyyy-W##), for example: `2013-W02`. + * + * The model must always be a Date object, otherwise AngularJS will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this + * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add + * native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this + * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add + * native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + +
    + + Required! + + Not a valid date! +
    + value = {{example.value | date: "yyyy-Www"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-Www"')); + var valid = element(by.binding('myForm.input.$valid')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-W01'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-W01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'), + + /** + * @ngdoc input + * @name input[month] + * + * @description + * Input with month validation and transformation. In browsers that do not yet support + * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * month format (yyyy-MM), for example: `2009-01`. + * + * The model must always be a Date object, otherwise AngularJS will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * If the model is not set to the first of the month, the next view to model update will set it + * to the first of the month. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this + * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add + * native HTML5 constraint validation. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this + * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add + * native HTML5 constraint validation. + * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string + * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute. + * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string + * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute. + + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + + +
    + + Required! + + Not a valid month! +
    + value = {{example.value | date: "yyyy-MM"}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value | date: "yyyy-MM"')); + var valid = element(by.binding('myForm.input.$valid')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-10'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + +
    + */ + 'month': createDateInputType('month', MONTH_REGEXP, + createDateParser(MONTH_REGEXP, ['yyyy', 'MM']), + 'yyyy-MM'), + + /** + * @ngdoc input + * @name input[number] + * + * @description + * Text input with number validation and transformation. Sets the `number` validation + * error if not a valid number. + * + *
    + * The model must always be of type `number` otherwise AngularJS will throw an error. + * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt} + * error docs for more information and an example of how to convert your model if necessary. + *
    + * + * ## Issues with HTML5 constraint validation + * + * In browsers that follow the + * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29), + * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}. + * If a non-number is entered in the input, the browser will report the value as an empty string, + * which means the view / model values in `ngModel` and subsequently the scope value + * will also be an empty string. + * + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * Can be interpolated. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * Can be interpolated. + * @param {string=} ngMin Like `min`, sets the `min` validation error key if the value entered is less than `ngMin`, + * but does not trigger HTML5 native validation. Takes an expression. + * @param {string=} ngMax Like `max`, sets the `max` validation error key if the value entered is greater than `ngMax`, + * but does not trigger HTML5 native validation. Takes an expression. + * @param {string=} step Sets the `step` validation error key if the value entered does not fit the `step` constraint. + * Can be interpolated. + * @param {string=} ngStep Like `step`, sets the `step` validation error key if the value entered does not fit the `ngStep` constraint, + * but does not trigger HTML5 native validation. Takes an expression. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + +
    + + Required! + + Not valid number! +
    + value = {{example.value}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    +
    +
    + + var value = element(by.binding('example.value')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + it('should initialize to model', function() { + expect(value.getText()).toContain('12'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if over max', function() { + input.clear(); + input.sendKeys('123'); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'number': numberInputType, + + + /** + * @ngdoc input + * @name input[url] + * + * @description + * Text input with URL validation. Sets the `url` validation error key if the content is not a + * valid URL. + * + *
    + * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex + * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify + * the built-in validators (see the {@link guide/forms Forms guide}) + *
    + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    +
    + + var text = element(by.binding('url.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('url.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('http://google.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not url', function() { + input.clear(); + input.sendKeys('box'); + + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'url': urlInputType, + + + /** + * @ngdoc input + * @name input[email] + * + * @description + * Text input with email validation. Sets the `email` validation error key if not a valid email + * address. + * + *
    + * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex + * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can + * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide}) + *
    + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    + +
    + + Required! + + Not valid email! +
    + text = {{email.text}}
    + myForm.input.$valid = {{myForm.input.$valid}}
    + myForm.input.$error = {{myForm.input.$error}}
    + myForm.$valid = {{myForm.$valid}}
    + myForm.$error.required = {{!!myForm.$error.required}}
    + myForm.$error.email = {{!!myForm.$error.email}}
    +
    +
    + + var text = element(by.binding('email.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('email.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('me@example.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not email', function() { + input.clear(); + input.sendKeys('xxx'); + + expect(valid.getText()).toContain('false'); + }); + +
    + */ + 'email': emailInputType, + + + /** + * @ngdoc input + * @name input[radio] + * + * @description + * HTML radio button. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string} value The value to which the `ngModel` expression should be set when selected. + * Note that `value` only supports `string` values, i.e. the scope model needs to be a string, + * too. Use `ngValue` if you need complex models (`number`, `object`, ...). + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * @param {string} ngValue AngularJS expression to which `ngModel` will be be set when the radio + * is selected. Should be used instead of the `value` attribute if you need + * a non-string `ngModel` (`boolean`, `array`, ...). + * + * @example + + + +
    +
    +
    +
    + color = {{color.name | json}}
    +
    + Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. +
    + + it('should change state', function() { + var inputs = element.all(by.model('color.name')); + var color = element(by.binding('color.name')); + + expect(color.getText()).toContain('blue'); + + inputs.get(0).click(); + expect(color.getText()).toContain('red'); + + inputs.get(1).click(); + expect(color.getText()).toContain('green'); + }); + +
    + */ + 'radio': radioInputType, + + /** + * @ngdoc input + * @name input[range] + * + * @description + * Native range input with validation and transformation. + * + * The model for the range input must always be a `Number`. + * + * IE9 and other browsers that do not support the `range` type fall back + * to a text input without any default values for `min`, `max` and `step`. Model binding, + * validation and number parsing are nevertheless supported. + * + * Browsers that support range (latest Chrome, Safari, Firefox, Edge) treat `input[range]` + * in a way that never allows the input to hold an invalid value. That means: + * - any non-numerical value is set to `(max + min) / 2`. + * - any numerical value that is less than the current min val, or greater than the current max val + * is set to the min / max val respectively. + * - additionally, the current `step` is respected, so the nearest value that satisfies a step + * is used. + * + * See the [HTML Spec on input[type=range]](https://www.w3.org/TR/html5/forms.html#range-state-(type=range)) + * for more info. + * + * This has the following consequences for AngularJS: + * + * Since the element value should always reflect the current model value, a range input + * will set the bound ngModel expression to the value that the browser has set for the + * input element. For example, in the following input ``, + * if the application sets `model.value = null`, the browser will set the input to `'50'`. + * AngularJS will then set the model to `50`, to prevent input and model value being out of sync. + * + * That means the model for range will immediately be set to `50` after `ngModel` has been + * initialized. It also means a range input can never have the required error. + * + * This does not only affect changes to the model value, but also to the values of the `min`, + * `max`, and `step` attributes. When these change in a way that will cause the browser to modify + * the input value, AngularJS will also update the model value. + * + * Automatic value adjustment also means that a range input element can never have the `required`, + * `min`, or `max` errors. + * + * However, `step` is currently only fully implemented by Firefox. Other browsers have problems + * when the step value changes dynamically - they do not adjust the element value correctly, but + * instead may set the `stepMismatch` error. If that's the case, the AngularJS will set the `step` + * error on the input, and set the model to `undefined`. + * + * Note that `input[range]` is not compatible with`ngMax`, `ngMin`, and `ngStep`, because they do + * not set the `min` and `max` attributes, which means that the browser won't automatically adjust + * the input value based on their values, and will always assume min = 0, max = 100, and step = 1. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation to ensure that the value entered is greater + * than `min`. Can be interpolated. + * @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`. + * Can be interpolated. + * @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step` + * Can be interpolated. + * @param {string=} ngChange AngularJS expression to be executed when the ngModel value changes due + * to user interaction with the input element. + * @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the + * element. **Note** : `ngChecked` should not be used alongside `ngModel`. + * Checkout {@link ng.directive:ngChecked ngChecked} for usage. + * + * @example + + + +
    + + Model as range: +
    + Model as number:
    + Min:
    + Max:
    + value = {{value}}
    + myForm.range.$valid = {{myForm.range.$valid}}
    + myForm.range.$error = {{myForm.range.$error}} +
    +
    +
    + + * ## Range Input with ngMin & ngMax attributes + + * @example + + + +
    + Model as range: +
    + Model as number:
    + Min:
    + Max:
    + value = {{value}}
    + myForm.range.$valid = {{myForm.range.$valid}}
    + myForm.range.$error = {{myForm.range.$error}} +
    +
    +
    + + */ + 'range': rangeInputType, + + /** + * @ngdoc input + * @name input[checkbox] + * + * @description + * HTML checkbox. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {expression=} ngTrueValue The value to which the expression should be set when selected. + * @param {expression=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + + + +
    +
    +
    + value1 = {{checkboxModel.value1}}
    + value2 = {{checkboxModel.value2}}
    +
    +
    + + it('should change state', function() { + var value1 = element(by.binding('checkboxModel.value1')); + var value2 = element(by.binding('checkboxModel.value2')); + + expect(value1.getText()).toContain('true'); + expect(value2.getText()).toContain('YES'); + + element(by.model('checkboxModel.value1')).click(); + element(by.model('checkboxModel.value2')).click(); + + expect(value1.getText()).toContain('false'); + expect(value2.getText()).toContain('NO'); + }); + +
    + */ + 'checkbox': checkboxInputType, + + 'hidden': noop, + 'button': noop, + 'submit': noop, + 'reset': noop, + 'file': noop +}; + +function stringBasedInputType(ctrl) { + ctrl.$formatters.push(function(value) { + return ctrl.$isEmpty(value) ? value : value.toString(); + }); +} + +function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); +} + +function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { + var type = lowercase(element[0].type); + + // In composition mode, users are still inputting intermediate text buffer, + // hold the listener until composition is done. + // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent + if (!$sniffer.android) { + var composing = false; + + element.on('compositionstart', function() { + composing = true; + }); + + element.on('compositionend', function() { + composing = false; + listener(); + }); + } + + var timeout; + + var listener = function(ev) { + if (timeout) { + $browser.defer.cancel(timeout); + timeout = null; + } + if (composing) return; + var value = element.val(), + event = ev && ev.type; + + // By default we will trim the value + // If the attribute ng-trim exists we will avoid trimming + // If input type is 'password', the value is never trimmed + if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { + value = trim(value); + } + + // If a control is suffering from bad input (due to native validators), browsers discard its + // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the + // control's value is the same empty value twice in a row. + if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) { + ctrl.$setViewValue(value, event); + } + }; + + // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the + // input event on backspace, delete or cut + if ($sniffer.hasEvent('input')) { + element.on('input', listener); + } else { + var deferListener = function(ev, input, origValue) { + if (!timeout) { + timeout = $browser.defer(function() { + timeout = null; + if (!input || input.value !== origValue) { + listener(ev); + } + }); + } + }; + + element.on('keydown', /** @this */ function(event) { + var key = event.keyCode; + + // ignore + // command modifiers arrows + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; + + deferListener(event, this, this.value); + }); + + // if user modifies input value using context menu in IE, we need "paste", "cut" and "drop" events to catch it + if ($sniffer.hasEvent('paste')) { + element.on('paste cut drop', deferListener); + } + } + + // if user paste into input using mouse on older browser + // or form autocomplete on newer browser, we need "change" event to catch it + element.on('change', listener); + + // Some native input types (date-family) have the ability to change validity without + // firing any input/change events. + // For these event types, when native validators are present and the browser supports the type, + // check for validity changes on various DOM events. + if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) { + element.on(PARTIAL_VALIDATION_EVENTS, /** @this */ function(ev) { + if (!timeout) { + var validity = this[VALIDITY_STATE_PROPERTY]; + var origBadInput = validity.badInput; + var origTypeMismatch = validity.typeMismatch; + timeout = $browser.defer(function() { + timeout = null; + if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) { + listener(ev); + } + }); + } + }); + } + + ctrl.$render = function() { + // Workaround for Firefox validation #12102. + var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue; + if (element.val() !== value) { + element.val(value); + } + }; +} + +function weekParser(isoWeek, existingDate) { + if (isDate(isoWeek)) { + return isoWeek; + } + + if (isString(isoWeek)) { + WEEK_REGEXP.lastIndex = 0; + var parts = WEEK_REGEXP.exec(isoWeek); + if (parts) { + var year = +parts[1], + week = +parts[2], + hours = 0, + minutes = 0, + seconds = 0, + milliseconds = 0, + firstThurs = getFirstThursdayOfYear(year), + addDays = (week - 1) * 7; + + if (existingDate) { + hours = existingDate.getHours(); + minutes = existingDate.getMinutes(); + seconds = existingDate.getSeconds(); + milliseconds = existingDate.getMilliseconds(); + } + + return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds); + } + } + + return NaN; +} + +function createDateParser(regexp, mapping) { + return function(iso, date) { + var parts, map; + + if (isDate(iso)) { + return iso; + } + + if (isString(iso)) { + // When a date is JSON'ified to wraps itself inside of an extra + // set of double quotes. This makes the date parsing code unable + // to match the date string and parse it as a date. + if (iso.charAt(0) === '"' && iso.charAt(iso.length - 1) === '"') { + iso = iso.substring(1, iso.length - 1); + } + if (ISO_DATE_REGEXP.test(iso)) { + return new Date(iso); + } + regexp.lastIndex = 0; + parts = regexp.exec(iso); + + if (parts) { + parts.shift(); + if (date) { + map = { + yyyy: date.getFullYear(), + MM: date.getMonth() + 1, + dd: date.getDate(), + HH: date.getHours(), + mm: date.getMinutes(), + ss: date.getSeconds(), + sss: date.getMilliseconds() / 1000 + }; + } else { + map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 }; + } + + forEach(parts, function(part, index) { + if (index < mapping.length) { + map[mapping[index]] = +part; + } + }); + return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0); + } + } + + return NaN; + }; +} + +function createDateInputType(type, regexp, parseDate, format) { + return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { + badInputChecker(scope, element, attr, ctrl); + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + var timezone = ctrl && ctrl.$options.getOption('timezone'); + var previousDate; + + ctrl.$$parserName = type; + ctrl.$parsers.push(function(value) { + if (ctrl.$isEmpty(value)) return null; + if (regexp.test(value)) { + // Note: We cannot read ctrl.$modelValue, as there might be a different + // parser/formatter in the processing chain so that the model + // contains some different data format! + var parsedDate = parseDate(value, previousDate); + if (timezone) { + parsedDate = convertTimezoneToLocal(parsedDate, timezone); + } + return parsedDate; + } + return undefined; + }); + + ctrl.$formatters.push(function(value) { + if (value && !isDate(value)) { + throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); + } + if (isValidDate(value)) { + previousDate = value; + if (previousDate && timezone) { + previousDate = convertTimezoneToLocal(previousDate, timezone, true); + } + return $filter('date')(value, format, timezone); + } else { + previousDate = null; + return ''; + } + }); + + if (isDefined(attr.min) || attr.ngMin) { + var minVal; + ctrl.$validators.min = function(value) { + return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; + }; + attr.$observe('min', function(val) { + minVal = parseObservedDateValue(val); + ctrl.$validate(); + }); + } + + if (isDefined(attr.max) || attr.ngMax) { + var maxVal; + ctrl.$validators.max = function(value) { + return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; + }; + attr.$observe('max', function(val) { + maxVal = parseObservedDateValue(val); + ctrl.$validate(); + }); + } + + function isValidDate(value) { + // Invalid Date: getTime() returns NaN + return value && !(value.getTime && value.getTime() !== value.getTime()); + } + + function parseObservedDateValue(val) { + return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val; + } + }; +} + +function badInputChecker(scope, element, attr, ctrl) { + var node = element[0]; + var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity); + if (nativeValidation) { + ctrl.$parsers.push(function(value) { + var validity = element.prop(VALIDITY_STATE_PROPERTY) || {}; + return validity.badInput || validity.typeMismatch ? undefined : value; + }); + } +} + +function numberFormatterParser(ctrl) { + ctrl.$$parserName = 'number'; + ctrl.$parsers.push(function(value) { + if (ctrl.$isEmpty(value)) return null; + if (NUMBER_REGEXP.test(value)) return parseFloat(value); + return undefined; + }); + + ctrl.$formatters.push(function(value) { + if (!ctrl.$isEmpty(value)) { + if (!isNumber(value)) { + throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); + } + value = value.toString(); + } + return value; + }); +} + +function parseNumberAttrVal(val) { + if (isDefined(val) && !isNumber(val)) { + val = parseFloat(val); + } + return !isNumberNaN(val) ? val : undefined; +} + +function isNumberInteger(num) { + // See http://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066 + // (minus the assumption that `num` is a number) + + // eslint-disable-next-line no-bitwise + return (num | 0) === num; +} + +function countDecimals(num) { + var numString = num.toString(); + var decimalSymbolIndex = numString.indexOf('.'); + + if (decimalSymbolIndex === -1) { + if (-1 < num && num < 1) { + // It may be in the exponential notation format (`1e-X`) + var match = /e-(\d+)$/.exec(numString); + + if (match) { + return Number(match[1]); + } + } + + return 0; + } + + return numString.length - decimalSymbolIndex - 1; +} + +function isValidForStep(viewValue, stepBase, step) { + // At this point `stepBase` and `step` are expected to be non-NaN values + // and `viewValue` is expected to be a valid stringified number. + var value = Number(viewValue); + + var isNonIntegerValue = !isNumberInteger(value); + var isNonIntegerStepBase = !isNumberInteger(stepBase); + var isNonIntegerStep = !isNumberInteger(step); + + // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or + // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers. + if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) { + var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0; + var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0; + var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0; + + var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals); + var multiplier = Math.pow(10, decimalCount); + + value = value * multiplier; + stepBase = stepBase * multiplier; + step = step * multiplier; + + if (isNonIntegerValue) value = Math.round(value); + if (isNonIntegerStepBase) stepBase = Math.round(stepBase); + if (isNonIntegerStep) step = Math.round(step); + } + + return (value - stepBase) % step === 0; +} + +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { + badInputChecker(scope, element, attr, ctrl); + numberFormatterParser(ctrl); + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var minVal; + var maxVal; + + if (isDefined(attr.min) || attr.ngMin) { + ctrl.$validators.min = function(value) { + return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal; + }; + + attr.$observe('min', function(val) { + minVal = parseNumberAttrVal(val); + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + }); + } + + if (isDefined(attr.max) || attr.ngMax) { + ctrl.$validators.max = function(value) { + return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal; + }; + + attr.$observe('max', function(val) { + maxVal = parseNumberAttrVal(val); + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + }); + } + + if (isDefined(attr.step) || attr.ngStep) { + var stepVal; + ctrl.$validators.step = function(modelValue, viewValue) { + return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) || + isValidForStep(viewValue, minVal || 0, stepVal); + }; + + attr.$observe('step', function(val) { + stepVal = parseNumberAttrVal(val); + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + }); + } +} + +function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) { + badInputChecker(scope, element, attr, ctrl); + numberFormatterParser(ctrl); + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + + var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range', + minVal = supportsRange ? 0 : undefined, + maxVal = supportsRange ? 100 : undefined, + stepVal = supportsRange ? 1 : undefined, + validity = element[0].validity, + hasMinAttr = isDefined(attr.min), + hasMaxAttr = isDefined(attr.max), + hasStepAttr = isDefined(attr.step); + + var originalRender = ctrl.$render; + + ctrl.$render = supportsRange && isDefined(validity.rangeUnderflow) && isDefined(validity.rangeOverflow) ? + //Browsers that implement range will set these values automatically, but reading the adjusted values after + //$render would cause the min / max validators to be applied with the wrong value + function rangeRender() { + originalRender(); + ctrl.$setViewValue(element.val()); + } : + originalRender; + + if (hasMinAttr) { + ctrl.$validators.min = supportsRange ? + // Since all browsers set the input to a valid value, we don't need to check validity + function noopMinValidator() { return true; } : + // non-support browsers validate the min val + function minValidator(modelValue, viewValue) { + return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal; + }; + + setInitialValueAndObserver('min', minChange); + } + + if (hasMaxAttr) { + ctrl.$validators.max = supportsRange ? + // Since all browsers set the input to a valid value, we don't need to check validity + function noopMaxValidator() { return true; } : + // non-support browsers validate the max val + function maxValidator(modelValue, viewValue) { + return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal; + }; + + setInitialValueAndObserver('max', maxChange); + } + + if (hasStepAttr) { + ctrl.$validators.step = supportsRange ? + function nativeStepValidator() { + // Currently, only FF implements the spec on step change correctly (i.e. adjusting the + // input element value to a valid value). It's possible that other browsers set the stepMismatch + // validity error instead, so we can at least report an error in that case. + return !validity.stepMismatch; + } : + // ngStep doesn't set the setp attr, so the browser doesn't adjust the input value as setting step would + function stepValidator(modelValue, viewValue) { + return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) || + isValidForStep(viewValue, minVal || 0, stepVal); + }; + + setInitialValueAndObserver('step', stepChange); + } + + function setInitialValueAndObserver(htmlAttrName, changeFn) { + // interpolated attributes set the attribute value only after a digest, but we need the + // attribute value when the input is first rendered, so that the browser can adjust the + // input value based on the min/max value + element.attr(htmlAttrName, attr[htmlAttrName]); + attr.$observe(htmlAttrName, changeFn); + } + + function minChange(val) { + minVal = parseNumberAttrVal(val); + // ignore changes before model is initialized + if (isNumberNaN(ctrl.$modelValue)) { + return; + } + + if (supportsRange) { + var elVal = element.val(); + // IE11 doesn't set the el val correctly if the minVal is greater than the element value + if (minVal > elVal) { + elVal = minVal; + element.val(elVal); + } + ctrl.$setViewValue(elVal); + } else { + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + } + } + + function maxChange(val) { + maxVal = parseNumberAttrVal(val); + // ignore changes before model is initialized + if (isNumberNaN(ctrl.$modelValue)) { + return; + } + + if (supportsRange) { + var elVal = element.val(); + // IE11 doesn't set the el val correctly if the maxVal is less than the element value + if (maxVal < elVal) { + element.val(maxVal); + // IE11 and Chrome don't set the value to the minVal when max < min + elVal = maxVal < minVal ? minVal : maxVal; + } + ctrl.$setViewValue(elVal); + } else { + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + } + } + + function stepChange(val) { + stepVal = parseNumberAttrVal(val); + // ignore changes before model is initialized + if (isNumberNaN(ctrl.$modelValue)) { + return; + } + + // Some browsers don't adjust the input value correctly, but set the stepMismatch error + if (supportsRange && ctrl.$viewValue !== element.val()) { + ctrl.$setViewValue(element.val()); + } else { + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + } + } +} + +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { + // Note: no badInputChecker here by purpose as `url` is only a validation + // in browsers, i.e. we can always read out input.value even if it is not valid! + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); + + ctrl.$$parserName = 'url'; + ctrl.$validators.url = function(modelValue, viewValue) { + var value = modelValue || viewValue; + return ctrl.$isEmpty(value) || URL_REGEXP.test(value); + }; +} + +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { + // Note: no badInputChecker here by purpose as `url` is only a validation + // in browsers, i.e. we can always read out input.value even if it is not valid! + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); + + ctrl.$$parserName = 'email'; + ctrl.$validators.email = function(modelValue, viewValue) { + var value = modelValue || viewValue; + return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); + }; +} + +function radioInputType(scope, element, attr, ctrl) { + var doTrim = !attr.ngTrim || trim(attr.ngTrim) !== 'false'; + // make the name unique, if not defined + if (isUndefined(attr.name)) { + element.attr('name', nextUid()); + } + + var listener = function(ev) { + var value; + if (element[0].checked) { + value = attr.value; + if (doTrim) { + value = trim(value); + } + ctrl.$setViewValue(value, ev && ev.type); + } + }; + + element.on('click', listener); + + ctrl.$render = function() { + var value = attr.value; + if (doTrim) { + value = trim(value); + } + element[0].checked = (value === ctrl.$viewValue); + }; + + attr.$observe('value', ctrl.$render); +} + +function parseConstantExpr($parse, context, name, expression, fallback) { + var parseFn; + if (isDefined(expression)) { + parseFn = $parse(expression); + if (!parseFn.constant) { + throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' + + '`{1}`.', name, expression); + } + return parseFn(context); + } + return fallback; +} + +function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { + var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true); + var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false); + + var listener = function(ev) { + ctrl.$setViewValue(element[0].checked, ev && ev.type); + }; + + element.on('click', listener); + + ctrl.$render = function() { + element[0].checked = ctrl.$viewValue; + }; + + // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` + // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert + // it to a boolean. + ctrl.$isEmpty = function(value) { + return value === false; + }; + + ctrl.$formatters.push(function(value) { + return equals(value, trueValue); + }); + + ctrl.$parsers.push(function(value) { + return value ? trueValue : falseValue; + }); +} + + +/** + * @ngdoc directive + * @name textarea + * @restrict E + * + * @description + * HTML textarea element control with AngularJS data-binding. The data-binding and validation + * properties of this element are exactly the same as those of the + * {@link ng.directive:input input element}. + * + * @param {string} ngModel Assignable AngularJS expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any + * length. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue} + * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value. + * If the expression evaluates to a RegExp object, then this is used directly. + * If the expression evaluates to a string, then it will be converted to a RegExp + * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to + * `new RegExp('^abc$')`.
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + * @param {string=} ngChange AngularJS expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input. + * + * @knownIssue + * + * When specifying the `placeholder` attribute of ` + *
    {{ list | json }}
    + * + * + * it("should split the text by newlines", function() { + * var listInput = element(by.model('list')); + * var output = element(by.binding('list | json')); + * listInput.sendKeys('abc\ndef\nghi'); + * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]'); + * }); + * + * + * + */ +var ngListDirective = function() { + return { + restrict: 'A', + priority: 100, + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + var ngList = attr.ngList || ', '; + var trimValues = attr.ngTrim !== 'false'; + var separator = trimValues ? trim(ngList) : ngList; + + var parse = function(viewValue) { + // If the viewValue is invalid (say required but empty) it will be `undefined` + if (isUndefined(viewValue)) return; + + var list = []; + + if (viewValue) { + forEach(viewValue.split(separator), function(value) { + if (value) list.push(trimValues ? trim(value) : value); + }); + } + + return list; + }; + + ctrl.$parsers.push(parse); + ctrl.$formatters.push(function(value) { + if (isArray(value)) { + return value.join(ngList); + } + + return undefined; + }); + + // Override the standard $isEmpty because an empty array means the input is empty. + ctrl.$isEmpty = function(value) { + return !value || !value.length; + }; + } + }; +}; + +/* global VALID_CLASS: true, + INVALID_CLASS: true, + PRISTINE_CLASS: true, + DIRTY_CLASS: true, + UNTOUCHED_CLASS: true, + TOUCHED_CLASS: true, + PENDING_CLASS: true, + addSetValidityMethod: true, + setupValidity: true, + defaultModelOptions: false +*/ + + +var VALID_CLASS = 'ng-valid', + INVALID_CLASS = 'ng-invalid', + PRISTINE_CLASS = 'ng-pristine', + DIRTY_CLASS = 'ng-dirty', + UNTOUCHED_CLASS = 'ng-untouched', + TOUCHED_CLASS = 'ng-touched', + EMPTY_CLASS = 'ng-empty', + NOT_EMPTY_CLASS = 'ng-not-empty'; + +var ngModelMinErr = minErr('ngModel'); + +/** + * @ngdoc type + * @name ngModel.NgModelController + * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a + * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue + * is set. + * + * @property {*} $modelValue The value in the model that the control is bound to. + * + * @property {Array.} $parsers Array of functions to execute, as a pipeline, whenever + * the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue + `$viewValue`} from the DOM, usually via user input. + See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation. + Note that the `$parsers` are not called when the bound ngModel expression changes programmatically. + + The functions are called in array order, each passing + its return value through to the next. The last return value is forwarded to the + {@link ngModel.NgModelController#$validators `$validators`} collection. + + Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue + `$viewValue`}. + + Returning `undefined` from a parser means a parse error occurred. In that case, + no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel` + will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`} + is set to `true`. The parse error is stored in `ngModel.$error.parse`. + + This simple example shows a parser that would convert text input value to lowercase: + * ```js + * function parse(value) { + * if (value) { + * return value.toLowerCase(); + * } + * } + * ngModelController.$parsers.push(parse); + * ``` + + * + * @property {Array.} $formatters Array of functions to execute, as a pipeline, whenever + the bound ngModel expression changes programmatically. The `$formatters` are not called when the + value of the control is changed by user interaction. + + Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue + `$modelValue`} for display in the control. + + The functions are called in reverse array order, each passing the value through to the + next. The last return value is used as the actual DOM value. + + This simple example shows a formatter that would convert the model value to uppercase: + + * ```js + * function format(value) { + * if (value) { + * return value.toUpperCase(); + * } + * } + * ngModel.$formatters.push(format); + * ``` + * + * @property {Object.} $validators A collection of validators that are applied + * whenever the model value changes. The key value within the object refers to the name of the + * validator while the function refers to the validation operation. The validation operation is + * provided with the model value as an argument and must return a true or false value depending + * on the response of that validation. + * + * ```js + * ngModel.$validators.validCharacters = function(modelValue, viewValue) { + * var value = modelValue || viewValue; + * return /[0-9]+/.test(value) && + * /[a-z]+/.test(value) && + * /[A-Z]+/.test(value) && + * /\W+/.test(value); + * }; + * ``` + * + * @property {Object.} $asyncValidators A collection of validations that are expected to + * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided + * is expected to return a promise when it is run during the model validation process. Once the promise + * is delivered then the validation status will be set to true when fulfilled and false when rejected. + * When the asynchronous validators are triggered, each of the validators will run in parallel and the model + * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator + * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators + * will only run once all synchronous validators have passed. + * + * Please note that if $http is used then it is important that the server returns a success HTTP response code + * in order to fulfill the validation and a status level of `4xx` in order to reject the validation. + * + * ```js + * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) { + * var value = modelValue || viewValue; + * + * // Lookup user by username + * return $http.get('/api/users/' + value). + * then(function resolved() { + * //username exists, this means validation fails + * return $q.reject('exists'); + * }, function rejected() { + * //username does not exist, therefore this validation passes + * return true; + * }); + * }; + * ``` + * + * @property {Array.} $viewChangeListeners Array of functions to execute whenever + * a change to {@link ngModel.NgModelController#$viewValue `$viewValue`} has caused a change + * to {@link ngModel.NgModelController#$modelValue `$modelValue`}. + * It is called with no arguments, and its return value is ignored. + * This can be used in place of additional $watches against the model value. + * + * @property {Object} $error An object hash with all failing validator ids as keys. + * @property {Object} $pending An object hash with all pending validator ids as keys. + * + * @property {boolean} $untouched True if control has not lost focus yet. + * @property {boolean} $touched True if control has lost focus. + * @property {boolean} $pristine True if user has not interacted with the control yet. + * @property {boolean} $dirty True if user has already interacted with the control. + * @property {boolean} $valid True if there is no error. + * @property {boolean} $invalid True if at least one error on the control. + * @property {string} $name The name attribute of the control. + * + * @description + * + * `NgModelController` provides API for the {@link ngModel `ngModel`} directive. + * The controller contains services for data-binding, validation, CSS updates, and value formatting + * and parsing. It purposefully does not contain any logic which deals with DOM rendering or + * listening to DOM events. + * Such DOM related logic should be provided by other directives which make use of + * `NgModelController` for data-binding to control elements. + * AngularJS provides this DOM logic for most {@link input `input`} elements. + * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example + * custom control example} that uses `ngModelController` to bind to `contenteditable` elements. + * + * @example + * ### Custom Control Example + * This example shows how to use `NgModelController` with a custom control to achieve + * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) + * collaborate together to achieve the desired result. + * + * `contenteditable` is an HTML5 attribute, which tells the browser to let the element + * contents be edited in place by the user. + * + * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} + * module to automatically remove "bad" content like inline event listener (e.g. ``). + * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks + * that content using the `$sce` service. + * + * + + [contenteditable] { + border: 1px solid black; + background-color: white; + min-height: 20px; + } + + .ng-invalid { + border: 1px solid red; + } + + + + angular.module('customControl', ['ngSanitize']). + directive('contenteditable', ['$sce', function($sce) { + return { + restrict: 'A', // only activate on element attribute + require: '?ngModel', // get a hold of NgModelController + link: function(scope, element, attrs, ngModel) { + if (!ngModel) return; // do nothing if no ng-model + + // Specify how UI should be updated + ngModel.$render = function() { + element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); + }; + + // Listen for change events to enable binding + element.on('blur keyup change', function() { + scope.$evalAsync(read); + }); + read(); // initialize + + // Write data to the model + function read() { + var html = element.html(); + // When we clear the content editable the browser leaves a
    behind + // If strip-br attribute is provided then we strip this out + if (attrs.stripBr && html === '
    ') { + html = ''; + } + ngModel.$setViewValue(html); + } + } + }; + }]); +
    + +
    +
    Change me!
    + Required! +
    + +
    +
    + + it('should data-bind and become invalid', function() { + if (browser.params.browser === 'safari' || browser.params.browser === 'firefox') { + // SafariDriver can't handle contenteditable + // and Firefox driver can't clear contenteditables very well + return; + } + var contentEditable = element(by.css('[contenteditable]')); + var content = 'Change me!'; + + expect(contentEditable.getText()).toEqual(content); + + contentEditable.clear(); + contentEditable.sendKeys(protractor.Key.BACK_SPACE); + expect(contentEditable.getText()).toEqual(''); + expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/); + }); + + *
    + * + * + */ +NgModelController.$inject = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$q', '$interpolate']; +function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $q, $interpolate) { + this.$viewValue = Number.NaN; + this.$modelValue = Number.NaN; + this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity. + this.$validators = {}; + this.$asyncValidators = {}; + this.$parsers = []; + this.$formatters = []; + this.$viewChangeListeners = []; + this.$untouched = true; + this.$touched = false; + this.$pristine = true; + this.$dirty = false; + this.$valid = true; + this.$invalid = false; + this.$error = {}; // keep invalid keys here + this.$$success = {}; // keep valid keys here + this.$pending = undefined; // keep pending keys here + this.$name = $interpolate($attr.name || '', false)($scope); + this.$$parentForm = nullFormCtrl; + this.$options = defaultModelOptions; + this.$$updateEvents = ''; + // Attach the correct context to the event handler function for updateOn + this.$$updateEventHandler = this.$$updateEventHandler.bind(this); + + this.$$parsedNgModel = $parse($attr.ngModel); + this.$$parsedNgModelAssign = this.$$parsedNgModel.assign; + this.$$ngModelGet = this.$$parsedNgModel; + this.$$ngModelSet = this.$$parsedNgModelAssign; + this.$$pendingDebounce = null; + this.$$parserValid = undefined; + + this.$$currentValidationRunId = 0; + + // https://github.com/angular/angular.js/issues/15833 + // Prevent `$$scope` from being iterated over by `copy` when NgModelController is deep watched + Object.defineProperty(this, '$$scope', {value: $scope}); + this.$$attr = $attr; + this.$$element = $element; + this.$$animate = $animate; + this.$$timeout = $timeout; + this.$$parse = $parse; + this.$$q = $q; + this.$$exceptionHandler = $exceptionHandler; + + setupValidity(this); + setupModelWatcher(this); +} + +NgModelController.prototype = { + $$initGetterSetters: function() { + if (this.$options.getOption('getterSetter')) { + var invokeModelGetter = this.$$parse(this.$$attr.ngModel + '()'), + invokeModelSetter = this.$$parse(this.$$attr.ngModel + '($$$p)'); + + this.$$ngModelGet = function($scope) { + var modelValue = this.$$parsedNgModel($scope); + if (isFunction(modelValue)) { + modelValue = invokeModelGetter($scope); + } + return modelValue; + }; + this.$$ngModelSet = function($scope, newValue) { + if (isFunction(this.$$parsedNgModel($scope))) { + invokeModelSetter($scope, {$$$p: newValue}); + } else { + this.$$parsedNgModelAssign($scope, newValue); + } + }; + } else if (!this.$$parsedNgModel.assign) { + throw ngModelMinErr('nonassign', 'Expression \'{0}\' is non-assignable. Element: {1}', + this.$$attr.ngModel, startingTag(this.$$element)); + } + }, + + + /** + * @ngdoc method + * @name ngModel.NgModelController#$render + * + * @description + * Called when the view needs to be updated. It is expected that the user of the ng-model + * directive will implement this method. + * + * The `$render()` method is invoked in the following situations: + * + * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last + * committed value then `$render()` is called to update the input control. + * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and + * the `$viewValue` are different from last time. + * + * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of + * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue` + * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be + * invoked if you only change a property on the objects. + */ + $render: noop, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$isEmpty + * + * @description + * This is called when we need to determine if the value of an input is empty. + * + * For instance, the required directive does this to work out if the input has data or not. + * + * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. + * + * You can override this for input directives whose concept of being empty is different from the + * default. The `checkboxInputType` directive does this because in its case a value of `false` + * implies empty. + * + * @param {*} value The value of the input to check for emptiness. + * @returns {boolean} True if `value` is "empty". + */ + $isEmpty: function(value) { + // eslint-disable-next-line no-self-compare + return isUndefined(value) || value === '' || value === null || value !== value; + }, + + $$updateEmptyClasses: function(value) { + if (this.$isEmpty(value)) { + this.$$animate.removeClass(this.$$element, NOT_EMPTY_CLASS); + this.$$animate.addClass(this.$$element, EMPTY_CLASS); + } else { + this.$$animate.removeClass(this.$$element, EMPTY_CLASS); + this.$$animate.addClass(this.$$element, NOT_EMPTY_CLASS); + } + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setPristine + * + * @description + * Sets the control to its pristine state. + * + * This method can be called to remove the `ng-dirty` class and set the control to its pristine + * state (`ng-pristine` class). A model is considered to be pristine when the control + * has not been changed from when first compiled. + */ + $setPristine: function() { + this.$dirty = false; + this.$pristine = true; + this.$$animate.removeClass(this.$$element, DIRTY_CLASS); + this.$$animate.addClass(this.$$element, PRISTINE_CLASS); + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setDirty + * + * @description + * Sets the control to its dirty state. + * + * This method can be called to remove the `ng-pristine` class and set the control to its dirty + * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed + * from when first compiled. + */ + $setDirty: function() { + this.$dirty = true; + this.$pristine = false; + this.$$animate.removeClass(this.$$element, PRISTINE_CLASS); + this.$$animate.addClass(this.$$element, DIRTY_CLASS); + this.$$parentForm.$setDirty(); + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setUntouched + * + * @description + * Sets the control to its untouched state. + * + * This method can be called to remove the `ng-touched` class and set the control to its + * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched + * by default, however this function can be used to restore that state if the model has + * already been touched by the user. + */ + $setUntouched: function() { + this.$touched = false; + this.$untouched = true; + this.$$animate.setClass(this.$$element, UNTOUCHED_CLASS, TOUCHED_CLASS); + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setTouched + * + * @description + * Sets the control to its touched state. + * + * This method can be called to remove the `ng-untouched` class and set the control to its + * touched state (`ng-touched` class). A model is considered to be touched when the user has + * first focused the control element and then shifted focus away from the control (blur event). + */ + $setTouched: function() { + this.$touched = true; + this.$untouched = false; + this.$$animate.setClass(this.$$element, TOUCHED_CLASS, UNTOUCHED_CLASS); + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$rollbackViewValue + * + * @description + * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`, + * which may be caused by a pending debounced event or because the input is waiting for some + * future event. + * + * If you have an input that uses `ng-model-options` to set up debounced updates or updates that + * depend on special events such as `blur`, there can be a period when the `$viewValue` is out of + * sync with the ngModel's `$modelValue`. + * + * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update + * and reset the input to the last committed view value. + * + * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue` + * programmatically before these debounced/future events have resolved/occurred, because AngularJS's + * dirty checking mechanism is not able to tell whether the model has actually changed or not. + * + * The `$rollbackViewValue()` method should be called before programmatically changing the model of an + * input which may have such events pending. This is important in order to make sure that the + * input field will be updated with the new model value and any pending operations are cancelled. + * + * @example + * + * + * angular.module('cancel-update-example', []) + * + * .controller('CancelUpdateController', ['$scope', function($scope) { + * $scope.model = {value1: '', value2: ''}; + * + * $scope.setEmpty = function(e, value, rollback) { + * if (e.keyCode === 27) { + * e.preventDefault(); + * if (rollback) { + * $scope.myForm[value].$rollbackViewValue(); + * } + * $scope.model[value] = ''; + * } + * }; + * }]); + * + * + *
    + *

    Both of these inputs are only updated if they are blurred. Hitting escape should + * empty them. Follow these steps and observe the difference:

    + *
      + *
    1. Type something in the input. You will see that the model is not yet updated
    2. + *
    3. Press the Escape key. + *
        + *
      1. In the first example, nothing happens, because the model is already '', and no + * update is detected. If you blur the input, the model will be set to the current view. + *
      2. + *
      3. In the second example, the pending update is cancelled, and the input is set back + * to the last committed view value (''). Blurring the input does nothing. + *
      4. + *
      + *
    4. + *
    + * + *
    + *
    + *

    Without $rollbackViewValue():

    + * + * value1: "{{ model.value1 }}" + *
    + * + *
    + *

    With $rollbackViewValue():

    + * + * value2: "{{ model.value2 }}" + *
    + *
    + *
    + *
    + + div { + display: table-cell; + } + div:nth-child(1) { + padding-right: 30px; + } + + + *
    + */ + $rollbackViewValue: function() { + this.$$timeout.cancel(this.$$pendingDebounce); + this.$viewValue = this.$$lastCommittedViewValue; + this.$render(); + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$validate + * + * @description + * Runs each of the registered validators (first synchronous validators and then + * asynchronous validators). + * If the validity changes to invalid, the model will be set to `undefined`, + * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`. + * If the validity changes to valid, it will set the model to the last available valid + * `$modelValue`, i.e. either the last parsed value or the last value set from the scope. + */ + $validate: function() { + // ignore $validate before model is initialized + if (isNumberNaN(this.$modelValue)) { + return; + } + + var viewValue = this.$$lastCommittedViewValue; + // Note: we use the $$rawModelValue as $modelValue might have been + // set to undefined during a view -> model update that found validation + // errors. We can't parse the view here, since that could change + // the model although neither viewValue nor the model on the scope changed + var modelValue = this.$$rawModelValue; + + var prevValid = this.$valid; + var prevModelValue = this.$modelValue; + + var allowInvalid = this.$options.getOption('allowInvalid'); + + var that = this; + this.$$runValidators(modelValue, viewValue, function(allValid) { + // If there was no change in validity, don't update the model + // This prevents changing an invalid modelValue to undefined + if (!allowInvalid && prevValid !== allValid) { + // Note: Don't check this.$valid here, as we could have + // external validators (e.g. calculated on the server), + // that just call $setValidity and need the model value + // to calculate their validity. + that.$modelValue = allValid ? modelValue : undefined; + + if (that.$modelValue !== prevModelValue) { + that.$$writeModelToScope(); + } + } + }); + }, + + $$runValidators: function(modelValue, viewValue, doneCallback) { + this.$$currentValidationRunId++; + var localValidationRunId = this.$$currentValidationRunId; + var that = this; + + // check parser error + if (!processParseErrors()) { + validationDone(false); + return; + } + if (!processSyncValidators()) { + validationDone(false); + return; + } + processAsyncValidators(); + + function processParseErrors() { + var errorKey = that.$$parserName || 'parse'; + if (isUndefined(that.$$parserValid)) { + setValidity(errorKey, null); + } else { + if (!that.$$parserValid) { + forEach(that.$validators, function(v, name) { + setValidity(name, null); + }); + forEach(that.$asyncValidators, function(v, name) { + setValidity(name, null); + }); + } + // Set the parse error last, to prevent unsetting it, should a $validators key == parserName + setValidity(errorKey, that.$$parserValid); + return that.$$parserValid; + } + return true; + } + + function processSyncValidators() { + var syncValidatorsValid = true; + forEach(that.$validators, function(validator, name) { + var result = Boolean(validator(modelValue, viewValue)); + syncValidatorsValid = syncValidatorsValid && result; + setValidity(name, result); + }); + if (!syncValidatorsValid) { + forEach(that.$asyncValidators, function(v, name) { + setValidity(name, null); + }); + return false; + } + return true; + } + + function processAsyncValidators() { + var validatorPromises = []; + var allValid = true; + forEach(that.$asyncValidators, function(validator, name) { + var promise = validator(modelValue, viewValue); + if (!isPromiseLike(promise)) { + throw ngModelMinErr('nopromise', + 'Expected asynchronous validator to return a promise but got \'{0}\' instead.', promise); + } + setValidity(name, undefined); + validatorPromises.push(promise.then(function() { + setValidity(name, true); + }, function() { + allValid = false; + setValidity(name, false); + })); + }); + if (!validatorPromises.length) { + validationDone(true); + } else { + that.$$q.all(validatorPromises).then(function() { + validationDone(allValid); + }, noop); + } + } + + function setValidity(name, isValid) { + if (localValidationRunId === that.$$currentValidationRunId) { + that.$setValidity(name, isValid); + } + } + + function validationDone(allValid) { + if (localValidationRunId === that.$$currentValidationRunId) { + + doneCallback(allValid); + } + } + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$commitViewValue + * + * @description + * Commit a pending update to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. this method is rarely needed as `NgModelController` + * usually handles calling this in response to input events. + */ + $commitViewValue: function() { + var viewValue = this.$viewValue; + + this.$$timeout.cancel(this.$$pendingDebounce); + + // If the view value has not changed then we should just exit, except in the case where there is + // a native validator on the element. In this case the validation state may have changed even though + // the viewValue has stayed empty. + if (this.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !this.$$hasNativeValidators)) { + return; + } + this.$$updateEmptyClasses(viewValue); + this.$$lastCommittedViewValue = viewValue; + + // change to dirty + if (this.$pristine) { + this.$setDirty(); + } + this.$$parseAndValidate(); + }, + + $$parseAndValidate: function() { + var viewValue = this.$$lastCommittedViewValue; + var modelValue = viewValue; + var that = this; + + this.$$parserValid = isUndefined(modelValue) ? undefined : true; + + if (this.$$parserValid) { + for (var i = 0; i < this.$parsers.length; i++) { + modelValue = this.$parsers[i](modelValue); + if (isUndefined(modelValue)) { + this.$$parserValid = false; + break; + } + } + } + if (isNumberNaN(this.$modelValue)) { + // this.$modelValue has not been touched yet... + this.$modelValue = this.$$ngModelGet(this.$$scope); + } + var prevModelValue = this.$modelValue; + var allowInvalid = this.$options.getOption('allowInvalid'); + this.$$rawModelValue = modelValue; + + if (allowInvalid) { + this.$modelValue = modelValue; + writeToModelIfNeeded(); + } + + // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date. + // This can happen if e.g. $setViewValue is called from inside a parser + this.$$runValidators(modelValue, this.$$lastCommittedViewValue, function(allValid) { + if (!allowInvalid) { + // Note: Don't check this.$valid here, as we could have + // external validators (e.g. calculated on the server), + // that just call $setValidity and need the model value + // to calculate their validity. + that.$modelValue = allValid ? modelValue : undefined; + writeToModelIfNeeded(); + } + }); + + function writeToModelIfNeeded() { + if (that.$modelValue !== prevModelValue) { + that.$$writeModelToScope(); + } + } + }, + + $$writeModelToScope: function() { + this.$$ngModelSet(this.$$scope, this.$modelValue); + forEach(this.$viewChangeListeners, function(listener) { + try { + listener(); + } catch (e) { + // eslint-disable-next-line no-invalid-this + this.$$exceptionHandler(e); + } + }, this); + }, + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setViewValue + * + * @description + * Update the view value. + * + * This method should be called when a control wants to change the view value; typically, + * this is done from within a DOM event handler. For example, the {@link ng.directive:input input} + * directive calls it when the value of the input changes and {@link ng.directive:select select} + * calls it when an option is selected. + * + * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers` + * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged + * value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and + * `$asyncValidators` are called and the value is applied to `$modelValue`. + * Finally, the value is set to the **expression** specified in the `ng-model` attribute and + * all the registered change listeners, in the `$viewChangeListeners` list are called. + * + * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn` + * and the `default` trigger is not listed, all those actions will remain pending until one of the + * `updateOn` events is triggered on the DOM element. + * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions} + * directive is used with a custom debounce for this particular event. + * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce` + * is specified, once the timer runs out. + * + * When used with standard inputs, the view value will always be a string (which is in some cases + * parsed into another type, such as a `Date` object for `input[date]`.) + * However, custom controls might also pass objects to this method. In this case, we should make + * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not + * perform a deep watch of objects, it only looks for a change of identity. If you only change + * the property of the object then ngModel will not realize that the object has changed and + * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should + * not change properties of the copy once it has been passed to `$setViewValue`. + * Otherwise you may cause the model value on the scope to change incorrectly. + * + *
    + * In any case, the value passed to the method should always reflect the current value + * of the control. For example, if you are calling `$setViewValue` for an input element, + * you should pass the input DOM value. Otherwise, the control and the scope model become + * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change + * the control's DOM value in any way. If we want to change the control's DOM value + * programmatically, we should update the `ngModel` scope expression. Its new value will be + * picked up by the model controller, which will run it through the `$formatters`, `$render` it + * to update the DOM, and finally call `$validate` on it. + *
    + * + * @param {*} value value from the view. + * @param {string} trigger Event that triggered the update. + */ + $setViewValue: function(value, trigger) { + this.$viewValue = value; + if (this.$options.getOption('updateOnDefault')) { + this.$$debounceViewValueCommit(trigger); + } + }, + + $$debounceViewValueCommit: function(trigger) { + var debounceDelay = this.$options.getOption('debounce'); + + if (isNumber(debounceDelay[trigger])) { + debounceDelay = debounceDelay[trigger]; + } else if (isNumber(debounceDelay['default'])) { + debounceDelay = debounceDelay['default']; + } + + this.$$timeout.cancel(this.$$pendingDebounce); + var that = this; + if (debounceDelay > 0) { // this fails if debounceDelay is an object + this.$$pendingDebounce = this.$$timeout(function() { + that.$commitViewValue(); + }, debounceDelay); + } else if (this.$$scope.$root.$$phase) { + this.$commitViewValue(); + } else { + this.$$scope.$apply(function() { + that.$commitViewValue(); + }); + } + }, + + /** + * @ngdoc method + * + * @name ngModel.NgModelController#$overrideModelOptions + * + * @description + * + * Override the current model options settings programmatically. + * + * The previous `ModelOptions` value will not be modified. Instead, a + * new `ModelOptions` object will inherit from the previous one overriding + * or inheriting settings that are defined in the given parameter. + * + * See {@link ngModelOptions} for information about what options can be specified + * and how model option inheritance works. + * + *
    + * **Note:** this function only affects the options set on the `ngModelController`, + * and not the options on the {@link ngModelOptions} directive from which they might have been + * obtained initially. + *
    + * + *
    + * **Note:** it is not possible to override the `getterSetter` option. + *
    + * + * @param {Object} options a hash of settings to override the previous options + * + */ + $overrideModelOptions: function(options) { + this.$options = this.$options.createChild(options); + this.$$setUpdateOnEvents(); + }, + + /** + * @ngdoc method + * + * @name ngModel.NgModelController#$processModelValue + + * @description + * + * Runs the model -> view pipeline on the current + * {@link ngModel.NgModelController#$modelValue $modelValue}. + * + * The following actions are performed by this method: + * + * - the `$modelValue` is run through the {@link ngModel.NgModelController#$formatters $formatters} + * and the result is set to the {@link ngModel.NgModelController#$viewValue $viewValue} + * - the `ng-empty` or `ng-not-empty` class is set on the element + * - if the `$viewValue` has changed: + * - {@link ngModel.NgModelController#$render $render} is called on the control + * - the {@link ngModel.NgModelController#$validators $validators} are run and + * the validation status is set. + * + * This method is called by ngModel internally when the bound scope value changes. + * Application developers usually do not have to call this function themselves. + * + * This function can be used when the `$viewValue` or the rendered DOM value are not correctly + * formatted and the `$modelValue` must be run through the `$formatters` again. + * + * @example + * Consider a text input with an autocomplete list (for fruit), where the items are + * objects with a name and an id. + * A user enters `ap` and then selects `Apricot` from the list. + * Based on this, the autocomplete widget will call `$setViewValue({name: 'Apricot', id: 443})`, + * but the rendered value will still be `ap`. + * The widget can then call `ctrl.$processModelValue()` to run the model -> view + * pipeline again, which formats the object to the string `Apricot`, + * then updates the `$viewValue`, and finally renders it in the DOM. + * + * + +
    +
    + Search Fruit: + +
    +
    + Model:
    +
    {{selectedFruit | json}}
    +
    +
    +
    + + angular.module('inputExample', []) + .controller('inputController', function($scope) { + $scope.items = [ + {name: 'Apricot', id: 443}, + {name: 'Clementine', id: 972}, + {name: 'Durian', id: 169}, + {name: 'Jackfruit', id: 982}, + {name: 'Strawberry', id: 863} + ]; + }) + .component('basicAutocomplete', { + bindings: { + items: '<', + onSelect: '&' + }, + templateUrl: 'autocomplete.html', + controller: function($element, $scope) { + var that = this; + var ngModel; + + that.$postLink = function() { + ngModel = $element.find('input').controller('ngModel'); + + ngModel.$formatters.push(function(value) { + return (value && value.name) || value; + }); + + ngModel.$parsers.push(function(value) { + var match = value; + for (var i = 0; i < that.items.length; i++) { + if (that.items[i].name === value) { + match = that.items[i]; + break; + } + } + + return match; + }); + }; + + that.selectItem = function(item) { + ngModel.$setViewValue(item); + ngModel.$processModelValue(); + that.onSelect({item: item}); + }; + } + }); + + +
    + +
      +
    • + +
    • +
    +
    +
    + *
    + * + */ + $processModelValue: function() { + var viewValue = this.$$format(); + + if (this.$viewValue !== viewValue) { + this.$$updateEmptyClasses(viewValue); + this.$viewValue = this.$$lastCommittedViewValue = viewValue; + this.$render(); + // It is possible that model and view value have been updated during render + this.$$runValidators(this.$modelValue, this.$viewValue, noop); + } + }, + + /** + * This method is called internally to run the $formatters on the $modelValue + */ + $$format: function() { + var formatters = this.$formatters, + idx = formatters.length; + + var viewValue = this.$modelValue; + while (idx--) { + viewValue = formatters[idx](viewValue); + } + + return viewValue; + }, + + /** + * This method is called internally when the bound scope value changes. + */ + $$setModelValue: function(modelValue) { + this.$modelValue = this.$$rawModelValue = modelValue; + this.$$parserValid = undefined; + this.$processModelValue(); + }, + + $$setUpdateOnEvents: function() { + if (this.$$updateEvents) { + this.$$element.off(this.$$updateEvents, this.$$updateEventHandler); + } + + this.$$updateEvents = this.$options.getOption('updateOn'); + if (this.$$updateEvents) { + this.$$element.on(this.$$updateEvents, this.$$updateEventHandler); + } + }, + + $$updateEventHandler: function(ev) { + this.$$debounceViewValueCommit(ev && ev.type); + } +}; + +function setupModelWatcher(ctrl) { + // model -> value + // Note: we cannot use a normal scope.$watch as we want to detect the following: + // 1. scope value is 'a' + // 2. user enters 'b' + // 3. ng-change kicks in and reverts scope value to 'a' + // -> scope value did not change since the last digest as + // ng-change executes in apply phase + // 4. view should be changed back to 'a' + ctrl.$$scope.$watch(function ngModelWatch(scope) { + var modelValue = ctrl.$$ngModelGet(scope); + + // if scope model value and ngModel value are out of sync + // This cannot be moved to the action function, because it would not catch the + // case where the model is changed in the ngChange function or the model setter + if (modelValue !== ctrl.$modelValue && + // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator + // eslint-disable-next-line no-self-compare + (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue) + ) { + ctrl.$$setModelValue(modelValue); + } + + return modelValue; + }); +} + +/** + * @ngdoc method + * @name ngModel.NgModelController#$setValidity + * + * @description + * Change the validity state, and notify the form. + * + * This method can be called within $parsers/$formatters or a custom validation implementation. + * However, in most cases it should be sufficient to use the `ngModel.$validators` and + * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically. + * + * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned + * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` + * (for unfulfilled `$asyncValidators`), so that it is available for data-binding. + * The `validationErrorKey` should be in camelCase and will get converted into dash-case + * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` + * classes and can be bound to as `{{ someForm.someControl.$error.myError }}`. + * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined), + * or skipped (null). Pending is used for unfulfilled `$asyncValidators`. + * Skipped is used by AngularJS when validators do not run because of parse errors and + * when `$asyncValidators` do not run because any of the `$validators` failed. + */ +addSetValidityMethod({ + clazz: NgModelController, + set: function(object, property) { + object[property] = true; + }, + unset: function(object, property) { + delete object[property]; + } +}); + + +/** + * @ngdoc directive + * @name ngModel + * @restrict A + * @priority 1 + * @param {expression} ngModel assignable {@link guide/expression Expression} to bind to. + * + * @description + * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a + * property on the scope using {@link ngModel.NgModelController NgModelController}, + * which is created and exposed by this directive. + * + * `ngModel` is responsible for: + * + * - Binding the view into the model, which other directives such as `input`, `textarea` or `select` + * require. + * - Providing validation behavior (i.e. required, number, email, url). + * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors). + * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, + * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations. + * - Registering the control with its parent {@link ng.directive:form form}. + * + * Note: `ngModel` will try to bind to the property given by evaluating the expression on the + * current scope. If the property doesn't already exist on this scope, it will be created + * implicitly and added to the scope. + * + * For best practices on using `ngModel`, see: + * + * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) + * + * For basic examples, how to use `ngModel`, see: + * + * - {@link ng.directive:input input} + * - {@link input[text] text} + * - {@link input[checkbox] checkbox} + * - {@link input[radio] radio} + * - {@link input[number] number} + * - {@link input[email] email} + * - {@link input[url] url} + * - {@link input[date] date} + * - {@link input[datetime-local] datetime-local} + * - {@link input[time] time} + * - {@link input[month] month} + * - {@link input[week] week} + * - {@link ng.directive:select select} + * - {@link ng.directive:textarea textarea} + * + * ## Complex Models (objects or collections) + * + * By default, `ngModel` watches the model by reference, not value. This is important to know when + * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the + * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered. + * + * The model must be assigned an entirely new object or collection before a re-rendering will occur. + * + * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression + * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or + * if the select is given the `multiple` attribute. + * + * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the + * first level of the object (or only changing the properties of an item in the collection if it's an array) will still + * not trigger a re-rendering of the model. + * + * ## CSS classes + * The following CSS classes are added and removed on the associated input/select/textarea element + * depending on the validity of the model. + * + * - `ng-valid`: the model is valid + * - `ng-invalid`: the model is invalid + * - `ng-valid-[key]`: for each valid key added by `$setValidity` + * - `ng-invalid-[key]`: for each invalid key added by `$setValidity` + * - `ng-pristine`: the control hasn't been interacted with yet + * - `ng-dirty`: the control has been interacted with + * - `ng-touched`: the control has been blurred + * - `ng-untouched`: the control hasn't been blurred + * - `ng-pending`: any `$asyncValidators` are unfulfilled + * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined + * by the {@link ngModel.NgModelController#$isEmpty} method + * - `ng-not-empty`: the view contains a non-empty value + * + * Keep in mind that ngAnimate can detect each of these classes when added and removed. + * + * @animations + * Animations within models are triggered when any of the associated CSS classes are added and removed + * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`, + * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself. + * The animations that are triggered within ngModel are similar to how they work in ngClass and + * animations can be hooked into using CSS transitions, keyframes as well as JS animations. + * + * The following example shows a simple way to utilize CSS transitions to style an input element + * that has been rendered as invalid after it has been validated: + * + *
    + * //be sure to include ngAnimate as a module to hook into more
    + * //advanced animations
    + * .my-input {
    + *   transition:0.5s linear all;
    + *   background: white;
    + * }
    + * .my-input.ng-invalid {
    + *   background: red;
    + *   color:white;
    + * }
    + * 
    + * + * @example + * ### Basic Usage + * + + + +

    + Update input to see transitions when valid/invalid. + Integer is a valid value. +

    +
    + +
    +
    + *
    + * + * @example + * ### Binding to a getter/setter + * + * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a + * function that returns a representation of the model when called with zero arguments, and sets + * the internal state of a model when called with an argument. It's sometimes useful to use this + * for models that have an internal representation that's different from what the model exposes + * to the view. + * + *
    + * **Best Practice:** It's best to keep getters fast because AngularJS is likely to call them more + * frequently than other parts of your code. + *
    + * + * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that + * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to + * a `
    `, which will enable this behavior for all ``s within it. See + * {@link ng.directive:ngModelOptions `ngModelOptions`} for more. + * + * The following example shows how to use `ngModel` with a getter/setter: + * + * @example + * + +
    + + + +
    user.name = 
    +
    +
    + + angular.module('getterSetterExample', []) + .controller('ExampleController', ['$scope', function($scope) { + var _name = 'Brian'; + $scope.user = { + name: function(newName) { + // Note that newName can be undefined for two reasons: + // 1. Because it is called as a getter and thus called with no arguments + // 2. Because the property should actually be set to undefined. This happens e.g. if the + // input is invalid + return arguments.length ? (_name = newName) : _name; + } + }; + }]); + + *
    + */ +var ngModelDirective = ['$rootScope', function($rootScope) { + return { + restrict: 'A', + require: ['ngModel', '^?form', '^?ngModelOptions'], + controller: NgModelController, + // Prelink needs to run before any input directive + // so that we can set the NgModelOptions in NgModelController + // before anyone else uses it. + priority: 1, + compile: function ngModelCompile(element) { + // Setup initial state of the control + element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS); + + return { + pre: function ngModelPreLink(scope, element, attr, ctrls) { + var modelCtrl = ctrls[0], + formCtrl = ctrls[1] || modelCtrl.$$parentForm, + optionsCtrl = ctrls[2]; + + if (optionsCtrl) { + modelCtrl.$options = optionsCtrl.$options; + } + + modelCtrl.$$initGetterSetters(); + + // notify others, especially parent forms + formCtrl.$addControl(modelCtrl); + + attr.$observe('name', function(newValue) { + if (modelCtrl.$name !== newValue) { + modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue); + } + }); + + scope.$on('$destroy', function() { + modelCtrl.$$parentForm.$removeControl(modelCtrl); + }); + }, + post: function ngModelPostLink(scope, element, attr, ctrls) { + var modelCtrl = ctrls[0]; + modelCtrl.$$setUpdateOnEvents(); + + function setTouched() { + modelCtrl.$setTouched(); + } + + element.on('blur', function() { + if (modelCtrl.$touched) return; + + if ($rootScope.$$phase) { + scope.$evalAsync(setTouched); + } else { + scope.$apply(setTouched); + } + }); + } + }; + } + }; +}]; + +/* exported defaultModelOptions */ +var defaultModelOptions; +var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; + +/** + * @ngdoc type + * @name ModelOptions + * @description + * A container for the options set by the {@link ngModelOptions} directive + */ +function ModelOptions(options) { + this.$$options = options; +} + +ModelOptions.prototype = { + + /** + * @ngdoc method + * @name ModelOptions#getOption + * @param {string} name the name of the option to retrieve + * @returns {*} the value of the option + * @description + * Returns the value of the given option + */ + getOption: function(name) { + return this.$$options[name]; + }, + + /** + * @ngdoc method + * @name ModelOptions#createChild + * @param {Object} options a hash of options for the new child that will override the parent's options + * @return {ModelOptions} a new `ModelOptions` object initialized with the given options. + */ + createChild: function(options) { + var inheritAll = false; + + // make a shallow copy + options = extend({}, options); + + // Inherit options from the parent if specified by the value `"$inherit"` + forEach(options, /* @this */ function(option, key) { + if (option === '$inherit') { + if (key === '*') { + inheritAll = true; + } else { + options[key] = this.$$options[key]; + // `updateOn` is special so we must also inherit the `updateOnDefault` option + if (key === 'updateOn') { + options.updateOnDefault = this.$$options.updateOnDefault; + } + } + } else { + if (key === 'updateOn') { + // If the `updateOn` property contains the `default` event then we have to remove + // it from the event list and set the `updateOnDefault` flag. + options.updateOnDefault = false; + options[key] = trim(option.replace(DEFAULT_REGEXP, function() { + options.updateOnDefault = true; + return ' '; + })); + } + } + }, this); + + if (inheritAll) { + // We have a property of the form: `"*": "$inherit"` + delete options['*']; + defaults(options, this.$$options); + } + + // Finally add in any missing defaults + defaults(options, defaultModelOptions.$$options); + + return new ModelOptions(options); + } +}; + + +defaultModelOptions = new ModelOptions({ + updateOn: '', + updateOnDefault: true, + debounce: 0, + getterSetter: false, + allowInvalid: false, + timezone: null +}); + + +/** + * @ngdoc directive + * @name ngModelOptions + * @restrict A + * @priority 10 + * + * @description + * This directive allows you to modify the behaviour of {@link ngModel} directives within your + * application. You can specify an `ngModelOptions` directive on any element. All {@link ngModel} + * directives will use the options of their nearest `ngModelOptions` ancestor. + * + * The `ngModelOptions` settings are found by evaluating the value of the attribute directive as + * an AngularJS expression. This expression should evaluate to an object, whose properties contain + * the settings. For example: `
    + *
    + * + *
    + *
    + * ``` + * + * the `input` element will have the following settings + * + * ```js + * { allowInvalid: true, updateOn: 'default', debounce: 0 } + * ``` + * + * Notice that the `debounce` setting was not inherited and used the default value instead. + * + * You can specify that all undefined settings are automatically inherited from an ancestor by + * including a property with key of `"*"` and value of `"$inherit"`. + * + * For example given the following fragment of HTML + * + * + * ```html + *
    + *
    + * + *
    + *
    + * ``` + * + * the `input` element will have the following settings + * + * ```js + * { allowInvalid: true, updateOn: 'default', debounce: 200 } + * ``` + * + * Notice that the `debounce` setting now inherits the value from the outer `
    ` element. + * + * If you are creating a reusable component then you should be careful when using `"*": "$inherit"` + * since you may inadvertently inherit a setting in the future that changes the behavior of your component. + * + * + * ## Triggering and debouncing model updates + * + * The `updateOn` and `debounce` properties allow you to specify a custom list of events that will + * trigger a model update and/or a debouncing delay so that the actual update only takes place when + * a timer expires; this timer will be reset after another change takes place. + * + * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might + * be different from the value in the actual model. This means that if you update the model you + * should also invoke {@link ngModel.NgModelController#$rollbackViewValue} on the relevant input field in + * order to make sure it is synchronized with the model and that any debounced action is canceled. + * + * The easiest way to reference the control's {@link ngModel.NgModelController#$rollbackViewValue} + * method is by making sure the input is placed inside a form that has a `name` attribute. This is + * important because `form` controllers are published to the related scope under the name in their + * `name` attribute. + * + * Any pending changes will take place immediately when an enclosing form is submitted via the + * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` + * to have access to the updated model. + * + * ### Overriding immediate updates + * + * The following example shows how to override immediate updates. Changes on the inputs within the + * form will update the model only when the control loses focus (blur event). If `escape` key is + * pressed while the input field is focused, the value is reset to the value in the current model. + * + * + * + *
    + *
    + *
    + *
    + *
    + *
    user.name = 
    + *
    + *
    + * + * angular.module('optionsExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.user = { name: 'say', data: '' }; + * + * $scope.cancel = function(e) { + * if (e.keyCode === 27) { + * $scope.userForm.userName.$rollbackViewValue(); + * } + * }; + * }]); + * + * + * var model = element(by.binding('user.name')); + * var input = element(by.model('user.name')); + * var other = element(by.model('user.data')); + * + * it('should allow custom events', function() { + * input.sendKeys(' hello'); + * input.click(); + * expect(model.getText()).toEqual('say'); + * other.click(); + * expect(model.getText()).toEqual('say hello'); + * }); + * + * it('should $rollbackViewValue when model changes', function() { + * input.sendKeys(' hello'); + * expect(input.getAttribute('value')).toEqual('say hello'); + * input.sendKeys(protractor.Key.ESCAPE); + * expect(input.getAttribute('value')).toEqual('say'); + * other.click(); + * expect(model.getText()).toEqual('say'); + * }); + * + *
    + * + * ### Debouncing updates + * + * The next example shows how to debounce model changes. Model will be updated only 1 sec after last change. + * If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty. + * + * + * + *
    + *
    + * Name: + * + *
    + *
    + *
    user.name = 
    + *
    + *
    + * + * angular.module('optionsExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.user = { name: 'say' }; + * }]); + * + *
    + * + * + * ## Model updates and validation + * + * The default behaviour in `ngModel` is that the model value is set to `undefined` when the + * validation determines that the value is invalid. By setting the `allowInvalid` property to true, + * the model will still be updated even if the value is invalid. + * + * + * ## Connecting to the scope + * + * By setting the `getterSetter` property to true you are telling ngModel that the `ngModel` expression + * on the scope refers to a "getter/setter" function rather than the value itself. + * + * The following example shows how to bind to getter/setters: + * + * + * + *
    + *
    + * + *
    + *
    user.name = 
    + *
    + *
    + * + * angular.module('getterSetterExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * var _name = 'Brian'; + * $scope.user = { + * name: function(newName) { + * return angular.isDefined(newName) ? (_name = newName) : _name; + * } + * }; + * }]); + * + *
    + * + * + * ## Specifying timezones + * + * You can specify the timezone that date/time input directives expect by providing its name in the + * `timezone` property. + * + * + * ## Programmatically changing options + * + * The `ngModelOptions` expression is only evaluated once when the directive is linked; it is not + * watched for changes. However, it is possible to override the options on a single + * {@link ngModel.NgModelController} instance with + * {@link ngModel.NgModelController#$overrideModelOptions `NgModelController#$overrideModelOptions()`}. + * + * + * @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and + * and its descendents. Valid keys are: + * - `updateOn`: string specifying which event should the input be bound to. You can set several + * events using an space delimited list. There is a special event called `default` that + * matches the default events belonging to the control. These are the events that are bound to + * the control, and when fired, update the `$viewValue` via `$setViewValue`. + * + * `ngModelOptions` considers every event that is not listed in `updateOn` a "default" event, + * since different control types use different default events. + * + * See also the section {@link ngModelOptions#triggering-and-debouncing-model-updates + * Triggering and debouncing model updates}. + * + * - `debounce`: integer value which contains the debounce model update value in milliseconds. A + * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a + * custom value for each event. For example: + * ``` + * ng-model-options="{ + * updateOn: 'default blur click', + * debounce: { 'default': 500, 'blur': 0 } + * }" + * ``` + * + * "default" also applies to all events that are listed in `updateOn` but are not + * listed in `debounce`, i.e. "click" would also be debounced by 500 milliseconds. + * + * - `allowInvalid`: boolean value which indicates that the model can be set with values that did + * not validate correctly instead of the default behavior of setting the model to undefined. + * - `getterSetter`: boolean value which determines whether or not to treat functions bound to + * `ngModel` as getters/setters. + * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for + * ``, ``, ... . It understands UTC/GMT and the + * continental US time zone abbreviations, but for general use, use a time zone offset, for + * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian) + * If not specified, the timezone of the browser will be used. + * + */ +var ngModelOptionsDirective = function() { + NgModelOptionsController.$inject = ['$attrs', '$scope']; + function NgModelOptionsController($attrs, $scope) { + this.$$attrs = $attrs; + this.$$scope = $scope; + } + NgModelOptionsController.prototype = { + $onInit: function() { + var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions; + var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions); + + this.$options = parentOptions.createChild(modelOptionsDefinition); + } + }; + + return { + restrict: 'A', + // ngModelOptions needs to run before ngModel and input directives + priority: 10, + require: {parentCtrl: '?^^ngModelOptions'}, + bindToController: true, + controller: NgModelOptionsController + }; +}; + + +// shallow copy over values from `src` that are not already specified on `dst` +function defaults(dst, src) { + forEach(src, function(value, key) { + if (!isDefined(dst[key])) { + dst[key] = value; + } + }); +} + +/** + * @ngdoc directive + * @name ngNonBindable + * @restrict AC + * @priority 1000 + * @element ANY + * + * @description + * The `ngNonBindable` directive tells AngularJS not to compile or bind the contents of the current + * DOM element, including directives on the element itself that have a lower priority than + * `ngNonBindable`. This is useful if the element contains what appears to be AngularJS directives + * and bindings but which should be ignored by AngularJS. This could be the case if you have a site + * that displays snippets of code, for instance. + * + * @example + * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, + * but the one wrapped in `ngNonBindable` is left alone. + * + + +
    Normal: {{1 + 2}}
    +
    Ignored: {{1 + 2}}
    +
    + + it('should check ng-non-bindable', function() { + expect(element(by.binding('1 + 2')).getText()).toContain('3'); + expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); + }); + +
    + */ +var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); + +/* exported ngOptionsDirective */ + +/* global jqLiteRemove */ + +var ngOptionsMinErr = minErr('ngOptions'); + +/** + * @ngdoc directive + * @name ngOptions + * @restrict A + * + * @description + * + * The `ngOptions` attribute can be used to dynamically generate a list of `` + * DOM element. + * * `disable`: The result of this expression will be used to disable the rendered ` + * + *
    + * + *
    + *
    + *
    + * singleSelect = {{data.singleSelect}} + * + *
    + *
    + *
    + * multipleSelect = {{data.multipleSelect}}
    + * + *
    + * + * + * angular.module('staticSelect', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.data = { + * singleSelect: null, + * multipleSelect: [], + * option1: 'option-1' + * }; + * + * $scope.forceUnknownOption = function() { + * $scope.data.singleSelect = 'nonsense'; + * }; + * }]); + * + * + * + * @example + * ### Using `ngRepeat` to generate `select` options + * + * + *
    + *
    + * + * + *
    + *
    + * model = {{data.model}}
    + *
    + *
    + * + * angular.module('ngrepeatSelect', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.data = { + * model: null, + * availableOptions: [ + * {id: '1', name: 'Option A'}, + * {id: '2', name: 'Option B'}, + * {id: '3', name: 'Option C'} + * ] + * }; + * }]); + * + *
    + * + * @example + * ### Using `ngValue` to bind the model to an array of objects + * + * + *
    + *
    + * + * + *
    + *
    + *
    model = {{data.model | json}}

    + *
    + *
    + * + * angular.module('ngvalueSelect', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.data = { + * model: null, + * availableOptions: [ + {value: 'myString', name: 'string'}, + {value: 1, name: 'integer'}, + {value: true, name: 'boolean'}, + {value: null, name: 'null'}, + {value: {prop: 'value'}, name: 'object'}, + {value: ['a'], name: 'array'} + * ] + * }; + * }]); + * + *
    + * + * @example + * ### Using `select` with `ngOptions` and setting a default value + * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples. + * + * + * + *
    + *
    + * + * + *
    + *
    + * option = {{data.selectedOption}}
    + *
    + *
    + * + * angular.module('defaultValueSelect', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.data = { + * availableOptions: [ + * {id: '1', name: 'Option A'}, + * {id: '2', name: 'Option B'}, + * {id: '3', name: 'Option C'} + * ], + * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui + * }; + * }]); + * + *
    + * + * @example + * ### Binding `select` to a non-string value via `ngModel` parsing / formatting + * + * + * + * + * {{ model }} + * + * + * angular.module('nonStringSelect', []) + * .run(function($rootScope) { + * $rootScope.model = { id: 2 }; + * }) + * .directive('convertToNumber', function() { + * return { + * require: 'ngModel', + * link: function(scope, element, attrs, ngModel) { + * ngModel.$parsers.push(function(val) { + * return parseInt(val, 10); + * }); + * ngModel.$formatters.push(function(val) { + * return '' + val; + * }); + * } + * }; + * }); + * + * + * it('should initialize to model', function() { + * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two'); + * }); + * + * + * + */ +var selectDirective = function() { + + return { + restrict: 'E', + require: ['select', '?ngModel'], + controller: SelectController, + priority: 1, + link: { + pre: selectPreLink, + post: selectPostLink + } + }; + + function selectPreLink(scope, element, attr, ctrls) { + + var selectCtrl = ctrls[0]; + var ngModelCtrl = ctrls[1]; + + // if ngModel is not defined, we don't need to do anything but set the registerOption + // function to noop, so options don't get added internally + if (!ngModelCtrl) { + selectCtrl.registerOption = noop; + return; + } + + + selectCtrl.ngModelCtrl = ngModelCtrl; + + // When the selected item(s) changes we delegate getting the value of the select control + // to the `readValue` method, which can be changed if the select can have multiple + // selected values or if the options are being generated by `ngOptions` + element.on('change', function() { + selectCtrl.removeUnknownOption(); + scope.$apply(function() { + ngModelCtrl.$setViewValue(selectCtrl.readValue()); + }); + }); + + // If the select allows multiple values then we need to modify how we read and write + // values from and to the control; also what it means for the value to be empty and + // we have to add an extra watch since ngModel doesn't work well with arrays - it + // doesn't trigger rendering if only an item in the array changes. + if (attr.multiple) { + selectCtrl.multiple = true; + + // Read value now needs to check each option to see if it is selected + selectCtrl.readValue = function readMultipleValue() { + var array = []; + forEach(element.find('option'), function(option) { + if (option.selected && !option.disabled) { + var val = option.value; + array.push(val in selectCtrl.selectValueMap ? selectCtrl.selectValueMap[val] : val); + } + }); + return array; + }; + + // Write value now needs to set the selected property of each matching option + selectCtrl.writeValue = function writeMultipleValue(value) { + forEach(element.find('option'), function(option) { + var shouldBeSelected = !!value && (includes(value, option.value) || + includes(value, selectCtrl.selectValueMap[option.value])); + var currentlySelected = option.selected; + + // Support: IE 9-11 only, Edge 12-15+ + // In IE and Edge adding options to the selection via shift+click/UP/DOWN + // will de-select already selected options if "selected" on those options was set + // more than once (i.e. when the options were already selected) + // So we only modify the selected property if necessary. + // Note: this behavior cannot be replicated via unit tests because it only shows in the + // actual user interface. + if (shouldBeSelected !== currentlySelected) { + setOptionSelectedStatus(jqLite(option), shouldBeSelected); + } + + }); + }; + + // we have to do it on each watch since ngModel watches reference, but + // we need to work of an array, so we need to see if anything was inserted/removed + var lastView, lastViewRef = NaN; + scope.$watch(function selectMultipleWatch() { + if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) { + lastView = shallowCopy(ngModelCtrl.$viewValue); + ngModelCtrl.$render(); + } + lastViewRef = ngModelCtrl.$viewValue; + }); + + // If we are a multiple select then value is now a collection + // so the meaning of $isEmpty changes + ngModelCtrl.$isEmpty = function(value) { + return !value || value.length === 0; + }; + + } + } + + function selectPostLink(scope, element, attrs, ctrls) { + // if ngModel is not defined, we don't need to do anything + var ngModelCtrl = ctrls[1]; + if (!ngModelCtrl) return; + + var selectCtrl = ctrls[0]; + + // We delegate rendering to the `writeValue` method, which can be changed + // if the select can have multiple selected values or if the options are being + // generated by `ngOptions`. + // This must be done in the postLink fn to prevent $render to be called before + // all nodes have been linked correctly. + ngModelCtrl.$render = function() { + selectCtrl.writeValue(ngModelCtrl.$viewValue); + }; + } +}; + + +// The option directive is purely designed to communicate the existence (or lack of) +// of dynamically created (and destroyed) option elements to their containing select +// directive via its controller. +var optionDirective = ['$interpolate', function($interpolate) { + return { + restrict: 'E', + priority: 100, + compile: function(element, attr) { + var interpolateValueFn, interpolateTextFn; + + if (isDefined(attr.ngValue)) { + // Will be handled by registerOption + } else if (isDefined(attr.value)) { + // If the value attribute is defined, check if it contains an interpolation + interpolateValueFn = $interpolate(attr.value, true); + } else { + // If the value attribute is not defined then we fall back to the + // text content of the option element, which may be interpolated + interpolateTextFn = $interpolate(element.text(), true); + if (!interpolateTextFn) { + attr.$set('value', element.text()); + } + } + + return function(scope, element, attr) { + // This is an optimization over using ^^ since we don't want to have to search + // all the way to the root of the DOM for every single option element + var selectCtrlName = '$selectController', + parent = element.parent(), + selectCtrl = parent.data(selectCtrlName) || + parent.parent().data(selectCtrlName); // in case we are in optgroup + + if (selectCtrl) { + selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn); + } + }; + } + }; +}]; + +/** + * @ngdoc directive + * @name ngRequired + * @restrict A + * + * @param {expression} ngRequired AngularJS expression. If it evaluates to `true`, it sets the + * `required` attribute to the element and adds the `required` + * {@link ngModel.NgModelController#$validators `validator`}. + * + * @description + * + * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. + * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be + * applied to custom controls. + * + * The directive sets the `required` attribute on the element if the AngularJS expression inside + * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we + * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide} + * for more info. + * + * The validator will set the `required` error key to true if the `required` attribute is set and + * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the + * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the + * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing + * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based. + * + * @example + * + * + * + *
    + *
    + * + * + *
    + * + *
    + *
    + * required error set? = {{form.input.$error.required}}
    + * model = {{model}} + *
    + *
    + *
    + * + var required = element(by.binding('form.input.$error.required')); + var model = element(by.binding('model')); + var input = element(by.id('input')); + + it('should set the required error', function() { + expect(required.getText()).toContain('true'); + + input.sendKeys('123'); + expect(required.getText()).not.toContain('true'); + expect(model.getText()).toContain('123'); + }); + * + *
    + */ +var requiredDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + attr.required = true; // force truthy in case we are on non input element + + ctrl.$validators.required = function(modelValue, viewValue) { + return !attr.required || !ctrl.$isEmpty(viewValue); + }; + + attr.$observe('required', function() { + ctrl.$validate(); + }); + } + }; +}; + +/** + * @ngdoc directive + * @name ngPattern + * @restrict A + * + * @param {expression|RegExp} ngPattern AngularJS expression that must evaluate to a `RegExp` or a `String` + * parsable into a `RegExp`, or a `RegExp` literal. See above for + * more details. + * + * @description + * + * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. + * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls. + * + * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} + * does not match a RegExp which is obtained from the `ngPattern` attribute value: + * - the value is an AngularJS expression: + * - If the expression evaluates to a RegExp object, then this is used directly. + * - If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it + * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. + * - If the value is a RegExp literal, e.g. `ngPattern="/^\d+$/"`, it is used directly. + * + *
    + * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to + * start at the index of the last search's match, thus not taking the whole input value into + * account. + *
    + * + *
    + * **Note:** This directive is also added when the plain `pattern` attribute is used, with two + * differences: + *
      + *
    1. + * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is + * not available. + *
    2. + *
    3. + * The `ngPattern` attribute must be an expression, while the `pattern` value must be + * interpolated. + *
    4. + *
    + *
    + * + * @example + * + * + * + *
    + *
    + * + * + *
    + * + *
    + *
    + * input valid? = {{form.input.$valid}}
    + * model = {{model}} + *
    + *
    + *
    + * + var model = element(by.binding('model')); + var input = element(by.id('input')); + + it('should validate the input with the default pattern', function() { + input.sendKeys('aaa'); + expect(model.getText()).not.toContain('aaa'); + + input.clear().then(function() { + input.sendKeys('123'); + expect(model.getText()).toContain('123'); + }); + }); + * + *
    + */ +var patternDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + + var regexp, patternExp = attr.ngPattern || attr.pattern; + attr.$observe('pattern', function(regex) { + if (isString(regex) && regex.length > 0) { + regex = new RegExp('^' + regex + '$'); + } + + if (regex && !regex.test) { + throw minErr('ngPattern')('noregexp', + 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp, + regex, startingTag(elm)); + } + + regexp = regex || undefined; + ctrl.$validate(); + }); + + ctrl.$validators.pattern = function(modelValue, viewValue) { + // HTML5 pattern constraint validates the input value, so we validate the viewValue + return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue); + }; + } + }; +}; + +/** + * @ngdoc directive + * @name ngMaxlength + * @restrict A + * + * @param {expression} ngMaxlength AngularJS expression that must evaluate to a `Number` or `String` + * parsable into a `Number`. Used as value for the `maxlength` + * {@link ngModel.NgModelController#$validators validator}. + * + * @description + * + * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. + * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls. + * + * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} + * is longer than the integer obtained by evaluating the AngularJS expression given in the + * `ngMaxlength` attribute value. + * + *
    + * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two + * differences: + *
      + *
    1. + * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint + * validation is not available. + *
    2. + *
    3. + * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be + * interpolated. + *
    4. + *
    + *
    + * + * @example + * + * + * + *
    + *
    + * + * + *
    + * + *
    + *
    + * input valid? = {{form.input.$valid}}
    + * model = {{model}} + *
    + *
    + *
    + * + var model = element(by.binding('model')); + var input = element(by.id('input')); + + it('should validate the input with the default maxlength', function() { + input.sendKeys('abcdef'); + expect(model.getText()).not.toContain('abcdef'); + + input.clear().then(function() { + input.sendKeys('abcde'); + expect(model.getText()).toContain('abcde'); + }); + }); + * + *
    + */ +var maxlengthDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + + var maxlength = -1; + attr.$observe('maxlength', function(value) { + var intVal = toInt(value); + maxlength = isNumberNaN(intVal) ? -1 : intVal; + ctrl.$validate(); + }); + ctrl.$validators.maxlength = function(modelValue, viewValue) { + return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength); + }; + } + }; +}; + +/** + * @ngdoc directive + * @name ngMinlength + * @restrict A + * + * @param {expression} ngMinlength AngularJS expression that must evaluate to a `Number` or `String` + * parsable into a `Number`. Used as value for the `minlength` + * {@link ngModel.NgModelController#$validators validator}. + * + * @description + * + * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}. + * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls. + * + * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} + * is shorter than the integer obtained by evaluating the AngularJS expression given in the + * `ngMinlength` attribute value. + * + *
    + * **Note:** This directive is also added when the plain `minlength` attribute is used, with two + * differences: + *
      + *
    1. + * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint + * validation is not available. + *
    2. + *
    3. + * The `ngMinlength` value must be an expression, while the `minlength` value must be + * interpolated. + *
    4. + *
    + *
    + * + * @example + * + * + * + *
    + *
    + * + * + *
    + * + *
    + *
    + * input valid? = {{form.input.$valid}}
    + * model = {{model}} + *
    + *
    + *
    + * + var model = element(by.binding('model')); + var input = element(by.id('input')); + + it('should validate the input with the default minlength', function() { + input.sendKeys('ab'); + expect(model.getText()).not.toContain('ab'); + + input.sendKeys('abc'); + expect(model.getText()).toContain('abc'); + }); + * + *
    + */ +var minlengthDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + + var minlength = 0; + attr.$observe('minlength', function(value) { + minlength = toInt(value) || 0; + ctrl.$validate(); + }); + ctrl.$validators.minlength = function(modelValue, viewValue) { + return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength; + }; + } + }; +}; + +if (window.angular.bootstrap) { + // AngularJS is already loaded, so we can return here... + if (window.console) { + console.log('WARNING: Tried to load AngularJS more than once.'); + } + return; +} + +// try to bind to jquery now so that one can write jqLite(fn) +// but we will rebind on bootstrap again. +bindJQuery(); + +publishExternalAPI(angular); + +angular.module("ngLocale", [], ["$provide", function($provide) { +var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"}; +function getDecimals(n) { + n = n + ''; + var i = n.indexOf('.'); + return (i == -1) ? 0 : n.length - i - 1; +} + +function getVF(n, opt_precision) { + var v = opt_precision; + + if (undefined === v) { + v = Math.min(getDecimals(n), 3); + } + + var base = Math.pow(10, v); + var f = ((n * base) | 0) % base; + return {v: v, f: f}; +} + +$provide.value("$locale", { + "DATETIME_FORMATS": { + "AMPMS": [ + "AM", + "PM" + ], + "DAY": [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" + ], + "ERANAMES": [ + "Before Christ", + "Anno Domini" + ], + "ERAS": [ + "BC", + "AD" + ], + "FIRSTDAYOFWEEK": 6, + "MONTH": [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + ], + "SHORTDAY": [ + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + ], + "SHORTMONTH": [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + ], + "STANDALONEMONTH": [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + ], + "WEEKENDRANGE": [ + 5, + 6 + ], + "fullDate": "EEEE, MMMM d, y", + "longDate": "MMMM d, y", + "medium": "MMM d, y h:mm:ss a", + "mediumDate": "MMM d, y", + "mediumTime": "h:mm:ss a", + "short": "M/d/yy h:mm a", + "shortDate": "M/d/yy", + "shortTime": "h:mm a" + }, + "NUMBER_FORMATS": { + "CURRENCY_SYM": "$", + "DECIMAL_SEP": ".", + "GROUP_SEP": ",", + "PATTERNS": [ + { + "gSize": 3, + "lgSize": 3, + "maxFrac": 3, + "minFrac": 0, + "minInt": 1, + "negPre": "-", + "negSuf": "", + "posPre": "", + "posSuf": "" + }, + { + "gSize": 3, + "lgSize": 3, + "maxFrac": 2, + "minFrac": 2, + "minInt": 1, + "negPre": "-\u00a4", + "negSuf": "", + "posPre": "\u00a4", + "posSuf": "" + } + ] + }, + "id": "en-us", + "localeID": "en_US", + "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;} +}); +}]); + + jqLite(function() { + angularInit(window.document, bootstrap); + }); + +})(window); + +!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(''); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 0d43d7ba..4642dcd7 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -27,6 +27,21 @@ var gulp = require("gulp"), .pipe(gulp.dest('./dist/css')); }); + gulp.task('angular', function () { + return gulp.src('./node_modules/angular/angular.js') + .pipe(gulp.dest('./dist/js/lib')); + }); + + gulp.task('angular-route', function () { + return gulp.src('./node_modules/angular-route/angular-route.js') + .pipe(gulp.dest('./dist/js/lib')); + }); + + gulp.task('normalize', function () { + return gulp.src('./node_modules/normalize.css/normalize.css') + .pipe(gulp.dest('./dist/css')); + }); + gulp.task('fetchApp', function () { return gulp.src('./js/**/*.js') .pipe(concat('main.js')) @@ -55,7 +70,7 @@ var gulp = require("gulp"), gulp.task("build", ['buildJs', "buildCss"]); - gulp.task('default', ['less', 'fetchApp', 'webserver'],function () { + gulp.task('default', ['angular', 'angular-route', 'normalize', 'less', 'fetchApp', 'webserver'],function () { gulp.watch(paths.less, ['less']); gulp.watch('./js/**/*.js', ["fetchApp"]); }); diff --git a/index.html b/index.html index cbf67db2..4e5f38d7 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,7 @@ - + @@ -43,8 +43,8 @@

    Profite - CNPJ 05.559.134/0001-60 End: Voluntários da Pátria, 301/702 Botafogo - RJ - 22270-000

    - - + + \ No newline at end of file From af3c6b4a1ff47637c23cc395e6ff87af115bb486 Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Fri, 13 Apr 2018 14:47:43 -0300 Subject: [PATCH 21/22] Create privacy.html --- privacy.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 privacy.html diff --git a/privacy.html b/privacy.html new file mode 100644 index 00000000..816a75fd --- /dev/null +++ b/privacy.html @@ -0,0 +1 @@ +

    teste

    From e68006475f54eea4c98157cd8ab875ab24a3db3d Mon Sep 17 00:00:00 2001 From: Patrick Narciso Date: Fri, 13 Apr 2018 14:49:51 -0300 Subject: [PATCH 22/22] Update privacy.html --- privacy.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/privacy.html b/privacy.html index 816a75fd..c1b208de 100644 --- a/privacy.html +++ b/privacy.html @@ -1 +1 @@ -

    teste

    +

    Política de privacidade para estante-virtual

    Todas as suas informações pessoais recolhidas, serão usadas para o ajudar a tornar a sua visita no nosso site o mais produtiva e agradável possível.

    A garantia da confidencialidade dos dados pessoais dos utilizadores do nosso site é importante para o estante-virtual.

    Todas as informações pessoais relativas a membros, assinantes, clientes ou visitantes que usem o estante-virtual serão tratadas em concordância com a Lei da Proteção de Dados Pessoais de 26 de outubro de 1998 (Lei n.º 67/98).

    A informação pessoal recolhida pode incluir o seu nome, e-mail, número de telefone e/ou telemóvel, morada, data de nascimento e/ou outros.

    O uso do estante-virtual pressupõe a aceitação deste Acordo de privacidade. A equipa do estante-virtual reserva-se ao direito de alterar este acordo sem aviso prévio. Deste modo, recomendamos que consulte a nossa política de privacidade com regularidade de forma a estar sempre atualizado.

    Os anúncios

    Tal como outros websites, coletamos e utilizamos informação contida nos anúncios. A informação contida nos anúncios, inclui o seu endereço IP (Internet Protocol), o seu ISP (Internet Service Provider, como o Sapo, Clix, ou outro), o browser que utilizou ao visitar o nosso website (como o Internet Explorer ou o Firefox), o tempo da sua visita e que páginas visitou dentro do nosso website.

    Ligações a Sites de terceiros

    O estante-virtual possui ligações para outros sites, os quais, a nosso ver, podem conter informações / ferramentas úteis para os nossos visitantes. A nossa política de privacidade não é aplicada a sites de terceiros, pelo que, caso visite outro site a partir do nosso deverá ler a politica de privacidade do mesmo.

    Não nos responsabilizamos pela política de privacidade ou conteúdo presente nesses mesmos sites.

    + +