angular-rails-engine 1.2.0.2 → 1.2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,14 +1,14 @@
1
1
  /*
2
- AngularJS v1.2.0
3
- (c) 2010-2012 Google, Inc. http://angularjs.org
2
+ AngularJS v1.2.3
3
+ (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  License: MIT
5
5
  */
6
- (function(m,g,n){'use strict';function h(a){var d={};a=a.split(",");var c;for(c=0;c<a.length;c++)d[a[c]]=!0;return d}function D(a,d){function c(a,b,c,f){b=g.lowercase(b);if(r[b])for(;e.last()&&s[e.last()];)k("",e.last());t[b]&&e.last()==b&&k("",b);(f=u[b]||!!f)||e.push(b);var l={};c.replace(E,function(a,b,d,c,e){l[b]=p(d||c||e||"")});d.start&&d.start(b,l,f)}function k(a,b){var c=0,k;if(b=g.lowercase(b))for(c=e.length-1;0<=c&&e[c]!=b;c--);if(0<=c){for(k=e.length-1;k>=c;k--)d.end&&d.end(e[k]);e.length=
7
- c}}var b,f,e=[],l=a;for(e.last=function(){return e[e.length-1]};a;){f=!0;if(e.last()&&v[e.last()])a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+e.last()+"[^>]*>","i"),function(a,b){b=b.replace(F,"$1").replace(G,"$1");d.chars&&d.chars(p(b));return""}),k("",e.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(d.comment&&d.comment(a.substring(4,b)),a=a.substring(b+3),f=!1);else if(w.test(a)){if(b=a.match(w))a=a.replace(b[0],""),f=!1}else if(H.test(a)){if(b=a.match(x))a=
8
- a.substring(b[0].length),b[0].replace(x,k),f=!1}else I.test(a)&&(b=a.match(y))&&(a=a.substring(b[0].length),b[0].replace(y,c),f=!1);f&&(b=a.indexOf("<"),f=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),d.chars&&d.chars(p(f)))}if(a==l)throw J("badparse",a);l=a}k()}function p(a){q.innerHTML=a.replace(/</g,"&lt;");return q.innerText||q.textContent||""}function z(a){return a.replace(/&/g,"&amp;").replace(K,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function A(a){var d=
9
- !1,c=g.bind(a,a.push);return{start:function(a,b,f){a=g.lowercase(a);!d&&v[a]&&(d=a);d||!0!==B[a]||(c("<"),c(a),g.forEach(b,function(a,b){var d=g.lowercase(b);!0!==L[d]||!0===C[d]&&!a.match(M)||(c(" "),c(b),c('="'),c(z(a)),c('"'))}),c(f?"/>":">"))},end:function(a){a=g.lowercase(a);d||!0!==B[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(z(a))}}}var J=g.$$minErr("$sanitize"),y=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,x=/^<\s*\/\s*([\w:-]+)[^>]*>/,
10
- E=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,I=/^</,H=/^<\s*\//,F=/\x3c!--(.*?)--\x3e/g,w=/<!DOCTYPE([^>]*?)>/i,G=/<!\[CDATA\[(.*?)]]\x3e/g,M=/^((ftp|https?):\/\/|mailto:|tel:|#)/i,K=/([^\#-~| |!])/g,u=h("area,br,col,hr,img,wbr");m=h("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");n=h("rp,rt");var t=g.extend({},n,m),r=g.extend({},m,h("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),
11
- s=g.extend({},n,h("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),v=h("script,style"),B=g.extend({},u,r,s,t),C=h("background,cite,href,longdesc,src,usemap"),L=g.extend({},C,h("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),
12
- q=document.createElement("pre");g.module("ngSanitize",[]).value("$sanitize",function(a){var d=[];D(a,A(d));return d.join("")});g.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,d=/^mailto:/;return function(c,k){if(!c)return c;var b,f=c,e=[],l=A(e),h,m,n={};g.isDefined(k)&&(n.target=k);for(;b=f.match(a);)h=b[0],b[2]==b[3]&&(h="mailto:"+h),m=b.index,l.chars(f.substr(0,m)),n.href=h,l.start("a",n),l.chars(b[0].replace(d,"")),l.end("a"),
13
- f=f.substring(m+b[0].length);l.chars(f);return e.join("")}})})(window,window.angular);
6
+ (function(n,h,q){'use strict';function F(a){var e=[];t(e,h.noop).chars(a);return e.join("")}function k(a){var e={};a=a.split(",");var d;for(d=0;d<a.length;d++)e[a[d]]=!0;return e}function G(a,e){function d(a,b,d,g){b=h.lowercase(b);if(u[b])for(;f.last()&&v[f.last()];)c("",f.last());w[b]&&f.last()==b&&c("",b);(g=x[b]||!!g)||f.push(b);var l={};d.replace(H,function(a,b,e,c,m){l[b]=r(e||c||m||"")});e.start&&e.start(b,l,g)}function c(a,b){var c=0,d;if(b=h.lowercase(b))for(c=f.length-1;0<=c&&f[c]!=b;c--);
7
+ if(0<=c){for(d=f.length-1;d>=c;d--)e.end&&e.end(f[d]);f.length=c}}var b,g,f=[],l=a;for(f.last=function(){return f[f.length-1]};a;){g=!0;if(f.last()&&y[f.last()])a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+f.last()+"[^>]*>","i"),function(a,b){b=b.replace(I,"$1").replace(J,"$1");e.chars&&e.chars(r(b));return""}),c("",f.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(e.comment&&e.comment(a.substring(4,b)),a=a.substring(b+3),g=!1);else if(z.test(a)){if(b=a.match(z))a=
8
+ a.replace(b[0],""),g=!1}else if(K.test(a)){if(b=a.match(A))a=a.substring(b[0].length),b[0].replace(A,c),g=!1}else L.test(a)&&(b=a.match(B))&&(a=a.substring(b[0].length),b[0].replace(B,d),g=!1);g&&(b=a.indexOf("<"),g=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),e.chars&&e.chars(r(g)))}if(a==l)throw M("badparse",a);l=a}c()}function r(a){if(!a)return"";a=/^(\s*)([\s\S]*?)(\s*)$/.exec(a);a[0]="";a[2]&&(s.innerHTML=a[2].replace(/</g,"&lt;"),a[2]=s.innerText||s.textContent);return a.join("")}function C(a){return a.replace(/&/g,
9
+ "&amp;").replace(N,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function t(a,e){var d=!1,c=h.bind(a,a.push);return{start:function(a,g,f){a=h.lowercase(a);!d&&y[a]&&(d=a);d||!0!==D[a]||(c("<"),c(a),h.forEach(g,function(d,f){var g=h.lowercase(f),k="img"===a&&"src"===g||"background"===g;!0!==O[g]||!0===E[g]&&!e(d,k)||(c(" "),c(f),c('="'),c(C(d)),c('"'))}),c(f?"/>":">"))},end:function(a){a=h.lowercase(a);d||!0!==D[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||
10
+ c(C(a))}}}var M=h.$$minErr("$sanitize"),B=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,A=/^<\s*\/\s*([\w:-]+)[^>]*>/,H=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,L=/^</,K=/^<\s*\//,I=/\x3c!--(.*?)--\x3e/g,z=/<!DOCTYPE([^>]*?)>/i,J=/<!\[CDATA\[(.*?)]]\x3e/g,N=/([^\#-~| |!])/g,x=k("area,br,col,hr,img,wbr");n=k("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");q=k("rp,rt");var w=h.extend({},q,n),u=h.extend({},n,k("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),
11
+ v=h.extend({},q,k("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),y=k("script,style"),D=h.extend({},x,u,v,w),E=k("background,cite,href,longdesc,src,usemap"),O=h.extend({},E,k("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),
12
+ s=document.createElement("pre");h.module("ngSanitize",[]).provider("$sanitize",function(){this.$get=["$$sanitizeUri",function(a){return function(e){var d=[];G(e,t(d,function(c,b){return!/^unsafe/.test(a(c,b))}));return d.join("")}}]});h.module("ngSanitize").filter("linky",["$sanitize",function(a){var e=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,d=/^mailto:/;return function(c,b){function g(a){a&&m.push(F(a))}function f(a,c){m.push("<a ");h.isDefined(b)&&(m.push('target="'),
13
+ m.push(b),m.push('" '));m.push('href="');m.push(a);m.push('">');g(c);m.push("</a>")}if(!c)return c;for(var l,k=c,m=[],p,n;l=k.match(e);)p=l[0],l[2]==l[3]&&(p="mailto:"+p),n=l.index,g(k.substr(0,n)),f(p,l[0].replace(d,"")),k=k.substring(n+l[0].length);g(k);return a(m.join(""))}}])})(window,window.angular);
14
14
  //# sourceMappingURL=angular-sanitize.min.js.map
@@ -9790,8 +9790,8 @@ if ( typeof module === "object" && module && typeof module.exports === "object"
9790
9790
  })( window );
9791
9791
 
9792
9792
  /**
9793
- * @license AngularJS v1.2.0
9794
- * (c) 2010-2012 Google, Inc. http://angularjs.org
9793
+ * @license AngularJS v1.2.3
9794
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
9795
9795
  * License: MIT
9796
9796
  */
9797
9797
  (function(window, document){
@@ -9832,11 +9832,11 @@ function minErr(module) {
9832
9832
  template = arguments[1],
9833
9833
  templateArgs = arguments,
9834
9834
  stringify = function (obj) {
9835
- if (isFunction(obj)) {
9835
+ if (typeof obj === 'function') {
9836
9836
  return obj.toString().replace(/ \{[\s\S]*$/, '');
9837
- } else if (isUndefined(obj)) {
9837
+ } else if (typeof obj === 'undefined') {
9838
9838
  return 'undefined';
9839
- } else if (!isString(obj)) {
9839
+ } else if (typeof obj !== 'string') {
9840
9840
  return JSON.stringify(obj);
9841
9841
  }
9842
9842
  return obj;
@@ -9848,11 +9848,11 @@ function minErr(module) {
9848
9848
 
9849
9849
  if (index + 2 < templateArgs.length) {
9850
9850
  arg = templateArgs[index + 2];
9851
- if (isFunction(arg)) {
9851
+ if (typeof arg === 'function') {
9852
9852
  return arg.toString().replace(/ ?\{[\s\S]*$/, '');
9853
- } else if (isUndefined(arg)) {
9853
+ } else if (typeof arg === 'undefined') {
9854
9854
  return 'undefined';
9855
- } else if (!isString(arg)) {
9855
+ } else if (typeof arg !== 'string') {
9856
9856
  return toJson(arg);
9857
9857
  }
9858
9858
  return arg;
@@ -9860,7 +9860,7 @@ function minErr(module) {
9860
9860
  return match;
9861
9861
  });
9862
9862
 
9863
- message = message + '\nhttp://errors.angularjs.org/' + version.full + '/' +
9863
+ message = message + '\nhttp://errors.angularjs.org/1.2.3/' +
9864
9864
  (module ? module + '/' : '') + code;
9865
9865
  for (i = 2; i < arguments.length; i++) {
9866
9866
  message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
@@ -9951,7 +9951,7 @@ function minErr(module) {
9951
9951
  -assertArgFn,
9952
9952
  -assertNotHasOwnProperty,
9953
9953
  -getter,
9954
- -getBlockElements
9954
+ -getBlockElements,
9955
9955
 
9956
9956
  */
9957
9957
 
@@ -10415,7 +10415,7 @@ var trim = (function() {
10415
10415
  // TODO: we should move this into IE/ES5 polyfill
10416
10416
  if (!String.prototype.trim) {
10417
10417
  return function(value) {
10418
- return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
10418
+ return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
10419
10419
  };
10420
10420
  }
10421
10421
  return function(value) {
@@ -10973,26 +10973,38 @@ function encodeUriQuery(val, pctEncodeSpaces) {
10973
10973
  *
10974
10974
  * @description
10975
10975
  *
10976
- * Use this directive to auto-bootstrap an application. Only
10977
- * one ngApp directive can be used per HTML document. The directive
10978
- * designates the root of the application and is typically placed
10979
- * at the root of the page.
10976
+ * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
10977
+ * designates the **root element** of the application and is typically placed near the root element
10978
+ * of the page - e.g. on the `<body>` or `<html>` tags.
10980
10979
  *
10981
- * The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in
10982
- * an HTML document you must manually bootstrap them using {@link angular.bootstrap}.
10983
- * Applications cannot be nested.
10980
+ * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
10981
+ * found in the document will be used to define the root element to auto-bootstrap as an
10982
+ * application. To run multiple applications in an HTML document you must manually bootstrap them using
10983
+ * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
10984
10984
  *
10985
- * In the example below if the `ngApp` directive were not placed
10986
- * on the `html` element then the document would not be compiled
10987
- * and the `{{ 1+2 }}` would not be resolved to `3`.
10985
+ * You can specify an **AngularJS module** to be used as the root module for the application. This
10986
+ * module will be loaded into the {@link AUTO.$injector} when the application is bootstrapped and
10987
+ * should contain the application code needed or have dependencies on other modules that will
10988
+ * contain the code. See {@link angular.module} for more information.
10988
10989
  *
10989
- * `ngApp` is the easiest way to bootstrap an application.
10990
+ * In the example below if the `ngApp` directive were not placed on the `html` element then the
10991
+ * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
10992
+ * would not be resolved to `3`.
10990
10993
  *
10991
- <doc:example>
10992
- <doc:source>
10993
- I can add: 1 + 2 = {{ 1+2 }}
10994
- </doc:source>
10995
- </doc:example>
10994
+ * `ngApp` is the easiest, and most common, way to bootstrap an application.
10995
+ *
10996
+ <example module="ngAppDemo">
10997
+ <file name="index.html">
10998
+ <div ng-controller="ngAppDemoController">
10999
+ I can add: {{a}} + {{b}} = {{ a+b }}
11000
+ </file>
11001
+ <file name="script.js">
11002
+ angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
11003
+ $scope.a = 1;
11004
+ $scope.b = 2;
11005
+ });
11006
+ </file>
11007
+ </example>
10996
11008
  *
10997
11009
  */
10998
11010
  function angularInit(element, bootstrap) {
@@ -11221,12 +11233,18 @@ function getBlockElements(block) {
11221
11233
  function setupModuleLoader(window) {
11222
11234
 
11223
11235
  var $injectorMinErr = minErr('$injector');
11236
+ var ngMinErr = minErr('ng');
11224
11237
 
11225
11238
  function ensure(obj, name, factory) {
11226
11239
  return obj[name] || (obj[name] = factory());
11227
11240
  }
11228
11241
 
11229
- return ensure(ensure(window, 'angular', Object), 'module', function() {
11242
+ var angular = ensure(window, 'angular', Object);
11243
+
11244
+ // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
11245
+ angular.$$minErr = angular.$$minErr || minErr;
11246
+
11247
+ return ensure(angular, 'module', function() {
11230
11248
  /** @type {Object.<string, angular.Module>} */
11231
11249
  var modules = {};
11232
11250
 
@@ -11281,6 +11299,12 @@ function setupModuleLoader(window) {
11281
11299
  * @returns {module} new module with the {@link angular.Module} api.
11282
11300
  */
11283
11301
  return function module(name, requires, configFn) {
11302
+ var assertNotHasOwnProperty = function(name, context) {
11303
+ if (name === 'hasOwnProperty') {
11304
+ throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
11305
+ }
11306
+ };
11307
+
11284
11308
  assertNotHasOwnProperty(name, 'module');
11285
11309
  if (requires && modules.hasOwnProperty(name)) {
11286
11310
  modules[name] = null;
@@ -11570,6 +11594,7 @@ function setupModuleLoader(window) {
11570
11594
  $ParseProvider,
11571
11595
  $RootScopeProvider,
11572
11596
  $QProvider,
11597
+ $$SanitizeUriProvider,
11573
11598
  $SceProvider,
11574
11599
  $SceDelegateProvider,
11575
11600
  $SnifferProvider,
@@ -11593,11 +11618,11 @@ function setupModuleLoader(window) {
11593
11618
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
11594
11619
  */
11595
11620
  var version = {
11596
- full: '1.2.0', // all of these placeholder strings will be replaced by grunt's
11621
+ full: '1.2.3', // all of these placeholder strings will be replaced by grunt's
11597
11622
  major: 1, // package task
11598
- minor: "NG_VERSION_MINOR",
11599
- dot: 0,
11600
- codeName: 'timely-delivery'
11623
+ minor: 2,
11624
+ dot: 3,
11625
+ codeName: 'unicorn-zapper'
11601
11626
  };
11602
11627
 
11603
11628
 
@@ -11641,6 +11666,10 @@ function publishExternalAPI(angular){
11641
11666
 
11642
11667
  angularModule('ng', ['ngLocale'], ['$provide',
11643
11668
  function ngModule($provide) {
11669
+ // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
11670
+ $provide.provider({
11671
+ $$sanitizeUri: $$SanitizeUriProvider
11672
+ });
11644
11673
  $provide.provider('$compile', $CompileProvider).
11645
11674
  directive({
11646
11675
  a: htmlAnchorDirective,
@@ -13144,11 +13173,11 @@ function annotate(fn) {
13144
13173
  * @example
13145
13174
  * Here are some examples of creating value services.
13146
13175
  * <pre>
13147
- * $provide.constant('ADMIN_USER', 'admin');
13176
+ * $provide.value('ADMIN_USER', 'admin');
13148
13177
  *
13149
- * $provide.constant('RoleLookup', { admin: 0, writer: 1, reader: 2 });
13178
+ * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
13150
13179
  *
13151
- * $provide.constant('halfOf', function(value) {
13180
+ * $provide.value('halfOf', function(value) {
13152
13181
  * return value / 2;
13153
13182
  * });
13154
13183
  * </pre>
@@ -13634,13 +13663,14 @@ var $AnimateProvider = ['$provide', function($provide) {
13634
13663
  * inserted into the DOM
13635
13664
  */
13636
13665
  enter : function(element, parent, after, done) {
13637
- var afterNode = after && after[after.length - 1];
13638
- var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
13639
- // IE does not like undefined so we have to pass null.
13640
- var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
13641
- forEach(element, function(node) {
13642
- parentNode.insertBefore(node, afterNextSibling);
13643
- });
13666
+ if (after) {
13667
+ after.after(element);
13668
+ } else {
13669
+ if (!parent || !parent[0]) {
13670
+ parent = after.parent();
13671
+ }
13672
+ parent.append(element);
13673
+ }
13644
13674
  done && $timeout(done, 0, false);
13645
13675
  },
13646
13676
 
@@ -14488,8 +14518,9 @@ function $TemplateCacheProvider() {
14488
14518
  * When there are multiple directives defined on a single DOM element, sometimes it
14489
14519
  * is necessary to specify the order in which the directives are applied. The `priority` is used
14490
14520
  * to sort the directives before their `compile` functions get called. Priority is defined as a
14491
- * number. Directives with greater numerical `priority` are compiled first. The order of directives with
14492
- * the same priority is undefined. The default priority is `0`.
14521
+ * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
14522
+ * are also run in priority order, but post-link functions are run in reverse order. The order
14523
+ * of directives with the same priority is undefined. The default priority is `0`.
14493
14524
  *
14494
14525
  * #### `terminal`
14495
14526
  * If set to true then the current `priority` will be the last set of directives
@@ -14550,8 +14581,9 @@ function $TemplateCacheProvider() {
14550
14581
  * * `$scope` - Current scope associated with the element
14551
14582
  * * `$element` - Current element
14552
14583
  * * `$attrs` - Current attributes object for the element
14553
- * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
14554
- * `function(cloneLinkingFn)`.
14584
+ * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope.
14585
+ * The scope can be overridden by an optional first argument.
14586
+ * `function([scope], cloneLinkingFn)`.
14555
14587
  *
14556
14588
  *
14557
14589
  * #### `require`
@@ -14644,7 +14676,7 @@ function $TemplateCacheProvider() {
14644
14676
  * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
14645
14677
  * between all directive compile functions.
14646
14678
  *
14647
- * * `transclude` - A transclude linking function: `function(scope, cloneLinkingFn)`.
14679
+ * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
14648
14680
  *
14649
14681
  * <div class="alert alert-warning">
14650
14682
  * **Note:** The template instance and the link instance may be different objects if the template has
@@ -14653,6 +14685,12 @@ function $TemplateCacheProvider() {
14653
14685
  * should be done in a linking function rather than in a compile function.
14654
14686
  * </div>
14655
14687
  *
14688
+ * <div class="alert alert-error">
14689
+ * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
14690
+ * e.g. does not know about the right outer scope. Please use the transclude function that is passed
14691
+ * to the link function instead.
14692
+ * </div>
14693
+
14656
14694
  * A compile function can have a return value which can be either a function or an object.
14657
14695
  *
14658
14696
  * * returning a (post-link) function - is equivalent to registering the linking function via the
@@ -14667,7 +14705,7 @@ function $TemplateCacheProvider() {
14667
14705
  * This property is used only if the `compile` property is not defined.
14668
14706
  *
14669
14707
  * <pre>
14670
- * function link(scope, iElement, iAttrs, controller) { ... }
14708
+ * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
14671
14709
  * </pre>
14672
14710
  *
14673
14711
  * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
@@ -14688,6 +14726,10 @@ function $TemplateCacheProvider() {
14688
14726
  * element defines a controller. The controller is shared among all the directives, which allows
14689
14727
  * the directives to use the controllers as a communication channel.
14690
14728
  *
14729
+ * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
14730
+ * The scope can be overridden by an optional first argument. This is the same as the `$transclude`
14731
+ * parameter of directive controllers.
14732
+ * `function([scope], cloneLinkingFn)`.
14691
14733
  *
14692
14734
  *
14693
14735
  * #### Pre-linking function
@@ -14854,14 +14896,12 @@ var $compileMinErr = minErr('$compile');
14854
14896
  *
14855
14897
  * @description
14856
14898
  */
14857
- $CompileProvider.$inject = ['$provide'];
14858
- function $CompileProvider($provide) {
14899
+ $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
14900
+ function $CompileProvider($provide, $$sanitizeUriProvider) {
14859
14901
  var hasDirectives = {},
14860
14902
  Suffix = 'Directive',
14861
14903
  COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
14862
- CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
14863
- aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
14864
- imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
14904
+ CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
14865
14905
 
14866
14906
  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
14867
14907
  // The assumption is that future DOM event attribute names will begin with
@@ -14945,10 +14985,11 @@ function $CompileProvider($provide) {
14945
14985
  */
14946
14986
  this.aHrefSanitizationWhitelist = function(regexp) {
14947
14987
  if (isDefined(regexp)) {
14948
- aHrefSanitizationWhitelist = regexp;
14988
+ $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
14949
14989
  return this;
14990
+ } else {
14991
+ return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
14950
14992
  }
14951
- return aHrefSanitizationWhitelist;
14952
14993
  };
14953
14994
 
14954
14995
 
@@ -14975,18 +15016,18 @@ function $CompileProvider($provide) {
14975
15016
  */
14976
15017
  this.imgSrcSanitizationWhitelist = function(regexp) {
14977
15018
  if (isDefined(regexp)) {
14978
- imgSrcSanitizationWhitelist = regexp;
15019
+ $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
14979
15020
  return this;
15021
+ } else {
15022
+ return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
14980
15023
  }
14981
- return imgSrcSanitizationWhitelist;
14982
15024
  };
14983
15025
 
14984
-
14985
15026
  this.$get = [
14986
15027
  '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
14987
- '$controller', '$rootScope', '$document', '$sce', '$animate',
15028
+ '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
14988
15029
  function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
14989
- $controller, $rootScope, $document, $sce, $animate) {
15030
+ $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
14990
15031
 
14991
15032
  var Attributes = function(element, attr) {
14992
15033
  this.$$element = element;
@@ -15033,6 +15074,24 @@ function $CompileProvider($provide) {
15033
15074
  }
15034
15075
  },
15035
15076
 
15077
+ /**
15078
+ * @ngdoc function
15079
+ * @name ng.$compile.directive.Attributes#$updateClass
15080
+ * @methodOf ng.$compile.directive.Attributes
15081
+ * @function
15082
+ *
15083
+ * @description
15084
+ * Adds and removes the appropriate CSS class values to the element based on the difference
15085
+ * between the new and old CSS class values (specified as newClasses and oldClasses).
15086
+ *
15087
+ * @param {string} newClasses The current CSS className value
15088
+ * @param {string} oldClasses The former CSS className value
15089
+ */
15090
+ $updateClass : function(newClasses, oldClasses) {
15091
+ this.$removeClass(tokenDifference(oldClasses, newClasses));
15092
+ this.$addClass(tokenDifference(newClasses, oldClasses));
15093
+ },
15094
+
15036
15095
  /**
15037
15096
  * Set a normalized attribute on the element in a way such that all directives
15038
15097
  * can share the attribute. This function properly handles boolean attributes.
@@ -15043,59 +15102,44 @@ function $CompileProvider($provide) {
15043
15102
  * @param {string=} attrName Optional none normalized name. Defaults to key.
15044
15103
  */
15045
15104
  $set: function(key, value, writeAttr, attrName) {
15046
- //special case for class attribute addition + removal
15047
- //so that class changes can tap into the animation
15048
- //hooks provided by the $animate service
15049
- if(key == 'class') {
15050
- value = value || '';
15051
- var current = this.$$element.attr('class') || '';
15052
- this.$removeClass(tokenDifference(current, value).join(' '));
15053
- this.$addClass(tokenDifference(value, current).join(' '));
15054
- } else {
15055
- var booleanKey = getBooleanAttrName(this.$$element[0], key),
15056
- normalizedVal,
15057
- nodeName;
15105
+ // TODO: decide whether or not to throw an error if "class"
15106
+ //is set through this function since it may cause $updateClass to
15107
+ //become unstable.
15058
15108
 
15059
- if (booleanKey) {
15060
- this.$$element.prop(key, value);
15061
- attrName = booleanKey;
15062
- }
15109
+ var booleanKey = getBooleanAttrName(this.$$element[0], key),
15110
+ normalizedVal,
15111
+ nodeName;
15063
15112
 
15064
- this[key] = value;
15113
+ if (booleanKey) {
15114
+ this.$$element.prop(key, value);
15115
+ attrName = booleanKey;
15116
+ }
15065
15117
 
15066
- // translate normalized key to actual key
15067
- if (attrName) {
15068
- this.$attr[key] = attrName;
15069
- } else {
15070
- attrName = this.$attr[key];
15071
- if (!attrName) {
15072
- this.$attr[key] = attrName = snake_case(key, '-');
15073
- }
15074
- }
15118
+ this[key] = value;
15075
15119
 
15076
- nodeName = nodeName_(this.$$element);
15077
-
15078
- // sanitize a[href] and img[src] values
15079
- if ((nodeName === 'A' && key === 'href') ||
15080
- (nodeName === 'IMG' && key === 'src')) {
15081
- // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
15082
- if (!msie || msie >= 8 ) {
15083
- normalizedVal = urlResolve(value).href;
15084
- if (normalizedVal !== '') {
15085
- if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
15086
- (key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
15087
- this[key] = value = 'unsafe:' + normalizedVal;
15088
- }
15089
- }
15090
- }
15120
+ // translate normalized key to actual key
15121
+ if (attrName) {
15122
+ this.$attr[key] = attrName;
15123
+ } else {
15124
+ attrName = this.$attr[key];
15125
+ if (!attrName) {
15126
+ this.$attr[key] = attrName = snake_case(key, '-');
15091
15127
  }
15128
+ }
15092
15129
 
15093
- if (writeAttr !== false) {
15094
- if (value === null || value === undefined) {
15095
- this.$$element.removeAttr(attrName);
15096
- } else {
15097
- this.$$element.attr(attrName, value);
15098
- }
15130
+ nodeName = nodeName_(this.$$element);
15131
+
15132
+ // sanitize a[href] and img[src] values
15133
+ if ((nodeName === 'A' && key === 'href') ||
15134
+ (nodeName === 'IMG' && key === 'src')) {
15135
+ this[key] = value = $$sanitizeUri(value, key === 'src');
15136
+ }
15137
+
15138
+ if (writeAttr !== false) {
15139
+ if (value === null || value === undefined) {
15140
+ this.$$element.removeAttr(attrName);
15141
+ } else {
15142
+ this.$$element.attr(attrName, value);
15099
15143
  }
15100
15144
  }
15101
15145
 
@@ -15108,22 +15152,6 @@ function $CompileProvider($provide) {
15108
15152
  $exceptionHandler(e);
15109
15153
  }
15110
15154
  });
15111
-
15112
- function tokenDifference(str1, str2) {
15113
- var values = [],
15114
- tokens1 = str1.split(/\s+/),
15115
- tokens2 = str2.split(/\s+/);
15116
-
15117
- outer:
15118
- for(var i=0;i<tokens1.length;i++) {
15119
- var token = tokens1[i];
15120
- for(var j=0;j<tokens2.length;j++) {
15121
- if(token == tokens2[j]) continue outer;
15122
- }
15123
- values.push(token);
15124
- }
15125
- return values;
15126
- }
15127
15155
  },
15128
15156
 
15129
15157
 
@@ -15193,7 +15221,7 @@ function $CompileProvider($provide) {
15193
15221
  var compositeLinkFn =
15194
15222
  compileNodes($compileNodes, transcludeFn, $compileNodes,
15195
15223
  maxPriority, ignoreDirective, previousCompileContext);
15196
- return function publicLinkFn(scope, cloneConnectFn){
15224
+ return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
15197
15225
  assertArg(scope, 'scope');
15198
15226
  // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
15199
15227
  // and sometimes changes the structure of the DOM.
@@ -15201,6 +15229,10 @@ function $CompileProvider($provide) {
15201
15229
  ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
15202
15230
  : $compileNodes;
15203
15231
 
15232
+ forEach(transcludeControllers, function(instance, name) {
15233
+ $linkNode.data('$' + name + 'Controller', instance);
15234
+ });
15235
+
15204
15236
  // Attach scope only to non-text nodes.
15205
15237
  for(var i = 0, ii = $linkNode.length; i<ii; i++) {
15206
15238
  var node = $linkNode[i];
@@ -15299,15 +15331,7 @@ function $CompileProvider($provide) {
15299
15331
  childTranscludeFn = nodeLinkFn.transclude;
15300
15332
  if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
15301
15333
  nodeLinkFn(childLinkFn, childScope, node, $rootElement,
15302
- (function(transcludeFn) {
15303
- return function(cloneFn) {
15304
- var transcludeScope = scope.$new();
15305
- transcludeScope.$$transcluded = true;
15306
-
15307
- return transcludeFn(transcludeScope, cloneFn).
15308
- on('$destroy', bind(transcludeScope, transcludeScope.$destroy));
15309
- };
15310
- })(childTranscludeFn || transcludeFn)
15334
+ createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
15311
15335
  );
15312
15336
  } else {
15313
15337
  nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
@@ -15319,6 +15343,23 @@ function $CompileProvider($provide) {
15319
15343
  }
15320
15344
  }
15321
15345
 
15346
+ function createBoundTranscludeFn(scope, transcludeFn) {
15347
+ return function boundTranscludeFn(transcludedScope, cloneFn, controllers) {
15348
+ var scopeCreated = false;
15349
+
15350
+ if (!transcludedScope) {
15351
+ transcludedScope = scope.$new();
15352
+ transcludedScope.$$transcluded = true;
15353
+ scopeCreated = true;
15354
+ }
15355
+
15356
+ var clone = transcludeFn(transcludedScope, cloneFn, controllers);
15357
+ if (scopeCreated) {
15358
+ clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));
15359
+ }
15360
+ return clone;
15361
+ };
15362
+ }
15322
15363
 
15323
15364
  /**
15324
15365
  * Looks for directives on the given node and adds them to the directive collection which is
@@ -15456,9 +15497,9 @@ function $CompileProvider($provide) {
15456
15497
  * @returns {Function}
15457
15498
  */
15458
15499
  function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
15459
- return function(scope, element, attrs, controllers) {
15500
+ return function(scope, element, attrs, controllers, transcludeFn) {
15460
15501
  element = groupScan(element[0], attrStart, attrEnd);
15461
- return linkFn(scope, element, attrs, controllers);
15502
+ return linkFn(scope, element, attrs, controllers, transcludeFn);
15462
15503
  };
15463
15504
  }
15464
15505
 
@@ -15495,7 +15536,9 @@ function $CompileProvider($provide) {
15495
15536
  controllerDirectives = previousCompileContext.controllerDirectives,
15496
15537
  newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
15497
15538
  templateDirective = previousCompileContext.templateDirective,
15498
- transcludeDirective = previousCompileContext.transcludeDirective,
15539
+ nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
15540
+ hasTranscludeDirective = false,
15541
+ hasElementTranscludeDirective = false,
15499
15542
  $compileNode = templateAttrs.$$element = jqLite(compileNode),
15500
15543
  directive,
15501
15544
  directiveName,
@@ -15546,15 +15589,18 @@ function $CompileProvider($provide) {
15546
15589
  }
15547
15590
 
15548
15591
  if (directiveValue = directive.transclude) {
15592
+ hasTranscludeDirective = true;
15593
+
15549
15594
  // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
15550
15595
  // This option should only be used by directives that know how to how to safely handle element transclusion,
15551
15596
  // where the transcluded nodes are added or replaced after linking.
15552
15597
  if (!directive.$$tlb) {
15553
- assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode);
15554
- transcludeDirective = directive;
15598
+ assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
15599
+ nonTlbTranscludeDirective = directive;
15555
15600
  }
15556
15601
 
15557
15602
  if (directiveValue == 'element') {
15603
+ hasElementTranscludeDirective = true;
15558
15604
  terminalPriority = directive.priority;
15559
15605
  $template = groupScan(compileNode, attrStart, attrEnd);
15560
15606
  $compileNode = templateAttrs.$$element =
@@ -15570,9 +15616,9 @@ function $CompileProvider($provide) {
15570
15616
  // - newIsolateScopeDirective or templateDirective - combining templates with
15571
15617
  // element transclusion doesn't make sense.
15572
15618
  //
15573
- // We need only transcludeDirective so that we prevent putting transclusion
15619
+ // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
15574
15620
  // on the same element more than once.
15575
- transcludeDirective: transcludeDirective
15621
+ nonTlbTranscludeDirective: nonTlbTranscludeDirective
15576
15622
  });
15577
15623
  } else {
15578
15624
  $template = jqLite(jqLiteClone(compileNode)).contents();
@@ -15641,7 +15687,7 @@ function $CompileProvider($provide) {
15641
15687
  controllerDirectives: controllerDirectives,
15642
15688
  newIsolateScopeDirective: newIsolateScopeDirective,
15643
15689
  templateDirective: templateDirective,
15644
- transcludeDirective: transcludeDirective
15690
+ nonTlbTranscludeDirective: nonTlbTranscludeDirective
15645
15691
  });
15646
15692
  ii = directives.length;
15647
15693
  } else if (directive.compile) {
@@ -15665,7 +15711,7 @@ function $CompileProvider($provide) {
15665
15711
  }
15666
15712
 
15667
15713
  nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
15668
- nodeLinkFn.transclude = transcludeDirective && childTranscludeFn;
15714
+ nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
15669
15715
 
15670
15716
  // might be normal or delayed nodeLinkFn depending on if templateUrl is present
15671
15717
  return nodeLinkFn;
@@ -15692,7 +15738,7 @@ function $CompileProvider($provide) {
15692
15738
  }
15693
15739
 
15694
15740
 
15695
- function getControllers(require, $element) {
15741
+ function getControllers(require, $element, elementControllers) {
15696
15742
  var value, retrievalMethod = 'data', optional = false;
15697
15743
  if (isString(require)) {
15698
15744
  while((value = require.charAt(0)) == '^' || value == '?') {
@@ -15702,13 +15748,12 @@ function $CompileProvider($provide) {
15702
15748
  }
15703
15749
  optional = optional || value == '?';
15704
15750
  }
15751
+ value = null;
15705
15752
 
15706
- value = $element[retrievalMethod]('$' + require + 'Controller');
15707
-
15708
- if ($element[0].nodeType == 8 && $element[0].$$controller) { // Transclusion comment node
15709
- value = value || $element[0].$$controller;
15710
- $element[0].$$controller = null;
15753
+ if (elementControllers && retrievalMethod === 'data') {
15754
+ value = elementControllers[require];
15711
15755
  }
15756
+ value = value || $element[retrievalMethod]('$' + require + 'Controller');
15712
15757
 
15713
15758
  if (!value && !optional) {
15714
15759
  throw $compileMinErr('ctreq',
@@ -15719,7 +15764,7 @@ function $CompileProvider($provide) {
15719
15764
  } else if (isArray(require)) {
15720
15765
  value = [];
15721
15766
  forEach(require, function(require) {
15722
- value.push(getControllers(require, $element));
15767
+ value.push(getControllers(require, $element, elementControllers));
15723
15768
  });
15724
15769
  }
15725
15770
  return value;
@@ -15727,7 +15772,7 @@ function $CompileProvider($provide) {
15727
15772
 
15728
15773
 
15729
15774
  function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
15730
- var attrs, $element, i, ii, linkFn, controller, isolateScope;
15775
+ var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn;
15731
15776
 
15732
15777
  if (compileNode === linkNode) {
15733
15778
  attrs = templateAttrs;
@@ -15821,14 +15866,14 @@ function $CompileProvider($provide) {
15821
15866
  }
15822
15867
  });
15823
15868
  }
15824
-
15869
+ transcludeFn = boundTranscludeFn && controllersBoundTransclude;
15825
15870
  if (controllerDirectives) {
15826
15871
  forEach(controllerDirectives, function(directive) {
15827
15872
  var locals = {
15828
15873
  $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
15829
15874
  $element: $element,
15830
15875
  $attrs: attrs,
15831
- $transclude: boundTranscludeFn
15876
+ $transclude: transcludeFn
15832
15877
  }, controllerInstance;
15833
15878
 
15834
15879
  controller = directive.controller;
@@ -15837,16 +15882,16 @@ function $CompileProvider($provide) {
15837
15882
  }
15838
15883
 
15839
15884
  controllerInstance = $controller(controller, locals);
15840
-
15841
- // Directives with element transclusion and a controller need to attach controller
15842
- // to the comment node created by the compiler, but jQuery .data doesn't support
15843
- // attaching data to comment nodes so instead we set it directly on the element and
15844
- // remove it after we read it later.
15845
- if ($element[0].nodeType == 8) { // Transclusion comment node
15846
- $element[0].$$controller = controllerInstance;
15847
- } else {
15885
+ // For directives with element transclusion the element is a comment,
15886
+ // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
15887
+ // clean up (http://bugs.jquery.com/ticket/8335).
15888
+ // Instead, we save the controllers for the element in a local hash and attach to .data
15889
+ // later, once we have the actual element.
15890
+ elementControllers[directive.name] = controllerInstance;
15891
+ if (!hasElementTranscludeDirective) {
15848
15892
  $element.data('$' + directive.name + 'Controller', controllerInstance);
15849
15893
  }
15894
+
15850
15895
  if (directive.controllerAs) {
15851
15896
  locals.$scope[directive.controllerAs] = controllerInstance;
15852
15897
  }
@@ -15858,7 +15903,7 @@ function $CompileProvider($provide) {
15858
15903
  try {
15859
15904
  linkFn = preLinkFns[i];
15860
15905
  linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
15861
- linkFn.require && getControllers(linkFn.require, $element));
15906
+ linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
15862
15907
  } catch (e) {
15863
15908
  $exceptionHandler(e, startingTag($element));
15864
15909
  }
@@ -15878,11 +15923,28 @@ function $CompileProvider($provide) {
15878
15923
  try {
15879
15924
  linkFn = postLinkFns[i];
15880
15925
  linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
15881
- linkFn.require && getControllers(linkFn.require, $element));
15926
+ linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
15882
15927
  } catch (e) {
15883
15928
  $exceptionHandler(e, startingTag($element));
15884
15929
  }
15885
15930
  }
15931
+
15932
+ // This is the function that is injected as `$transclude`.
15933
+ function controllersBoundTransclude(scope, cloneAttachFn) {
15934
+ var transcludeControllers;
15935
+
15936
+ // no scope passed
15937
+ if (arguments.length < 2) {
15938
+ cloneAttachFn = scope;
15939
+ scope = undefined;
15940
+ }
15941
+
15942
+ if (hasElementTranscludeDirective) {
15943
+ transcludeControllers = elementControllers;
15944
+ }
15945
+
15946
+ return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers);
15947
+ }
15886
15948
  }
15887
15949
  }
15888
15950
 
@@ -15961,6 +16023,7 @@ function $CompileProvider($provide) {
15961
16023
  dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
15962
16024
  } else if (key == 'style') {
15963
16025
  $element.attr('style', $element.attr('style') + ';' + value);
16026
+ dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
15964
16027
  // `dst` will never contain hasOwnProperty as DOM parser won't let it.
15965
16028
  // You will get an "InvalidCharacterError: DOM Exception 5" error if you
15966
16029
  // have an attribute like "has-own-property" or "data-has-own-property", etc.
@@ -15991,7 +16054,7 @@ function $CompileProvider($provide) {
15991
16054
 
15992
16055
  $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
15993
16056
  success(function(content) {
15994
- var compileNode, tempTemplateAttrs, $template;
16057
+ var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
15995
16058
 
15996
16059
  content = denormalizeTemplate(content);
15997
16060
 
@@ -16036,7 +16099,7 @@ function $CompileProvider($provide) {
16036
16099
  var scope = linkQueue.shift(),
16037
16100
  beforeTemplateLinkNode = linkQueue.shift(),
16038
16101
  linkRootElement = linkQueue.shift(),
16039
- controller = linkQueue.shift(),
16102
+ boundTranscludeFn = linkQueue.shift(),
16040
16103
  linkNode = $compileNode[0];
16041
16104
 
16042
16105
  if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
@@ -16044,9 +16107,13 @@ function $CompileProvider($provide) {
16044
16107
  linkNode = jqLiteClone(compileNode);
16045
16108
  replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
16046
16109
  }
16047
-
16110
+ if (afterTemplateNodeLinkFn.transclude) {
16111
+ childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
16112
+ } else {
16113
+ childBoundTranscludeFn = boundTranscludeFn;
16114
+ }
16048
16115
  afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
16049
- controller);
16116
+ childBoundTranscludeFn);
16050
16117
  }
16051
16118
  linkQueue = null;
16052
16119
  }).
@@ -16054,14 +16121,14 @@ function $CompileProvider($provide) {
16054
16121
  throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url);
16055
16122
  });
16056
16123
 
16057
- return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) {
16124
+ return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
16058
16125
  if (linkQueue) {
16059
16126
  linkQueue.push(scope);
16060
16127
  linkQueue.push(node);
16061
16128
  linkQueue.push(rootElement);
16062
- linkQueue.push(controller);
16129
+ linkQueue.push(boundTranscludeFn);
16063
16130
  } else {
16064
- afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller);
16131
+ afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, boundTranscludeFn);
16065
16132
  }
16066
16133
  };
16067
16134
  }
@@ -16106,10 +16173,15 @@ function $CompileProvider($provide) {
16106
16173
 
16107
16174
 
16108
16175
  function getTrustedContext(node, attrNormalizedName) {
16176
+ if (attrNormalizedName == "srcdoc") {
16177
+ return $sce.HTML;
16178
+ }
16179
+ var tag = nodeName_(node);
16109
16180
  // maction[xlink:href] can source SVG. It's not limited to <maction>.
16110
16181
  if (attrNormalizedName == "xlinkHref" ||
16111
- (nodeName_(node) != "IMG" && (attrNormalizedName == "src" ||
16112
- attrNormalizedName == "ngSrc"))) {
16182
+ (tag == "FORM" && attrNormalizedName == "action") ||
16183
+ (tag != "IMG" && (attrNormalizedName == "src" ||
16184
+ attrNormalizedName == "ngSrc"))) {
16113
16185
  return $sce.RESOURCE_URL;
16114
16186
  }
16115
16187
  }
@@ -16154,9 +16226,19 @@ function $CompileProvider($provide) {
16154
16226
  attr[name] = interpolateFn(scope);
16155
16227
  ($$observers[name] || ($$observers[name] = [])).$$inter = true;
16156
16228
  (attr.$$observers && attr.$$observers[name].$$scope || scope).
16157
- $watch(interpolateFn, function interpolateFnWatchAction(value) {
16158
- attr.$set(name, value);
16159
- });
16229
+ $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
16230
+ //special case for class attribute addition + removal
16231
+ //so that class changes can tap into the animation
16232
+ //hooks provided by the $animate service. Be sure to
16233
+ //skip animations when the first digest occurs (when
16234
+ //both the new and the old values are the same) since
16235
+ //the CSS classes are the non-interpolated values
16236
+ if(name === 'class' && newValue != oldValue) {
16237
+ attr.$updateClass(newValue, oldValue);
16238
+ } else {
16239
+ attr.$set(name, newValue);
16240
+ }
16241
+ });
16160
16242
  }
16161
16243
  };
16162
16244
  }
@@ -16297,6 +16379,22 @@ function directiveLinkingFn(
16297
16379
  /* function(Function) */ boundTranscludeFn
16298
16380
  ){}
16299
16381
 
16382
+ function tokenDifference(str1, str2) {
16383
+ var values = '',
16384
+ tokens1 = str1.split(/\s+/),
16385
+ tokens2 = str2.split(/\s+/);
16386
+
16387
+ outer:
16388
+ for(var i = 0; i < tokens1.length; i++) {
16389
+ var token = tokens1[i];
16390
+ for(var j = 0; j < tokens2.length; j++) {
16391
+ if(token == tokens2[j]) continue outer;
16392
+ }
16393
+ values += (values.length > 0 ? ' ' : '') + token;
16394
+ }
16395
+ return values;
16396
+ }
16397
+
16300
16398
  /**
16301
16399
  * @ngdoc object
16302
16400
  * @name ng.$controllerProvider
@@ -16766,9 +16864,11 @@ function $HttpProvider() {
16766
16864
  *
16767
16865
  * # Caching
16768
16866
  *
16769
- * To enable caching, set the configuration property `cache` to `true`. When the cache is
16770
- * enabled, `$http` stores the response from the server in local cache. Next time the
16771
- * response is served from the cache without sending a request to the server.
16867
+ * To enable caching, set the request configuration `cache` property to `true` (to use default
16868
+ * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
16869
+ * When the cache is enabled, `$http` stores the response from the server in the specified
16870
+ * cache. The next time the same request is made, the response is served from the cache without
16871
+ * sending a request to the server.
16772
16872
  *
16773
16873
  * Note that even if the response is served from cache, delivery of the data is asynchronous in
16774
16874
  * the same way that real requests are.
@@ -16777,9 +16877,13 @@ function $HttpProvider() {
16777
16877
  * cache, but the cache is not populated yet, only one request to the server will be made and
16778
16878
  * the remaining requests will be fulfilled using the response from the first request.
16779
16879
  *
16780
- * A custom default cache built with $cacheFactory can be provided in $http.defaults.cache.
16781
- * To skip it, set configuration property `cache` to `false`.
16880
+ * You can change the default cache to a new object (built with
16881
+ * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
16882
+ * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set
16883
+ * their `cache` property to `true` will now use this cache object.
16782
16884
  *
16885
+ * If you set the default cache to `false` then only requests that specify their own custom
16886
+ * cache object will be cached.
16783
16887
  *
16784
16888
  * # Interceptors
16785
16889
  *
@@ -17501,12 +17605,13 @@ var XHR = window.XMLHttpRequest || function() {
17501
17605
  */
17502
17606
  function $HttpBackendProvider() {
17503
17607
  this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
17504
- return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks,
17505
- $document[0], $window.location.protocol.replace(':', ''));
17608
+ return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, $document[0]);
17506
17609
  }];
17507
17610
  }
17508
17611
 
17509
- function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
17612
+ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument) {
17613
+ var ABORTED = -1;
17614
+
17510
17615
  // TODO(vojta): fix the signature
17511
17616
  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
17512
17617
  var status;
@@ -17542,13 +17647,19 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
17542
17647
  // always async
17543
17648
  xhr.onreadystatechange = function() {
17544
17649
  if (xhr.readyState == 4) {
17545
- var responseHeaders = xhr.getAllResponseHeaders();
17650
+ var responseHeaders = null,
17651
+ response = null;
17652
+
17653
+ if(status !== ABORTED) {
17654
+ responseHeaders = xhr.getAllResponseHeaders();
17655
+ response = xhr.responseType ? xhr.response : xhr.responseText;
17656
+ }
17546
17657
 
17547
17658
  // responseText is the old-school way of retrieving response (supported by IE8 & 9)
17548
17659
  // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
17549
17660
  completeRequest(callback,
17550
17661
  status || xhr.status,
17551
- (xhr.responseType ? xhr.response : xhr.responseText),
17662
+ response,
17552
17663
  responseHeaders);
17553
17664
  }
17554
17665
  };
@@ -17572,20 +17683,20 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
17572
17683
 
17573
17684
 
17574
17685
  function timeoutRequest() {
17575
- status = -1;
17686
+ status = ABORTED;
17576
17687
  jsonpDone && jsonpDone();
17577
17688
  xhr && xhr.abort();
17578
17689
  }
17579
17690
 
17580
17691
  function completeRequest(callback, status, response, headersString) {
17581
- var protocol = locationProtocol || urlResolve(url).protocol;
17692
+ var protocol = urlResolve(url).protocol;
17582
17693
 
17583
17694
  // cancel timeout and subsequent timeout promise resolution
17584
17695
  timeoutId && $browserDefer.cancel(timeoutId);
17585
17696
  jsonpDone = xhr = null;
17586
17697
 
17587
17698
  // fix status code for file protocol (it's always 0)
17588
- status = (protocol == 'file') ? (response ? 200 : 404) : status;
17699
+ status = (protocol == 'file' && status === 0) ? (response ? 200 : 404) : status;
17589
17700
 
17590
17701
  // normalize IE bug (http://bugs.jquery.com/ticket/1450)
17591
17702
  status = status == 1223 ? 204 : status;
@@ -17601,6 +17712,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
17601
17712
  // - adds and immediately removes script elements from the document
17602
17713
  var script = rawDocument.createElement('script'),
17603
17714
  doneWrapper = function() {
17715
+ script.onreadystatechange = script.onload = script.onerror = null;
17604
17716
  rawDocument.body.removeChild(script);
17605
17717
  if (done) done();
17606
17718
  };
@@ -17608,12 +17720,16 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
17608
17720
  script.type = 'text/javascript';
17609
17721
  script.src = url;
17610
17722
 
17611
- if (msie) {
17723
+ if (msie && msie <= 8) {
17612
17724
  script.onreadystatechange = function() {
17613
- if (/loaded|complete/.test(script.readyState)) doneWrapper();
17725
+ if (/loaded|complete/.test(script.readyState)) {
17726
+ doneWrapper();
17727
+ }
17614
17728
  };
17615
17729
  } else {
17616
- script.onload = script.onerror = doneWrapper;
17730
+ script.onload = script.onerror = function() {
17731
+ doneWrapper();
17732
+ };
17617
17733
  }
17618
17734
 
17619
17735
  rawDocument.body.appendChild(script);
@@ -18043,8 +18159,8 @@ function encodePath(path) {
18043
18159
  return segments.join('/');
18044
18160
  }
18045
18161
 
18046
- function parseAbsoluteUrl(absoluteUrl, locationObj) {
18047
- var parsedUrl = urlResolve(absoluteUrl);
18162
+ function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
18163
+ var parsedUrl = urlResolve(absoluteUrl, appBase);
18048
18164
 
18049
18165
  locationObj.$$protocol = parsedUrl.protocol;
18050
18166
  locationObj.$$host = parsedUrl.hostname;
@@ -18052,12 +18168,12 @@ function parseAbsoluteUrl(absoluteUrl, locationObj) {
18052
18168
  }
18053
18169
 
18054
18170
 
18055
- function parseAppUrl(relativeUrl, locationObj) {
18171
+ function parseAppUrl(relativeUrl, locationObj, appBase) {
18056
18172
  var prefixed = (relativeUrl.charAt(0) !== '/');
18057
18173
  if (prefixed) {
18058
18174
  relativeUrl = '/' + relativeUrl;
18059
18175
  }
18060
- var match = urlResolve(relativeUrl);
18176
+ var match = urlResolve(relativeUrl, appBase);
18061
18177
  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
18062
18178
  match.pathname.substring(1) : match.pathname);
18063
18179
  locationObj.$$search = parseKeyValue(match.search);
@@ -18112,7 +18228,7 @@ function LocationHtml5Url(appBase, basePrefix) {
18112
18228
  this.$$html5 = true;
18113
18229
  basePrefix = basePrefix || '';
18114
18230
  var appBaseNoFile = stripFile(appBase);
18115
- parseAbsoluteUrl(appBase, this);
18231
+ parseAbsoluteUrl(appBase, this, appBase);
18116
18232
 
18117
18233
 
18118
18234
  /**
@@ -18127,7 +18243,7 @@ function LocationHtml5Url(appBase, basePrefix) {
18127
18243
  appBaseNoFile);
18128
18244
  }
18129
18245
 
18130
- parseAppUrl(pathUrl, this);
18246
+ parseAppUrl(pathUrl, this, appBase);
18131
18247
 
18132
18248
  if (!this.$$path) {
18133
18249
  this.$$path = '/';
@@ -18179,7 +18295,7 @@ function LocationHtml5Url(appBase, basePrefix) {
18179
18295
  function LocationHashbangUrl(appBase, hashPrefix) {
18180
18296
  var appBaseNoFile = stripFile(appBase);
18181
18297
 
18182
- parseAbsoluteUrl(appBase, this);
18298
+ parseAbsoluteUrl(appBase, this, appBase);
18183
18299
 
18184
18300
 
18185
18301
  /**
@@ -18199,8 +18315,48 @@ function LocationHashbangUrl(appBase, hashPrefix) {
18199
18315
  throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
18200
18316
  hashPrefix);
18201
18317
  }
18202
- parseAppUrl(withoutHashUrl, this);
18318
+ parseAppUrl(withoutHashUrl, this, appBase);
18319
+
18320
+ this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
18321
+
18203
18322
  this.$$compose();
18323
+
18324
+ /*
18325
+ * In Windows, on an anchor node on documents loaded from
18326
+ * the filesystem, the browser will return a pathname
18327
+ * prefixed with the drive name ('/C:/path') when a
18328
+ * pathname without a drive is set:
18329
+ * * a.setAttribute('href', '/foo')
18330
+ * * a.pathname === '/C:/foo' //true
18331
+ *
18332
+ * Inside of Angular, we're always using pathnames that
18333
+ * do not include drive names for routing.
18334
+ */
18335
+ function removeWindowsDriveName (path, url, base) {
18336
+ /*
18337
+ Matches paths for file protocol on windows,
18338
+ such as /C:/foo/bar, and captures only /foo/bar.
18339
+ */
18340
+ var windowsFilePathExp = /^\/?.*?:(\/.*)/;
18341
+
18342
+ var firstPathSegmentMatch;
18343
+
18344
+ //Get the relative path from the input URL.
18345
+ if (url.indexOf(base) === 0) {
18346
+ url = url.replace(base, '');
18347
+ }
18348
+
18349
+ /*
18350
+ * The input URL intentionally contains a
18351
+ * first path segment that ends with a colon.
18352
+ */
18353
+ if (windowsFilePathExp.exec(url)) {
18354
+ return path;
18355
+ }
18356
+
18357
+ firstPathSegmentMatch = windowsFilePathExp.exec(path);
18358
+ return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
18359
+ }
18204
18360
  };
18205
18361
 
18206
18362
  /**
@@ -18690,7 +18846,7 @@ function $LocationProvider(){
18690
18846
  *
18691
18847
  * The main purpose of this service is to simplify debugging and troubleshooting.
18692
18848
  *
18693
- * The default is not to log `debug` messages. You can use
18849
+ * The default is to log `debug` messages. You can use
18694
18850
  * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
18695
18851
  *
18696
18852
  * @example
@@ -18847,23 +19003,18 @@ var promiseWarning;
18847
19003
  // ------------------------------
18848
19004
  // Angular expressions are generally considered safe because these expressions only have direct
18849
19005
  // access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
18850
- // obtaining a reference to native JS functions such as the Function constructor, the global Window
18851
- // or Document object. In addition, many powerful functions for use by JavaScript code are
18852
- // published on scope that shouldn't be available from within an Angular expression.
19006
+ // obtaining a reference to native JS functions such as the Function constructor.
18853
19007
  //
18854
19008
  // As an example, consider the following Angular expression:
18855
19009
  //
18856
19010
  // {}.toString.constructor(alert("evil JS code"))
18857
19011
  //
18858
19012
  // We want to prevent this type of access. For the sake of performance, during the lexing phase we
18859
- // disallow any "dotted" access to any member named "constructor" or to any member whose name begins
18860
- // or ends with an underscore. The latter allows one to exclude the private / JavaScript only API
18861
- // available on the scope and controllers from the context of an Angular expression.
19013
+ // disallow any "dotted" access to any member named "constructor".
18862
19014
  //
18863
- // For reflective calls (a[b]), we check that the value of the lookup is not the Function
18864
- // constructor, Window or DOM node while evaluating the expression, which is a stronger but more
18865
- // expensive test. Since reflective calls are expensive anyway, this is not such a big deal compared
18866
- // to static dereferencing.
19015
+ // For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
19016
+ // while evaluating the expression, which is a stronger but more expensive test. Since reflective
19017
+ // calls are expensive anyway, this is not such a big deal compared to static dereferencing.
18867
19018
  //
18868
19019
  // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
18869
19020
  // against the expression language, but not to prevent exploits that were enabled by exposing
@@ -18877,20 +19028,12 @@ var promiseWarning;
18877
19028
  // In general, it is not possible to access a Window object from an angular expression unless a
18878
19029
  // window or some DOM object that has a reference to window is published onto a Scope.
18879
19030
 
18880
- function ensureSafeMemberName(name, fullExpression, allowConstructor) {
18881
- if (typeof name !== 'string' && toString.apply(name) !== "[object String]") {
18882
- return name;
18883
- }
18884
- if (name === "constructor" && !allowConstructor) {
19031
+ function ensureSafeMemberName(name, fullExpression) {
19032
+ if (name === "constructor") {
18885
19033
  throw $parseMinErr('isecfld',
18886
19034
  'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
18887
19035
  fullExpression);
18888
19036
  }
18889
- if (name.charAt(0) === '_' || name.charAt(name.length-1) === '_') {
18890
- throw $parseMinErr('isecprv',
18891
- 'Referencing private fields in Angular expressions is disallowed! Expression: {0}',
18892
- fullExpression);
18893
- }
18894
19037
  return name;
18895
19038
  }
18896
19039
 
@@ -19574,10 +19717,7 @@ Parser.prototype = {
19574
19717
 
19575
19718
  return extend(function(self, locals) {
19576
19719
  var o = obj(self, locals),
19577
- // In the getter, we will not block looking up "constructor" by name in order to support user defined
19578
- // constructors. However, if value looked up is the Function constructor, we will still block it in the
19579
- // ensureSafeObject call right after we look up o[i] (a few lines below.)
19580
- i = ensureSafeMemberName(indexFn(self, locals), parser.text, true /* allowConstructor */),
19720
+ i = indexFn(self, locals),
19581
19721
  v, p;
19582
19722
 
19583
19723
  if (!o) return undefined;
@@ -19593,7 +19733,7 @@ Parser.prototype = {
19593
19733
  return v;
19594
19734
  }, {
19595
19735
  assign: function(self, value, locals) {
19596
- var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
19736
+ var key = indexFn(self, locals);
19597
19737
  // prevent overwriting of Function.constructor which would break ensureSafeObject check
19598
19738
  var safe = ensureSafeObject(obj(self, locals), parser.text);
19599
19739
  return safe[key] = value;
@@ -19872,7 +20012,7 @@ function getterFn(path, options, fullExp) {
19872
20012
  : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
19873
20013
  (options.unwrapPromises
19874
20014
  ? 'if (s && s.then) {\n' +
19875
- ' pw("' + fullExp.replace(/\"/g, '\\"') + '");\n' +
20015
+ ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
19876
20016
  ' if (!("$$v" in s)) {\n' +
19877
20017
  ' p=s;\n' +
19878
20018
  ' p.$$v = undefined;\n' +
@@ -20253,7 +20393,7 @@ function $ParseProvider() {
20253
20393
  * // Propagate promise resolution to 'then' functions using $apply().
20254
20394
  * $rootScope.$apply();
20255
20395
  * expect(resolvedValue).toEqual(123);
20256
- * });
20396
+ * }));
20257
20397
  * </pre>
20258
20398
  */
20259
20399
  function $QProvider() {
@@ -21643,6 +21783,79 @@ function $RootScopeProvider(){
21643
21783
  }];
21644
21784
  }
21645
21785
 
21786
+ /**
21787
+ * @description
21788
+ * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
21789
+ */
21790
+ function $$SanitizeUriProvider() {
21791
+ var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
21792
+ imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
21793
+
21794
+ /**
21795
+ * @description
21796
+ * Retrieves or overrides the default regular expression that is used for whitelisting of safe
21797
+ * urls during a[href] sanitization.
21798
+ *
21799
+ * The sanitization is a security measure aimed at prevent XSS attacks via html links.
21800
+ *
21801
+ * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
21802
+ * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
21803
+ * regular expression. If a match is found, the original url is written into the dom. Otherwise,
21804
+ * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
21805
+ *
21806
+ * @param {RegExp=} regexp New regexp to whitelist urls with.
21807
+ * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
21808
+ * chaining otherwise.
21809
+ */
21810
+ this.aHrefSanitizationWhitelist = function(regexp) {
21811
+ if (isDefined(regexp)) {
21812
+ aHrefSanitizationWhitelist = regexp;
21813
+ return this;
21814
+ }
21815
+ return aHrefSanitizationWhitelist;
21816
+ };
21817
+
21818
+
21819
+ /**
21820
+ * @description
21821
+ * Retrieves or overrides the default regular expression that is used for whitelisting of safe
21822
+ * urls during img[src] sanitization.
21823
+ *
21824
+ * The sanitization is a security measure aimed at prevent XSS attacks via html links.
21825
+ *
21826
+ * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
21827
+ * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
21828
+ * regular expression. If a match is found, the original url is written into the dom. Otherwise,
21829
+ * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
21830
+ *
21831
+ * @param {RegExp=} regexp New regexp to whitelist urls with.
21832
+ * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
21833
+ * chaining otherwise.
21834
+ */
21835
+ this.imgSrcSanitizationWhitelist = function(regexp) {
21836
+ if (isDefined(regexp)) {
21837
+ imgSrcSanitizationWhitelist = regexp;
21838
+ return this;
21839
+ }
21840
+ return imgSrcSanitizationWhitelist;
21841
+ };
21842
+
21843
+ this.$get = function() {
21844
+ return function sanitizeUri(uri, isImage) {
21845
+ var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
21846
+ var normalizedVal;
21847
+ // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
21848
+ if (!msie || msie >= 8 ) {
21849
+ normalizedVal = urlResolve(uri).href;
21850
+ if (normalizedVal !== '' && !normalizedVal.match(regex)) {
21851
+ return 'unsafe:'+normalizedVal;
21852
+ }
21853
+ }
21854
+ return uri;
21855
+ };
21856
+ };
21857
+ }
21858
+
21646
21859
  var $sceMinErr = minErr('$sce');
21647
21860
 
21648
21861
  var SCE_CONTEXTS = {
@@ -21842,8 +22055,7 @@ function $SceDelegateProvider() {
21842
22055
  return resourceUrlBlacklist;
21843
22056
  };
21844
22057
 
21845
- this.$get = ['$log', '$document', '$injector', function(
21846
- $log, $document, $injector) {
22058
+ this.$get = ['$injector', function($injector) {
21847
22059
 
21848
22060
  var htmlSanitizer = function htmlSanitizer(html) {
21849
22061
  throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
@@ -22070,10 +22282,10 @@ function $SceDelegateProvider() {
22070
22282
  *
22071
22283
  * <pre class="prettyprint">
22072
22284
  * <input ng-model="userHtml">
22073
- * <div ng-bind-html="{{userHtml}}">
22285
+ * <div ng-bind-html="userHtml">
22074
22286
  * </pre>
22075
22287
  *
22076
- * Notice that `ng-bind-html` is bound to `{{userHtml}}` controlled by the user. With SCE
22288
+ * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
22077
22289
  * disabled, this application allows the user to render arbitrary HTML into the DIV.
22078
22290
  * In a more realistic example, one may be rendering user comments, blog articles, etc. via
22079
22291
  * bindings. (HTML is just one example of a context where rendering user controlled input creates
@@ -22374,18 +22586,15 @@ function $SceProvider() {
22374
22586
  * sce.js and sceSpecs.js would need to be aware of this detail.
22375
22587
  */
22376
22588
 
22377
- this.$get = ['$parse', '$document', '$sceDelegate', function(
22378
- $parse, $document, $sceDelegate) {
22589
+ this.$get = ['$parse', '$sniffer', '$sceDelegate', function(
22590
+ $parse, $sniffer, $sceDelegate) {
22379
22591
  // Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows
22380
22592
  // the "expression(javascript expression)" syntax which is insecure.
22381
- if (enabled && msie) {
22382
- var documentMode = $document[0].documentMode;
22383
- if (documentMode !== undefined && documentMode < 8) {
22384
- throw $sceMinErr('iequirks',
22385
- 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
22386
- 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
22387
- 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
22388
- }
22593
+ if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) {
22594
+ throw $sceMinErr('iequirks',
22595
+ 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
22596
+ 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
22597
+ 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
22389
22598
  }
22390
22599
 
22391
22600
  var sce = copy(SCE_CONTEXTS);
@@ -22747,6 +22956,7 @@ function $SnifferProvider() {
22747
22956
  int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
22748
22957
  boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
22749
22958
  document = $document[0] || {},
22959
+ documentMode = document.documentMode,
22750
22960
  vendorPrefix,
22751
22961
  vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
22752
22962
  bodyStyle = document.body && document.body.style,
@@ -22791,7 +23001,7 @@ function $SnifferProvider() {
22791
23001
  // jshint +W018
22792
23002
  hashchange: 'onhashchange' in $window &&
22793
23003
  // IE8 compatible mode lies
22794
- (!document.documentMode || document.documentMode > 7),
23004
+ (!documentMode || documentMode > 7),
22795
23005
  hasEvent: function(event) {
22796
23006
  // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
22797
23007
  // it. In particular the event is not fired when backspace or delete key are pressed or
@@ -22809,7 +23019,8 @@ function $SnifferProvider() {
22809
23019
  vendorPrefix: vendorPrefix,
22810
23020
  transitions : transitions,
22811
23021
  animations : animations,
22812
- msie : msie
23022
+ msie : msie,
23023
+ msieDocumentMode: documentMode
22813
23024
  };
22814
23025
  }];
22815
23026
  }
@@ -22996,6 +23207,7 @@ function $TimeoutProvider() {
22996
23207
  var urlParsingNode = document.createElement("a");
22997
23208
  var originUrl = urlResolve(window.location.href, true);
22998
23209
 
23210
+
22999
23211
  /**
23000
23212
  *
23001
23213
  * Implementation Notes for non-IE browsers
@@ -23014,7 +23226,7 @@ var originUrl = urlResolve(window.location.href, true);
23014
23226
  * browsers. However, the parsed components will not be set if the URL assigned did not specify
23015
23227
  * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
23016
23228
  * work around that by performing the parsing in a 2nd step by taking a previously normalized
23017
- * URL (e.g. by assining to a.href) and assigning it a.href again. This correctly populates the
23229
+ * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
23018
23230
  * properties such as protocol, hostname, port, etc.
23019
23231
  *
23020
23232
  * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one
@@ -23048,8 +23260,9 @@ var originUrl = urlResolve(window.location.href, true);
23048
23260
  * | pathname | The pathname, beginning with "/"
23049
23261
  *
23050
23262
  */
23051
- function urlResolve(url) {
23263
+ function urlResolve(url, base) {
23052
23264
  var href = url;
23265
+
23053
23266
  if (msie) {
23054
23267
  // Normalize before parse. Refer Implementation Notes on why this is
23055
23268
  // done in two steps on IE.
@@ -23059,7 +23272,7 @@ function urlResolve(url) {
23059
23272
 
23060
23273
  urlParsingNode.setAttribute('href', href);
23061
23274
 
23062
- // $$urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
23275
+ // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
23063
23276
  return {
23064
23277
  href: urlParsingNode.href,
23065
23278
  protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
@@ -23068,12 +23281,12 @@ function urlResolve(url) {
23068
23281
  hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
23069
23282
  hostname: urlParsingNode.hostname,
23070
23283
  port: urlParsingNode.port,
23071
- pathname: urlParsingNode.pathname && urlParsingNode.pathname.charAt(0) === '/' ?
23072
- urlParsingNode.pathname : '/' + urlParsingNode.pathname
23284
+ pathname: (urlParsingNode.pathname.charAt(0) === '/')
23285
+ ? urlParsingNode.pathname
23286
+ : '/' + urlParsingNode.pathname
23073
23287
  };
23074
23288
  }
23075
23289
 
23076
-
23077
23290
  /**
23078
23291
  * Parse a request URL and determine whether this is a same-origin request as the application document.
23079
23292
  *
@@ -24382,8 +24595,11 @@ var htmlAnchorDirective = valueFn({
24382
24595
  *
24383
24596
  * The HTML specification does not require browsers to preserve the values of boolean attributes
24384
24597
  * such as disabled. (Their presence means true and their absence means false.)
24385
- * This prevents the Angular compiler from retrieving the binding expression.
24598
+ * If we put an Angular interpolation expression into such an attribute then the
24599
+ * binding information would be lost when the browser removes the attribute.
24386
24600
  * The `ngDisabled` directive solves this problem for the `disabled` attribute.
24601
+ * This complementary directive is not removed by the browser and so provides
24602
+ * a permanent reliable place to store the binding information.
24387
24603
  *
24388
24604
  * @example
24389
24605
  <doc:example>
@@ -24414,8 +24630,11 @@ var htmlAnchorDirective = valueFn({
24414
24630
  * @description
24415
24631
  * The HTML specification does not require browsers to preserve the values of boolean attributes
24416
24632
  * such as checked. (Their presence means true and their absence means false.)
24417
- * This prevents the Angular compiler from retrieving the binding expression.
24633
+ * If we put an Angular interpolation expression into such an attribute then the
24634
+ * binding information would be lost when the browser removes the attribute.
24418
24635
  * The `ngChecked` directive solves this problem for the `checked` attribute.
24636
+ * This complementary directive is not removed by the browser and so provides
24637
+ * a permanent reliable place to store the binding information.
24419
24638
  * @example
24420
24639
  <doc:example>
24421
24640
  <doc:source>
@@ -24445,8 +24664,12 @@ var htmlAnchorDirective = valueFn({
24445
24664
  * @description
24446
24665
  * The HTML specification does not require browsers to preserve the values of boolean attributes
24447
24666
  * such as readonly. (Their presence means true and their absence means false.)
24448
- * This prevents the Angular compiler from retrieving the binding expression.
24667
+ * If we put an Angular interpolation expression into such an attribute then the
24668
+ * binding information would be lost when the browser removes the attribute.
24449
24669
  * The `ngReadonly` directive solves this problem for the `readonly` attribute.
24670
+ * This complementary directive is not removed by the browser and so provides
24671
+ * a permanent reliable place to store the binding information.
24672
+
24450
24673
  * @example
24451
24674
  <doc:example>
24452
24675
  <doc:source>
@@ -24476,8 +24699,11 @@ var htmlAnchorDirective = valueFn({
24476
24699
  * @description
24477
24700
  * The HTML specification does not require browsers to preserve the values of boolean attributes
24478
24701
  * such as selected. (Their presence means true and their absence means false.)
24479
- * This prevents the Angular compiler from retrieving the binding expression.
24702
+ * If we put an Angular interpolation expression into such an attribute then the
24703
+ * binding information would be lost when the browser removes the attribute.
24480
24704
  * The `ngSelected` directive solves this problem for the `selected` atttribute.
24705
+ * This complementary directive is not removed by the browser and so provides
24706
+ * a permanent reliable place to store the binding information.
24481
24707
  * @example
24482
24708
  <doc:example>
24483
24709
  <doc:source>
@@ -24509,8 +24735,12 @@ var htmlAnchorDirective = valueFn({
24509
24735
  * @description
24510
24736
  * The HTML specification does not require browsers to preserve the values of boolean attributes
24511
24737
  * such as open. (Their presence means true and their absence means false.)
24512
- * This prevents the Angular compiler from retrieving the binding expression.
24738
+ * If we put an Angular interpolation expression into such an attribute then the
24739
+ * binding information would be lost when the browser removes the attribute.
24513
24740
  * The `ngOpen` directive solves this problem for the `open` attribute.
24741
+ * This complementary directive is not removed by the browser and so provides
24742
+ * a permanent reliable place to store the binding information.
24743
+
24514
24744
  *
24515
24745
  * @example
24516
24746
  <doc:example>
@@ -24603,7 +24833,7 @@ var nullFormCtrl = {
24603
24833
  * @property {Object} $error Is an object hash, containing references to all invalid controls or
24604
24834
  * forms, where:
24605
24835
  *
24606
- * - keys are validation tokens (error names) — such as `required`, `url` or `email`),
24836
+ * - keys are validation tokens (error names) — such as `required`, `url` or `email`,
24607
24837
  * - values are arrays of controls or forms that are invalid with given error.
24608
24838
  *
24609
24839
  * @description
@@ -25340,8 +25570,21 @@ var inputType = {
25340
25570
 
25341
25571
 
25342
25572
  function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25573
+ // In composition mode, users are still inputing intermediate text buffer,
25574
+ // hold the listener until composition is done.
25575
+ // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
25576
+ var composing = false;
25577
+
25578
+ element.on('compositionstart', function() {
25579
+ composing = true;
25580
+ });
25581
+
25582
+ element.on('compositionend', function() {
25583
+ composing = false;
25584
+ });
25343
25585
 
25344
25586
  var listener = function() {
25587
+ if (composing) return;
25345
25588
  var value = element.val();
25346
25589
 
25347
25590
  // By default we will trim the value
@@ -25384,15 +25627,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25384
25627
  deferListener();
25385
25628
  });
25386
25629
 
25387
- // if user paste into input using mouse, we need "change" event to catch it
25388
- element.on('change', listener);
25389
-
25390
25630
  // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
25391
25631
  if ($sniffer.hasEvent('paste')) {
25392
25632
  element.on('paste cut', deferListener);
25393
25633
  }
25394
25634
  }
25395
25635
 
25636
+ // if user paste into input using mouse on older browser
25637
+ // or form autocomplete on newer browser, we need "change" event to catch it
25638
+ element.on('change', listener);
25396
25639
 
25397
25640
  ctrl.$render = function() {
25398
25641
  element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
@@ -25788,6 +26031,11 @@ var VALID_CLASS = 'ng-valid',
25788
26031
  * }
25789
26032
  * ngModel.$formatters.push(formatter);
25790
26033
  * </pre>
26034
+ *
26035
+ * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
26036
+ * view value has changed. It is called with no arguments, and its return value is ignored.
26037
+ * This can be used in place of additional $watches against the model value.
26038
+ *
25791
26039
  * @property {Object} $error An object hash with all errors as keys.
25792
26040
  *
25793
26041
  * @property {boolean} $pristine True if user has not interacted with the control yet.
@@ -26051,14 +26299,19 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
26051
26299
  * @methodOf ng.directive:ngModel.NgModelController
26052
26300
  *
26053
26301
  * @description
26054
- * Read a value from view.
26302
+ * Update the view value.
26055
26303
  *
26056
- * This method should be called from within a DOM event handler.
26057
- * For example {@link ng.directive:input input} or
26304
+ * This method should be called when the view value changes, typically from within a DOM event handler.
26305
+ * For example {@link ng.directive:input input} and
26058
26306
  * {@link ng.directive:select select} directives call it.
26059
26307
  *
26060
- * It internally calls all `$parsers` (including validators) and updates the `$modelValue` and the actual model path.
26061
- * Lastly it calls all registered change listeners.
26308
+ * It will update the $viewValue, then pass this value through each of the functions in `$parsers`,
26309
+ * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to
26310
+ * `$modelValue` and the **expression** specified in the `ng-model` attribute.
26311
+ *
26312
+ * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
26313
+ *
26314
+ * Note that calling this function does not trigger a `$digest`.
26062
26315
  *
26063
26316
  * @param {string} value Value from the view.
26064
26317
  */
@@ -26560,27 +26813,33 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
26560
26813
  * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
26561
26814
  *
26562
26815
  * @example
26563
- * Try it here: enter text in text box and watch the greeting change.
26564
- <doc:example module="ngBindHtmlExample" deps="angular-sanitize.js" >
26565
- <doc:source>
26566
- <script>
26567
- angular.module('ngBindHtmlExample', ['ngSanitize'])
26568
-
26569
- .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) {
26570
- $scope.myHTML = 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>';
26571
- }]);
26572
- </script>
26816
+ Try it here: enter text in text box and watch the greeting change.
26817
+
26818
+ <example module="ngBindHtmlExample" deps="angular-sanitize.js">
26819
+ <file name="index.html">
26573
26820
  <div ng-controller="ngBindHtmlCtrl">
26574
26821
  <p ng-bind-html="myHTML"></p>
26575
26822
  </div>
26576
- </doc:source>
26577
- <doc:scenario>
26823
+ </file>
26824
+
26825
+ <file name="script.js">
26826
+ angular.module('ngBindHtmlExample', ['ngSanitize'])
26827
+
26828
+ .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) {
26829
+ $scope.myHTML =
26830
+ 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>';
26831
+ }]);
26832
+ </file>
26833
+
26834
+ <file name="scenario.js">
26578
26835
  it('should check ng-bind-html', function() {
26579
26836
  expect(using('.doc-example-live').binding('myHTML')).
26580
- toBe('I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>');
26837
+ toBe(
26838
+ 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'
26839
+ );
26581
26840
  });
26582
- </doc:scenario>
26583
- </doc:example>
26841
+ </file>
26842
+ </example>
26584
26843
  */
26585
26844
  var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
26586
26845
  return function(scope, element, attr) {
@@ -26615,11 +26874,10 @@ function classDirective(name, selector) {
26615
26874
  // jshint bitwise: false
26616
26875
  var mod = $index & 1;
26617
26876
  if (mod !== old$index & 1) {
26618
- if (mod === selector) {
26619
- addClass(scope.$eval(attr[name]));
26620
- } else {
26621
- removeClass(scope.$eval(attr[name]));
26622
- }
26877
+ var classes = flattenClasses(scope.$eval(attr[name]));
26878
+ mod === selector ?
26879
+ attr.$addClass(classes) :
26880
+ attr.$removeClass(classes);
26623
26881
  }
26624
26882
  });
26625
26883
  }
@@ -26627,24 +26885,17 @@ function classDirective(name, selector) {
26627
26885
 
26628
26886
  function ngClassWatchAction(newVal) {
26629
26887
  if (selector === true || scope.$index % 2 === selector) {
26630
- if (oldVal && !equals(newVal,oldVal)) {
26631
- removeClass(oldVal);
26888
+ var newClasses = flattenClasses(newVal || '');
26889
+ if(!oldVal) {
26890
+ attr.$addClass(newClasses);
26891
+ } else if(!equals(newVal,oldVal)) {
26892
+ attr.$updateClass(newClasses, flattenClasses(oldVal));
26632
26893
  }
26633
- addClass(newVal);
26634
26894
  }
26635
26895
  oldVal = copy(newVal);
26636
26896
  }
26637
26897
 
26638
26898
 
26639
- function removeClass(classVal) {
26640
- attr.$removeClass(flattenClasses(classVal));
26641
- }
26642
-
26643
-
26644
- function addClass(classVal) {
26645
- attr.$addClass(flattenClasses(classVal));
26646
- }
26647
-
26648
26899
  function flattenClasses(classVal) {
26649
26900
  if(isArray(classVal)) {
26650
26901
  return classVal.join(' ');
@@ -26693,18 +26944,18 @@ function classDirective(name, selector) {
26693
26944
  * @example Example that demonstrates basic bindings via ngClass directive.
26694
26945
  <example>
26695
26946
  <file name="index.html">
26696
- <p ng-class="{strike: strike, bold: bold, red: red}">Map Syntax Example</p>
26697
- <input type="checkbox" ng-model="bold"> bold
26698
- <input type="checkbox" ng-model="strike"> strike
26699
- <input type="checkbox" ng-model="red"> red
26947
+ <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
26948
+ <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
26949
+ <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
26950
+ <input type="checkbox" ng-model="error"> error (apply "red" class)
26700
26951
  <hr>
26701
26952
  <p ng-class="style">Using String Syntax</p>
26702
26953
  <input type="text" ng-model="style" placeholder="Type: bold strike red">
26703
26954
  <hr>
26704
26955
  <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
26705
- <input ng-model="style1" placeholder="Type: bold"><br>
26706
- <input ng-model="style2" placeholder="Type: strike"><br>
26707
- <input ng-model="style3" placeholder="Type: red"><br>
26956
+ <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
26957
+ <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
26958
+ <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
26708
26959
  </file>
26709
26960
  <file name="style.css">
26710
26961
  .strike {
@@ -26723,10 +26974,10 @@ function classDirective(name, selector) {
26723
26974
  expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
26724
26975
  expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);
26725
26976
 
26726
- input('bold').check();
26977
+ input('important').check();
26727
26978
  expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);
26728
26979
 
26729
- input('red').check();
26980
+ input('error').check();
26730
26981
  expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
26731
26982
  });
26732
26983
 
@@ -27122,7 +27373,8 @@ var ngCloakDirective = ngDirective({
27122
27373
  var ngControllerDirective = [function() {
27123
27374
  return {
27124
27375
  scope: true,
27125
- controller: '@'
27376
+ controller: '@',
27377
+ priority: 500
27126
27378
  };
27127
27379
  }];
27128
27380
 
@@ -27572,7 +27824,7 @@ forEach(
27572
27824
  }
27573
27825
 
27574
27826
  /&#42;
27575
- The transition styles can also be placed on the CSS base class above
27827
+ The transition styles can also be placed on the CSS base class above
27576
27828
  &#42;/
27577
27829
  .animate-if.ng-enter, .animate-if.ng-leave {
27578
27830
  -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
@@ -27598,22 +27850,21 @@ var ngIfDirective = ['$animate', function($animate) {
27598
27850
  terminal: true,
27599
27851
  restrict: 'A',
27600
27852
  $$tlb: true,
27601
- compile: function (element, attr, transclude) {
27602
- return function ($scope, $element, $attr) {
27853
+ link: function ($scope, $element, $attr, ctrl, $transclude) {
27603
27854
  var block, childScope;
27604
27855
  $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
27605
27856
 
27606
27857
  if (toBoolean(value)) {
27607
-
27608
- childScope = $scope.$new();
27609
- transclude(childScope, function (clone) {
27610
- block = {
27611
- startNode: clone[0],
27612
- endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ')
27613
- };
27614
- $animate.enter(clone, $element.parent(), $element);
27615
- });
27616
-
27858
+ if (!childScope) {
27859
+ childScope = $scope.$new();
27860
+ $transclude(childScope, function (clone) {
27861
+ block = {
27862
+ startNode: clone[0],
27863
+ endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ')
27864
+ };
27865
+ $animate.enter(clone, $element.parent(), $element);
27866
+ });
27867
+ }
27617
27868
  } else {
27618
27869
 
27619
27870
  if (childScope) {
@@ -27627,7 +27878,6 @@ var ngIfDirective = ['$animate', function($animate) {
27627
27878
  }
27628
27879
  }
27629
27880
  });
27630
- };
27631
27881
  }
27632
27882
  };
27633
27883
  }];
@@ -27786,12 +28036,12 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
27786
28036
  priority: 400,
27787
28037
  terminal: true,
27788
28038
  transclude: 'element',
27789
- compile: function(element, attr, transclusion) {
28039
+ compile: function(element, attr) {
27790
28040
  var srcExp = attr.ngInclude || attr.src,
27791
28041
  onloadExp = attr.onload || '',
27792
28042
  autoScrollExp = attr.autoscroll;
27793
28043
 
27794
- return function(scope, $element) {
28044
+ return function(scope, $element, $attr, ctrl, $transclude) {
27795
28045
  var changeCounter = 0,
27796
28046
  currentScope,
27797
28047
  currentElement;
@@ -27820,18 +28070,23 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
27820
28070
  if (thisChangeId !== changeCounter) return;
27821
28071
  var newScope = scope.$new();
27822
28072
 
27823
- transclusion(newScope, function(clone) {
27824
- cleanupLastIncludeContent();
27825
-
27826
- currentScope = newScope;
27827
- currentElement = clone;
27828
-
27829
- currentElement.html(response);
27830
- $animate.enter(currentElement, null, $element, afterAnimation);
27831
- $compile(currentElement.contents())(currentScope);
27832
- currentScope.$emit('$includeContentLoaded');
27833
- scope.$eval(onloadExp);
27834
- });
28073
+ // Note: This will also link all children of ng-include that were contained in the original
28074
+ // html. If that content contains controllers, ... they could pollute/change the scope.
28075
+ // However, using ng-include on an element with additional content does not make sense...
28076
+ // Note: We can't remove them in the cloneAttchFn of $transclude as that
28077
+ // function is called before linking the content, which would apply child
28078
+ // directives to non existing elements.
28079
+ var clone = $transclude(newScope, noop);
28080
+ cleanupLastIncludeContent();
28081
+
28082
+ currentScope = newScope;
28083
+ currentElement = clone;
28084
+
28085
+ currentElement.html(response);
28086
+ $animate.enter(currentElement, null, $element, afterAnimation);
28087
+ $compile(currentElement.contents())(currentScope);
28088
+ currentScope.$emit('$includeContentLoaded');
28089
+ scope.$eval(onloadExp);
27835
28090
  }).error(function() {
27836
28091
  if (thisChangeId === changeCounter) cleanupLastIncludeContent();
27837
28092
  });
@@ -27986,7 +28241,7 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
27986
28241
  * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
27987
28242
  * show "a dozen people are viewing".
27988
28243
  *
27989
- * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted
28244
+ * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
27990
28245
  * into pluralized strings. In the previous example, Angular will replace `{}` with
27991
28246
  * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
27992
28247
  * for <span ng-non-bindable>{{numberExpression}}</span>.
@@ -28247,7 +28502,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
28247
28502
  * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
28248
28503
  * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
28249
28504
  * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
28250
- * element in the same way ian the DOM.
28505
+ * element in the same way in the DOM.
28251
28506
  *
28252
28507
  * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
28253
28508
  * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
@@ -28349,8 +28604,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
28349
28604
  priority: 1000,
28350
28605
  terminal: true,
28351
28606
  $$tlb: true,
28352
- compile: function(element, attr, linker) {
28353
- return function($scope, $element, $attr){
28607
+ link: function($scope, $element, $attr, ctrl, $transclude){
28354
28608
  var expression = $attr.ngRepeat;
28355
28609
  var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
28356
28610
  trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
@@ -28512,7 +28766,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
28512
28766
  // jshint bitwise: true
28513
28767
 
28514
28768
  if (!block.startNode) {
28515
- linker(childScope, function(clone) {
28769
+ $transclude(childScope, function(clone) {
28516
28770
  clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
28517
28771
  $animate.enter(clone, null, jqLite(previousNode));
28518
28772
  previousNode = clone;
@@ -28525,7 +28779,6 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
28525
28779
  }
28526
28780
  lastBlockMap = nextBlockMap;
28527
28781
  });
28528
- };
28529
28782
  }
28530
28783
  };
28531
28784
  }];
@@ -29034,10 +29287,10 @@ var ngSwitchWhenDirective = ngDirective({
29034
29287
  transclude: 'element',
29035
29288
  priority: 800,
29036
29289
  require: '^ngSwitch',
29037
- compile: function(element, attrs, transclude) {
29038
- return function(scope, element, attr, ctrl) {
29290
+ compile: function(element, attrs) {
29291
+ return function(scope, element, attr, ctrl, $transclude) {
29039
29292
  ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
29040
- ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: transclude, element: element });
29293
+ ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
29041
29294
  };
29042
29295
  }
29043
29296
  });
@@ -29046,12 +29299,10 @@ var ngSwitchDefaultDirective = ngDirective({
29046
29299
  transclude: 'element',
29047
29300
  priority: 800,
29048
29301
  require: '^ngSwitch',
29049
- compile: function(element, attrs, transclude) {
29050
- return function(scope, element, attr, ctrl) {
29051
- ctrl.cases['?'] = (ctrl.cases['?'] || []);
29052
- ctrl.cases['?'].push({ transclude: transclude, element: element });
29053
- };
29054
- }
29302
+ link: function(scope, element, attr, ctrl, $transclude) {
29303
+ ctrl.cases['?'] = (ctrl.cases['?'] || []);
29304
+ ctrl.cases['?'].push({ transclude: $transclude, element: element });
29305
+ }
29055
29306
  });
29056
29307
 
29057
29308
  /**
@@ -32032,5 +32283,5 @@ if (config.autotest) {
32032
32283
  })(window, document);
32033
32284
 
32034
32285
 
32035
- !angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n\n/* The styles below ensure that the CSS transition will ALWAYS\n * animate and close. A nasty bug occurs with CSS transitions where\n * when the active class isn\'t set, or if the active class doesn\'t\n * contain any styles to transition to, then, if ngAnimate is used,\n * it will appear as if the webpage is broken due to the forever hanging\n * animations. The clip (!ie) and zoom (ie) CSS properties are used\n * since they trigger a transition without making the browser\n * animate anything and they\'re both highly underused CSS properties */\n.ng-animate-start { clip:rect(0, auto, auto, 0); -ms-zoom:1.0001; }\n.ng-animate-active { clip:rect(-1px, auto, auto, 0); -ms-zoom:1; }\n</style>');
32286
+ !angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak,\n.ng-hide {\n display: none !important;\n}\n\nng\\:form {\n display: block;\n}\n\n/* The styles below ensure that the CSS transition will ALWAYS\n * animate and close. A nasty bug occurs with CSS transitions where\n * when the active class isn\'t set, or if the active class doesn\'t\n * contain any styles to transition to, then, if ngAnimate is used,\n * it will appear as if the webpage is broken due to the forever hanging\n * animations. The border-spacing (!ie) and zoom (ie) CSS properties are\n * used below since they trigger a transition without making the browser\n * animate anything and they\'re both highly underused CSS properties */\n.ng-animate-start { border-spacing:1px 1px; -ms-zoom:1.0001; }\n.ng-animate-active { border-spacing:0px 0px; -ms-zoom:1; }\n</style>');
32036
32287
  !angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";\n/* CSS Document */\n\n/** Structure */\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n font-size: 14px;\n}\n\n#system-error {\n font-size: 1.5em;\n text-align: center;\n}\n\n#json, #xml {\n display: none;\n}\n\n#header {\n position: fixed;\n width: 100%;\n}\n\n#specs {\n padding-top: 50px;\n}\n\n#header .angular {\n font-family: Courier New, monospace;\n font-weight: bold;\n}\n\n#header h1 {\n font-weight: normal;\n float: left;\n font-size: 30px;\n line-height: 30px;\n margin: 0;\n padding: 10px 10px;\n height: 30px;\n}\n\n#application h2,\n#specs h2 {\n margin: 0;\n padding: 0.5em;\n font-size: 1.1em;\n}\n\n#status-legend {\n margin-top: 10px;\n margin-right: 10px;\n}\n\n#header,\n#application,\n.test-info,\n.test-actions li {\n overflow: hidden;\n}\n\n#application {\n margin: 10px;\n}\n\n#application iframe {\n width: 100%;\n height: 758px;\n}\n\n#application .popout {\n float: right;\n}\n\n#application iframe {\n border: none;\n}\n\n.tests li,\n.test-actions li,\n.test-it li,\n.test-it ol,\n.status-display {\n list-style-type: none;\n}\n\n.tests,\n.test-it ol,\n.status-display {\n margin: 0;\n padding: 0;\n}\n\n.test-info {\n margin-left: 1em;\n margin-top: 0.5em;\n border-radius: 8px 0 0 8px;\n -webkit-border-radius: 8px 0 0 8px;\n -moz-border-radius: 8px 0 0 8px;\n cursor: pointer;\n}\n\n.test-info:hover .test-name {\n text-decoration: underline;\n}\n\n.test-info .closed:before {\n content: \'\\25b8\\00A0\';\n}\n\n.test-info .open:before {\n content: \'\\25be\\00A0\';\n font-weight: bold;\n}\n\n.test-it ol {\n margin-left: 2.5em;\n}\n\n.status-display,\n.status-display li {\n float: right;\n}\n\n.status-display li {\n padding: 5px 10px;\n}\n\n.timer-result,\n.test-title {\n display: inline-block;\n margin: 0;\n padding: 4px;\n}\n\n.test-actions .test-title,\n.test-actions .test-result {\n display: table-cell;\n padding-left: 0.5em;\n padding-right: 0.5em;\n}\n\n.test-actions {\n display: table;\n}\n\n.test-actions li {\n display: table-row;\n}\n\n.timer-result {\n width: 4em;\n padding: 0 10px;\n text-align: right;\n font-family: monospace;\n}\n\n.test-it pre,\n.test-actions pre {\n clear: left;\n color: black;\n margin-left: 6em;\n}\n\n.test-describe {\n padding-bottom: 0.5em;\n}\n\n.test-describe .test-describe {\n margin: 5px 5px 10px 2em;\n}\n\n.test-actions .status-pending .test-title:before {\n content: \'\\00bb\\00A0\';\n}\n\n.scrollpane {\n max-height: 20em;\n overflow: auto;\n}\n\n/** Colors */\n\n#header {\n background-color: #F2C200;\n}\n\n#specs h2 {\n border-top: 2px solid #BABAD1;\n}\n\n#specs h2,\n#application h2 {\n background-color: #efefef;\n}\n\n#application {\n border: 1px solid #BABAD1;\n}\n\n.test-describe .test-describe {\n border-left: 1px solid #BABAD1;\n border-right: 1px solid #BABAD1;\n border-bottom: 1px solid #BABAD1;\n}\n\n.status-display {\n border: 1px solid #777;\n}\n\n.status-display .status-pending,\n.status-pending .test-info {\n background-color: #F9EEBC;\n}\n\n.status-display .status-success,\n.status-success .test-info {\n background-color: #B1D7A1;\n}\n\n.status-display .status-failure,\n.status-failure .test-info {\n background-color: #FF8286;\n}\n\n.status-display .status-error,\n.status-error .test-info {\n background-color: black;\n color: white;\n}\n\n.test-actions .status-success .test-title {\n color: #30B30A;\n}\n\n.test-actions .status-failure .test-title {\n color: #DF0000;\n}\n\n.test-actions .status-error .test-title {\n color: black;\n}\n\n.test-actions .timer-result {\n color: #888;\n}\n</style>');