angular-on-rails 1.0.5 → 1.1.3u

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. data/lib/angularjs/version.rb +1 -1
  2. data/vendor/assets/javascripts/angular-bootstrap-prettify.js +1 -1
  3. data/vendor/assets/javascripts/angular-bootstrap.js +1 -1
  4. data/vendor/assets/javascripts/angular-cookies.js +1 -1
  5. data/vendor/assets/javascripts/angular-loader.js +1 -1
  6. data/vendor/assets/javascripts/angular-mocks.js +68 -37
  7. data/vendor/assets/javascripts/angular-resource.js +99 -37
  8. data/vendor/assets/javascripts/angular-sanitize.js +25 -4
  9. data/vendor/assets/javascripts/angular-scenario.js +4204 -3710
  10. data/vendor/assets/javascripts/angular.js +603 -172
  11. data/vendor/assets/javascripts/i18n/angular-locale_am-et.js +4 -0
  12. data/vendor/assets/javascripts/i18n/angular-locale_am.js +4 -0
  13. data/vendor/assets/javascripts/i18n/angular-locale_ar-eg.js +4 -0
  14. data/vendor/assets/javascripts/i18n/angular-locale_ar.js +4 -0
  15. data/vendor/assets/javascripts/i18n/angular-locale_bg-bg.js +4 -0
  16. data/vendor/assets/javascripts/i18n/angular-locale_bg.js +4 -0
  17. data/vendor/assets/javascripts/i18n/angular-locale_bn-bd.js +4 -0
  18. data/vendor/assets/javascripts/i18n/angular-locale_bn.js +4 -0
  19. data/vendor/assets/javascripts/i18n/angular-locale_ca-es.js +4 -0
  20. data/vendor/assets/javascripts/i18n/angular-locale_ca.js +4 -0
  21. data/vendor/assets/javascripts/i18n/angular-locale_cs-cz.js +4 -0
  22. data/vendor/assets/javascripts/i18n/angular-locale_cs.js +4 -0
  23. data/vendor/assets/javascripts/i18n/angular-locale_da-dk.js +4 -0
  24. data/vendor/assets/javascripts/i18n/angular-locale_da.js +4 -0
  25. data/vendor/assets/javascripts/i18n/angular-locale_de-at.js +4 -0
  26. data/vendor/assets/javascripts/i18n/angular-locale_de-be.js +4 -0
  27. data/vendor/assets/javascripts/i18n/angular-locale_de-ch.js +4 -0
  28. data/vendor/assets/javascripts/i18n/angular-locale_de-de.js +4 -0
  29. data/vendor/assets/javascripts/i18n/angular-locale_de-lu.js +4 -0
  30. data/vendor/assets/javascripts/i18n/angular-locale_de.js +4 -0
  31. data/vendor/assets/javascripts/i18n/angular-locale_el-gr.js +4 -0
  32. data/vendor/assets/javascripts/i18n/angular-locale_el-polyton.js +4 -0
  33. data/vendor/assets/javascripts/i18n/angular-locale_el.js +4 -0
  34. data/vendor/assets/javascripts/i18n/angular-locale_en-as.js +4 -0
  35. data/vendor/assets/javascripts/i18n/angular-locale_en-au.js +4 -0
  36. data/vendor/assets/javascripts/i18n/angular-locale_en-dsrt-us.js +4 -0
  37. data/vendor/assets/javascripts/i18n/angular-locale_en-dsrt.js +4 -0
  38. data/vendor/assets/javascripts/i18n/angular-locale_en-gb.js +4 -0
  39. data/vendor/assets/javascripts/i18n/angular-locale_en-gu.js +4 -0
  40. data/vendor/assets/javascripts/i18n/angular-locale_en-ie.js +4 -0
  41. data/vendor/assets/javascripts/i18n/angular-locale_en-in.js +4 -0
  42. data/vendor/assets/javascripts/i18n/angular-locale_en-iso.js +4 -0
  43. data/vendor/assets/javascripts/i18n/angular-locale_en-mh.js +4 -0
  44. data/vendor/assets/javascripts/i18n/angular-locale_en-mp.js +4 -0
  45. data/vendor/assets/javascripts/i18n/angular-locale_en-sg.js +4 -0
  46. data/vendor/assets/javascripts/i18n/angular-locale_en-um.js +4 -0
  47. data/vendor/assets/javascripts/i18n/angular-locale_en-us.js +4 -0
  48. data/vendor/assets/javascripts/i18n/angular-locale_en-vi.js +4 -0
  49. data/vendor/assets/javascripts/i18n/angular-locale_en-za.js +4 -0
  50. data/vendor/assets/javascripts/i18n/angular-locale_en-zz.js +4 -0
  51. data/vendor/assets/javascripts/i18n/angular-locale_en.js +4 -0
  52. data/vendor/assets/javascripts/i18n/angular-locale_es-es.js +4 -0
  53. data/vendor/assets/javascripts/i18n/angular-locale_es.js +4 -0
  54. data/vendor/assets/javascripts/i18n/angular-locale_et-ee.js +4 -0
  55. data/vendor/assets/javascripts/i18n/angular-locale_et.js +4 -0
  56. data/vendor/assets/javascripts/i18n/angular-locale_eu-es.js +4 -0
  57. data/vendor/assets/javascripts/i18n/angular-locale_eu.js +4 -0
  58. data/vendor/assets/javascripts/i18n/angular-locale_fa-ir.js +4 -0
  59. data/vendor/assets/javascripts/i18n/angular-locale_fa.js +4 -0
  60. data/vendor/assets/javascripts/i18n/angular-locale_fi-fi.js +4 -0
  61. data/vendor/assets/javascripts/i18n/angular-locale_fi.js +4 -0
  62. data/vendor/assets/javascripts/i18n/angular-locale_fil-ph.js +4 -0
  63. data/vendor/assets/javascripts/i18n/angular-locale_fil.js +4 -0
  64. data/vendor/assets/javascripts/i18n/angular-locale_fr-bl.js +4 -0
  65. data/vendor/assets/javascripts/i18n/angular-locale_fr-ca.js +4 -0
  66. data/vendor/assets/javascripts/i18n/angular-locale_fr-fr.js +4 -0
  67. data/vendor/assets/javascripts/i18n/angular-locale_fr-gp.js +4 -0
  68. data/vendor/assets/javascripts/i18n/angular-locale_fr-mc.js +4 -0
  69. data/vendor/assets/javascripts/i18n/angular-locale_fr-mf.js +4 -0
  70. data/vendor/assets/javascripts/i18n/angular-locale_fr-mq.js +4 -0
  71. data/vendor/assets/javascripts/i18n/angular-locale_fr-re.js +4 -0
  72. data/vendor/assets/javascripts/i18n/angular-locale_fr.js +4 -0
  73. data/vendor/assets/javascripts/i18n/angular-locale_gl-es.js +4 -0
  74. data/vendor/assets/javascripts/i18n/angular-locale_gl.js +4 -0
  75. data/vendor/assets/javascripts/i18n/angular-locale_gsw-ch.js +4 -0
  76. data/vendor/assets/javascripts/i18n/angular-locale_gsw.js +4 -0
  77. data/vendor/assets/javascripts/i18n/angular-locale_gu-in.js +4 -0
  78. data/vendor/assets/javascripts/i18n/angular-locale_gu.js +4 -0
  79. data/vendor/assets/javascripts/i18n/angular-locale_he-il.js +4 -0
  80. data/vendor/assets/javascripts/i18n/angular-locale_he.js +4 -0
  81. data/vendor/assets/javascripts/i18n/angular-locale_hi-in.js +4 -0
  82. data/vendor/assets/javascripts/i18n/angular-locale_hi.js +4 -0
  83. data/vendor/assets/javascripts/i18n/angular-locale_hr-hr.js +4 -0
  84. data/vendor/assets/javascripts/i18n/angular-locale_hr.js +4 -0
  85. data/vendor/assets/javascripts/i18n/angular-locale_hu-hu.js +4 -0
  86. data/vendor/assets/javascripts/i18n/angular-locale_hu.js +4 -0
  87. data/vendor/assets/javascripts/i18n/angular-locale_id-id.js +4 -0
  88. data/vendor/assets/javascripts/i18n/angular-locale_id.js +4 -0
  89. data/vendor/assets/javascripts/i18n/angular-locale_in.js +4 -0
  90. data/vendor/assets/javascripts/i18n/angular-locale_is-is.js +4 -0
  91. data/vendor/assets/javascripts/i18n/angular-locale_is.js +4 -0
  92. data/vendor/assets/javascripts/i18n/angular-locale_it-it.js +4 -0
  93. data/vendor/assets/javascripts/i18n/angular-locale_it.js +4 -0
  94. data/vendor/assets/javascripts/i18n/angular-locale_iw.js +4 -0
  95. data/vendor/assets/javascripts/i18n/angular-locale_ja-jp.js +4 -0
  96. data/vendor/assets/javascripts/i18n/angular-locale_ja.js +4 -0
  97. data/vendor/assets/javascripts/i18n/angular-locale_kn-in.js +4 -0
  98. data/vendor/assets/javascripts/i18n/angular-locale_kn.js +4 -0
  99. data/vendor/assets/javascripts/i18n/angular-locale_ko-kr.js +4 -0
  100. data/vendor/assets/javascripts/i18n/angular-locale_ko.js +4 -0
  101. data/vendor/assets/javascripts/i18n/angular-locale_ln-cd.js +4 -0
  102. data/vendor/assets/javascripts/i18n/angular-locale_ln.js +4 -0
  103. data/vendor/assets/javascripts/i18n/angular-locale_lt-lt.js +4 -0
  104. data/vendor/assets/javascripts/i18n/angular-locale_lt.js +4 -0
  105. data/vendor/assets/javascripts/i18n/angular-locale_lv-lv.js +4 -0
  106. data/vendor/assets/javascripts/i18n/angular-locale_lv.js +4 -0
  107. data/vendor/assets/javascripts/i18n/angular-locale_ml-in.js +4 -0
  108. data/vendor/assets/javascripts/i18n/angular-locale_ml.js +4 -0
  109. data/vendor/assets/javascripts/i18n/angular-locale_mo.js +4 -0
  110. data/vendor/assets/javascripts/i18n/angular-locale_mr-in.js +4 -0
  111. data/vendor/assets/javascripts/i18n/angular-locale_mr.js +4 -0
  112. data/vendor/assets/javascripts/i18n/angular-locale_ms-my.js +4 -0
  113. data/vendor/assets/javascripts/i18n/angular-locale_ms.js +4 -0
  114. data/vendor/assets/javascripts/i18n/angular-locale_mt-mt.js +4 -0
  115. data/vendor/assets/javascripts/i18n/angular-locale_mt.js +4 -0
  116. data/vendor/assets/javascripts/i18n/angular-locale_nl-nl.js +4 -0
  117. data/vendor/assets/javascripts/i18n/angular-locale_nl.js +4 -0
  118. data/vendor/assets/javascripts/i18n/angular-locale_no.js +4 -0
  119. data/vendor/assets/javascripts/i18n/angular-locale_or-in.js +4 -0
  120. data/vendor/assets/javascripts/i18n/angular-locale_or.js +4 -0
  121. data/vendor/assets/javascripts/i18n/angular-locale_pl-pl.js +4 -0
  122. data/vendor/assets/javascripts/i18n/angular-locale_pl.js +4 -0
  123. data/vendor/assets/javascripts/i18n/angular-locale_pt-br.js +4 -0
  124. data/vendor/assets/javascripts/i18n/angular-locale_pt-pt.js +4 -0
  125. data/vendor/assets/javascripts/i18n/angular-locale_pt.js +4 -0
  126. data/vendor/assets/javascripts/i18n/angular-locale_ro-ro.js +4 -0
  127. data/vendor/assets/javascripts/i18n/angular-locale_ro.js +4 -0
  128. data/vendor/assets/javascripts/i18n/angular-locale_ru-ru.js +4 -0
  129. data/vendor/assets/javascripts/i18n/angular-locale_ru.js +4 -0
  130. data/vendor/assets/javascripts/i18n/angular-locale_sk-sk.js +4 -0
  131. data/vendor/assets/javascripts/i18n/angular-locale_sk.js +4 -0
  132. data/vendor/assets/javascripts/i18n/angular-locale_sl-si.js +4 -0
  133. data/vendor/assets/javascripts/i18n/angular-locale_sl.js +4 -0
  134. data/vendor/assets/javascripts/i18n/angular-locale_sq-al.js +4 -0
  135. data/vendor/assets/javascripts/i18n/angular-locale_sq.js +4 -0
  136. data/vendor/assets/javascripts/i18n/angular-locale_sr-cyrl-rs.js +4 -0
  137. data/vendor/assets/javascripts/i18n/angular-locale_sr-latn-rs.js +4 -0
  138. data/vendor/assets/javascripts/i18n/angular-locale_sr-rs.js +4 -0
  139. data/vendor/assets/javascripts/i18n/angular-locale_sr.js +4 -0
  140. data/vendor/assets/javascripts/i18n/angular-locale_sv-se.js +4 -0
  141. data/vendor/assets/javascripts/i18n/angular-locale_sv.js +4 -0
  142. data/vendor/assets/javascripts/i18n/angular-locale_sw-tz.js +4 -0
  143. data/vendor/assets/javascripts/i18n/angular-locale_sw.js +4 -0
  144. data/vendor/assets/javascripts/i18n/angular-locale_ta-in.js +4 -0
  145. data/vendor/assets/javascripts/i18n/angular-locale_ta.js +4 -0
  146. data/vendor/assets/javascripts/i18n/angular-locale_te-in.js +4 -0
  147. data/vendor/assets/javascripts/i18n/angular-locale_te.js +4 -0
  148. data/vendor/assets/javascripts/i18n/angular-locale_th-th.js +4 -0
  149. data/vendor/assets/javascripts/i18n/angular-locale_th.js +4 -0
  150. data/vendor/assets/javascripts/i18n/angular-locale_tl-ph.js +4 -0
  151. data/vendor/assets/javascripts/i18n/angular-locale_tl.js +4 -0
  152. data/vendor/assets/javascripts/i18n/angular-locale_tr-tr.js +4 -0
  153. data/vendor/assets/javascripts/i18n/angular-locale_tr.js +4 -0
  154. data/vendor/assets/javascripts/i18n/angular-locale_uk-ua.js +4 -0
  155. data/vendor/assets/javascripts/i18n/angular-locale_uk.js +4 -0
  156. data/vendor/assets/javascripts/i18n/angular-locale_ur-pk.js +4 -0
  157. data/vendor/assets/javascripts/i18n/angular-locale_ur.js +4 -0
  158. data/vendor/assets/javascripts/i18n/angular-locale_vi-vn.js +4 -0
  159. data/vendor/assets/javascripts/i18n/angular-locale_vi.js +4 -0
  160. data/vendor/assets/javascripts/i18n/angular-locale_zh-cn.js +4 -0
  161. data/vendor/assets/javascripts/i18n/angular-locale_zh-hans-cn.js +4 -0
  162. data/vendor/assets/javascripts/i18n/angular-locale_zh-hans.js +4 -0
  163. data/vendor/assets/javascripts/i18n/angular-locale_zh-hk.js +4 -0
  164. data/vendor/assets/javascripts/i18n/angular-locale_zh-tw.js +4 -0
  165. data/vendor/assets/javascripts/i18n/angular-locale_zh.js +4 -0
  166. metadata +160 -10
  167. data/vendor/assets/javascripts/jstd-scenario-adapter-config.js +0 -6
  168. data/vendor/assets/javascripts/jstd-scenario-adapter.js +0 -185
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.5
2
+ * @license AngularJS v1.1.3
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -63,12 +63,32 @@ var /** holds major version number for IE or NaN for real browsers */
63
63
  push = [].push,
64
64
  toString = Object.prototype.toString,
65
65
 
66
+
67
+ _angular = window.angular,
66
68
  /** @name angular */
67
69
  angular = window.angular || (window.angular = {}),
68
70
  angularModule,
69
71
  nodeName_,
70
72
  uid = ['0', '0', '0'];
71
73
 
74
+ /**
75
+ * @ngdoc function
76
+ * @name angular.noConflict
77
+ * @function
78
+ *
79
+ * @description
80
+ * Restores the previous global value of angular and returns the current instance. Other libraries may already use the
81
+ * angular namespace. Or a previous version of angular is already loaded on the page. In these cases you may want to
82
+ * restore the previous namespace and keep a reference to angular.
83
+ *
84
+ * @return {Object} The current angular namespace
85
+ */
86
+ function noConflict() {
87
+ var a = window.angular;
88
+ window.angular = _angular;
89
+ return a;
90
+ }
91
+
72
92
  /**
73
93
  * @ngdoc function
74
94
  * @name angular.forEach
@@ -1279,11 +1299,11 @@ function setupModuleLoader(window) {
1279
1299
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
1280
1300
  */
1281
1301
  var version = {
1282
- full: '1.0.5', // all of these placeholder strings will be replaced by rake's
1302
+ full: '1.1.3', // all of these placeholder strings will be replaced by rake's
1283
1303
  major: 1, // compile task
1284
- minor: 0,
1285
- dot: 5,
1286
- codeName: 'flatulent-propulsion'
1304
+ minor: 1,
1305
+ dot: 3,
1306
+ codeName: 'radioactive-gargle'
1287
1307
  };
1288
1308
 
1289
1309
 
@@ -1313,7 +1333,8 @@ function publishExternalAPI(angular){
1313
1333
  'isDate': isDate,
1314
1334
  'lowercase': lowercase,
1315
1335
  'uppercase': uppercase,
1316
- 'callbacks': {counter: 0}
1336
+ 'callbacks': {counter: 0},
1337
+ 'noConflict': noConflict
1317
1338
  });
1318
1339
 
1319
1340
  angularModule = setupModuleLoader(window);
@@ -1749,11 +1770,11 @@ var JQLitePrototype = JQLite.prototype = {
1749
1770
  // value on get.
1750
1771
  //////////////////////////////////////////
1751
1772
  var BOOLEAN_ATTR = {};
1752
- forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) {
1773
+ forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
1753
1774
  BOOLEAN_ATTR[lowercase(value)] = value;
1754
1775
  });
1755
1776
  var BOOLEAN_ELEMENTS = {};
1756
- forEach('input,select,option,textarea,button,form'.split(','), function(value) {
1777
+ forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
1757
1778
  BOOLEAN_ELEMENTS[uppercase(value)] = true;
1758
1779
  });
1759
1780
 
@@ -2055,8 +2076,9 @@ forEach({
2055
2076
 
2056
2077
  append: function(element, node) {
2057
2078
  forEach(new JQLite(node), function(child){
2058
- if (element.nodeType === 1)
2079
+ if (element.nodeType === 1 || element.nodeType === 11) {
2059
2080
  element.appendChild(child);
2081
+ }
2060
2082
  });
2061
2083
  },
2062
2084
 
@@ -2678,9 +2700,10 @@ function createInjector(modulesToLoad) {
2678
2700
  decorator: decorator
2679
2701
  }
2680
2702
  },
2681
- providerInjector = createInternalInjector(providerCache, function() {
2682
- throw Error("Unknown provider: " + path.join(' <- '));
2683
- }),
2703
+ providerInjector = (providerCache.$injector =
2704
+ createInternalInjector(providerCache, function() {
2705
+ throw Error("Unknown provider: " + path.join(' <- '));
2706
+ })),
2684
2707
  instanceCache = {},
2685
2708
  instanceInjector = (instanceCache.$injector =
2686
2709
  createInternalInjector(instanceCache, function(servicename) {
@@ -2757,9 +2780,7 @@ function createInjector(modulesToLoad) {
2757
2780
  try {
2758
2781
  for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
2759
2782
  var invokeArgs = invokeQueue[i],
2760
- provider = invokeArgs[0] == '$injector'
2761
- ? providerInjector
2762
- : providerInjector.get(invokeArgs[0]);
2783
+ provider = providerInjector.get(invokeArgs[0]);
2763
2784
 
2764
2785
  provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
2765
2786
  }
@@ -3314,7 +3335,7 @@ function $BrowserProvider(){
3314
3335
  * @returns {object} Newly created cache object with the following set of methods:
3315
3336
  *
3316
3337
  * - `{object}` `info()` — Returns id, size, and options of cache.
3317
- * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache.
3338
+ * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it.
3318
3339
  * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
3319
3340
  * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
3320
3341
  * - `{void}` `removeAll()` — Removes all cached values.
@@ -3353,6 +3374,8 @@ function $CacheFactoryProvider() {
3353
3374
  if (size > capacity) {
3354
3375
  this.remove(staleEnd.key);
3355
3376
  }
3377
+
3378
+ return value;
3356
3379
  },
3357
3380
 
3358
3381
 
@@ -4257,6 +4280,10 @@ function $CompileProvider($provide) {
4257
4280
  scope[scopeName] = value;
4258
4281
  });
4259
4282
  attrs.$$observers[attrName].$$scope = parentScope;
4283
+ if( attrs[attrName] ) {
4284
+ // If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn
4285
+ scope[scopeName] = $interpolate(attrs[attrName])(parentScope);
4286
+ }
4260
4287
  break;
4261
4288
  }
4262
4289
 
@@ -4555,7 +4582,7 @@ function $CompileProvider($provide) {
4555
4582
  interpolateFn = $interpolate(attr[name], true);
4556
4583
  }
4557
4584
 
4558
- attr[name] = undefined;
4585
+ attr[name] = interpolateFn(scope);
4559
4586
  ($$observers[name] || ($$observers[name] = [])).$$inter = true;
4560
4587
  (attr.$$observers && attr.$$observers[name].$$scope || scope).
4561
4588
  $watch(interpolateFn, function interpolateFnWatchAction(value) {
@@ -4838,7 +4865,7 @@ function $InterpolateProvider() {
4838
4865
  };
4839
4866
 
4840
4867
 
4841
- this.$get = ['$parse', function($parse) {
4868
+ this.$get = ['$parse', '$exceptionHandler', function($parse, $exceptionHandler) {
4842
4869
  var startSymbolLength = startSymbol.length,
4843
4870
  endSymbolLength = endSymbol.length;
4844
4871
 
@@ -4910,18 +4937,24 @@ function $InterpolateProvider() {
4910
4937
  if (!mustHaveExpression || hasInterpolation) {
4911
4938
  concat.length = length;
4912
4939
  fn = function(context) {
4913
- for(var i = 0, ii = length, part; i<ii; i++) {
4914
- if (typeof (part = parts[i]) == 'function') {
4915
- part = part(context);
4916
- if (part == null || part == undefined) {
4917
- part = '';
4918
- } else if (typeof part != 'string') {
4919
- part = toJson(part);
4940
+ try {
4941
+ for(var i = 0, ii = length, part; i<ii; i++) {
4942
+ if (typeof (part = parts[i]) == 'function') {
4943
+ part = part(context);
4944
+ if (part == null || part == undefined) {
4945
+ part = '';
4946
+ } else if (typeof part != 'string') {
4947
+ part = toJson(part);
4948
+ }
4920
4949
  }
4950
+ concat[i] = part;
4921
4951
  }
4922
- concat[i] = part;
4952
+ return concat.join('');
4953
+ }
4954
+ catch(err) {
4955
+ var newErr = new Error('Error while interpolating: ' + text + '\n' + err.toString());
4956
+ $exceptionHandler(newErr);
4923
4957
  }
4924
- return concat.join('');
4925
4958
  };
4926
4959
  fn.exp = text;
4927
4960
  fn.parts = parts;
@@ -5617,7 +5650,33 @@ function $LocationProvider(){
5617
5650
  </example>
5618
5651
  */
5619
5652
 
5653
+ /**
5654
+ * @ngdoc object
5655
+ * @name ng.$logProvider
5656
+ * @description
5657
+ * Use the `$logProvider` to configure how the application logs messages
5658
+ */
5620
5659
  function $LogProvider(){
5660
+ var debug = true,
5661
+ self = this;
5662
+
5663
+ /**
5664
+ * @ngdoc property
5665
+ * @name ng.$logProvider#debugEnabled
5666
+ * @methodOf ng.$logProvider
5667
+ * @description
5668
+ * @param {string=} flag enable or disable debug level messages
5669
+ * @returns {*} current value if used as getter or itself (chaining) if used as setter
5670
+ */
5671
+ this.debugEnabled = function(flag) {
5672
+ if (isDefined(flag)) {
5673
+ debug = flag;
5674
+ return this;
5675
+ } else {
5676
+ return debug;
5677
+ }
5678
+ };
5679
+
5621
5680
  this.$get = ['$window', function($window){
5622
5681
  return {
5623
5682
  /**
@@ -5658,7 +5717,25 @@ function $LogProvider(){
5658
5717
  * @description
5659
5718
  * Write an error message
5660
5719
  */
5661
- error: consoleLog('error')
5720
+ error: consoleLog('error'),
5721
+
5722
+ /**
5723
+ * @ngdoc method
5724
+ * @name ng.$log#debug
5725
+ * @methodOf ng.$log
5726
+ *
5727
+ * @description
5728
+ * Write a debug message
5729
+ */
5730
+ debug: (function () {
5731
+ var fn = consoleLog('debug');
5732
+
5733
+ return function() {
5734
+ if (debug) {
5735
+ fn.apply(self, arguments);
5736
+ }
5737
+ }
5738
+ }())
5662
5739
  };
5663
5740
 
5664
5741
  function formatError(arg) {
@@ -5717,6 +5794,8 @@ var OPERATORS = {
5717
5794
  '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
5718
5795
  '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
5719
5796
  '=':noop,
5797
+ '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
5798
+ '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
5720
5799
  '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
5721
5800
  '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
5722
5801
  '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
@@ -5767,9 +5846,14 @@ function lex(text, csp){
5767
5846
  continue;
5768
5847
  } else {
5769
5848
  var ch2 = ch + peek(),
5849
+ ch3 = ch2 + peek(2),
5770
5850
  fn = OPERATORS[ch],
5771
- fn2 = OPERATORS[ch2];
5772
- if (fn2) {
5851
+ fn2 = OPERATORS[ch2],
5852
+ fn3 = OPERATORS[ch3];
5853
+ if (fn3) {
5854
+ tokens.push({index:index, text:ch3, fn:fn3});
5855
+ index += 3;
5856
+ } else if (fn2) {
5773
5857
  tokens.push({index:index, text:ch2, fn:fn2});
5774
5858
  index += 2;
5775
5859
  } else if (fn) {
@@ -5791,8 +5875,9 @@ function lex(text, csp){
5791
5875
  return chars.indexOf(lastCh) != -1;
5792
5876
  }
5793
5877
 
5794
- function peek() {
5795
- return index + 1 < text.length ? text.charAt(index + 1) : false;
5878
+ function peek(i) {
5879
+ var num = i || 1;
5880
+ return index + num < text.length ? text.charAt(index + num) : false;
5796
5881
  }
5797
5882
  function isNumber(ch) {
5798
5883
  return '0' <= ch && ch <= '9';
@@ -5992,6 +6077,8 @@ function parser(text, json, $filter, csp){
5992
6077
  if (tokens.length !== 0) {
5993
6078
  throwError("is an unexpected token", tokens[0]);
5994
6079
  }
6080
+ value.literal = !!value.literal;
6081
+ value.constant = !!value.constant;
5995
6082
  return value;
5996
6083
 
5997
6084
  ///////////////////////////////////
@@ -6039,15 +6126,19 @@ function parser(text, json, $filter, csp){
6039
6126
  }
6040
6127
 
6041
6128
  function unaryFn(fn, right) {
6042
- return function(self, locals) {
6129
+ return extend(function(self, locals) {
6043
6130
  return fn(self, locals, right);
6044
- };
6131
+ }, {
6132
+ constant:right.constant
6133
+ });
6045
6134
  }
6046
6135
 
6047
6136
  function binaryFn(left, fn, right) {
6048
- return function(self, locals) {
6137
+ return extend(function(self, locals) {
6049
6138
  return fn(self, locals, left, right);
6050
- };
6139
+ }, {
6140
+ constant:left.constant && right.constant
6141
+ });
6051
6142
  }
6052
6143
 
6053
6144
  function statements() {
@@ -6153,7 +6244,7 @@ function parser(text, json, $filter, csp){
6153
6244
  function equality() {
6154
6245
  var left = relational();
6155
6246
  var token;
6156
- if ((token = expect('==','!='))) {
6247
+ if ((token = expect('==','!=','===','!=='))) {
6157
6248
  left = binaryFn(left, token.fn, equality());
6158
6249
  }
6159
6250
  return left;
@@ -6215,6 +6306,9 @@ function parser(text, json, $filter, csp){
6215
6306
  if (!primary) {
6216
6307
  throwError("not a primary expression", token);
6217
6308
  }
6309
+ if (token.json) {
6310
+ primary.constant = primary.literal = true;
6311
+ }
6218
6312
  }
6219
6313
 
6220
6314
  var next, context;
@@ -6303,23 +6397,32 @@ function parser(text, json, $filter, csp){
6303
6397
  // This is used with json array declaration
6304
6398
  function arrayDeclaration () {
6305
6399
  var elementFns = [];
6400
+ var allConstant = true;
6306
6401
  if (peekToken().text != ']') {
6307
6402
  do {
6308
- elementFns.push(expression());
6403
+ var elementFn = expression();
6404
+ elementFns.push(elementFn);
6405
+ if (!elementFn.constant) {
6406
+ allConstant = false;
6407
+ }
6309
6408
  } while (expect(','));
6310
6409
  }
6311
6410
  consume(']');
6312
- return function(self, locals){
6411
+ return extend(function(self, locals){
6313
6412
  var array = [];
6314
6413
  for ( var i = 0; i < elementFns.length; i++) {
6315
6414
  array.push(elementFns[i](self, locals));
6316
6415
  }
6317
6416
  return array;
6318
- };
6417
+ }, {
6418
+ literal:true,
6419
+ constant:allConstant
6420
+ });
6319
6421
  }
6320
6422
 
6321
6423
  function object () {
6322
6424
  var keyValues = [];
6425
+ var allConstant = true;
6323
6426
  if (peekToken().text != '}') {
6324
6427
  do {
6325
6428
  var token = expect(),
@@ -6327,10 +6430,13 @@ function parser(text, json, $filter, csp){
6327
6430
  consume(":");
6328
6431
  var value = expression();
6329
6432
  keyValues.push({key:key, value:value});
6433
+ if (!value.constant) {
6434
+ allConstant = false;
6435
+ }
6330
6436
  } while (expect(','));
6331
6437
  }
6332
6438
  consume('}');
6333
- return function(self, locals){
6439
+ return extend(function(self, locals){
6334
6440
  var object = {};
6335
6441
  for ( var i = 0; i < keyValues.length; i++) {
6336
6442
  var keyValue = keyValues[i];
@@ -6338,7 +6444,10 @@ function parser(text, json, $filter, csp){
6338
6444
  object[keyValue.key] = value;
6339
6445
  }
6340
6446
  return object;
6341
- };
6447
+ }, {
6448
+ literal:true,
6449
+ constant:allConstant
6450
+ });
6342
6451
  }
6343
6452
  }
6344
6453
 
@@ -6542,8 +6651,13 @@ function getterFn(path, csp) {
6542
6651
  * * `locals` – `{object=}` – local variables context object, useful for overriding values in
6543
6652
  * `context`.
6544
6653
  *
6545
- * The return function also has an `assign` property, if the expression is assignable, which
6546
- * allows one to set values to expressions.
6654
+ * The returned function also has the following properties:
6655
+ * * `literal` `{boolean}` whether the expression's top-level node is a JavaScript
6656
+ * literal.
6657
+ * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
6658
+ * constant literals.
6659
+ * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
6660
+ * set to a function to change its value on the given context.
6547
6661
  *
6548
6662
  */
6549
6663
  function $ParseProvider() {
@@ -7000,9 +7114,18 @@ function $RouteProvider(){
7000
7114
  * `$location.path` will be updated to add or drop the trailing slash to exactly match the
7001
7115
  * route definition.
7002
7116
  *
7003
- * `path` can contain named groups starting with a colon (`:name`). All characters up to the
7004
- * next slash are matched and stored in `$routeParams` under the given `name` when the route
7005
- * matches.
7117
+ * * `path` can contain named groups starting with a colon (`:name`). All characters up
7118
+ * to the next slash are matched and stored in `$routeParams` under the given `name`
7119
+ * when the route matches.
7120
+ * * `path` can contain named groups starting with a star (`*name`). All characters are
7121
+ * eagerly stored in `$routeParams` under the given `name` when the route matches.
7122
+ *
7123
+ * For example, routes like `/color/:color/largecode/*largecode/edit` will match
7124
+ * `/color/brown/largecode/code/with/slashs/edit` and extract:
7125
+ *
7126
+ * * `color: brown`
7127
+ * * `largecode: code/with/slashs`.
7128
+ *
7006
7129
  *
7007
7130
  * @param {Object} route Mapping information to be assigned to `$route.current` on route
7008
7131
  * match.
@@ -7012,12 +7135,24 @@ function $RouteProvider(){
7012
7135
  * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
7013
7136
  * created scope or the name of a {@link angular.Module#controller registered controller}
7014
7137
  * if passed as a string.
7015
- * - `template` – `{string=}` – html template as a string that should be used by
7016
- * {@link ng.directive:ngView ngView} or
7138
+ * - `template` – `{string=|function()=}` – html template as a string or function that returns
7139
+ * an html template as a string which should be used by {@link ng.directive:ngView ngView} or
7017
7140
  * {@link ng.directive:ngInclude ngInclude} directives.
7018
- * this property takes precedence over `templateUrl`.
7019
- * - `templateUrl` – `{string=}` – path to an html template that should be used by
7020
- * {@link ng.directive:ngView ngView}.
7141
+ * This property takes precedence over `templateUrl`.
7142
+ *
7143
+ * If `template` is a function, it will be called with the following parameters:
7144
+ *
7145
+ * - `{Array.<Object>}` - route parameters extracted from the current
7146
+ * `$location.path()` by applying the current route
7147
+ *
7148
+ * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
7149
+ * template that should be used by {@link ng.directive:ngView ngView}.
7150
+ *
7151
+ * If `templateUrl` is a function, it will be called with the following parameters:
7152
+ *
7153
+ * - `{Array.<Object>}` - route parameters extracted from the current
7154
+ * `$location.path()` by applying the current route
7155
+ *
7021
7156
  * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
7022
7157
  * be injected into the controller. If any of these dependencies are promises, they will be
7023
7158
  * resolved and converted to a value before the controller is instantiated and the
@@ -7306,12 +7441,12 @@ function $RouteProvider(){
7306
7441
  // regex only once and then reuse it
7307
7442
 
7308
7443
  // Escape regexp special characters.
7309
- when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
7444
+ when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$';
7310
7445
  var regex = '',
7311
7446
  params = [],
7312
7447
  dst = {};
7313
7448
 
7314
- var re = /:(\w+)/g,
7449
+ var re = /\\([:*])(\w+)/g,
7315
7450
  paramMatch,
7316
7451
  lastMatchedIndex = 0;
7317
7452
 
@@ -7319,8 +7454,15 @@ function $RouteProvider(){
7319
7454
  // Find each :param in `when` and replace it with a capturing group.
7320
7455
  // Append all other sections of when unchanged.
7321
7456
  regex += when.slice(lastMatchedIndex, paramMatch.index);
7322
- regex += '([^\\/]*)';
7323
- params.push(paramMatch[1]);
7457
+ switch(paramMatch[1]) {
7458
+ case ':':
7459
+ regex += '([^\\/]*)';
7460
+ break;
7461
+ case '*':
7462
+ regex += '(.*)';
7463
+ break;
7464
+ }
7465
+ params.push(paramMatch[2]);
7324
7466
  lastMatchedIndex = re.lastIndex;
7325
7467
  }
7326
7468
  // Append trailing path part.
@@ -7372,9 +7514,18 @@ function $RouteProvider(){
7372
7514
  values.push(isString(value) ? $injector.get(value) : $injector.invoke(value));
7373
7515
  });
7374
7516
  if (isDefined(template = next.template)) {
7517
+ if (isFunction(template)) {
7518
+ template = template(next.params);
7519
+ }
7375
7520
  } else if (isDefined(template = next.templateUrl)) {
7376
- template = $http.get(template, {cache: $templateCache}).
7377
- then(function(response) { return response.data; });
7521
+ if (isFunction(template)) {
7522
+ template = template(next.params);
7523
+ }
7524
+ if (isDefined(template)) {
7525
+ next.loadedTemplateUrl = template;
7526
+ template = $http.get(template, {cache: $templateCache}).
7527
+ then(function(response) { return response.data; });
7528
+ }
7378
7529
  }
7379
7530
  if (isDefined(template)) {
7380
7531
  keys.push('$template');
@@ -7672,7 +7823,6 @@ function $RootScopeProvider(){
7672
7823
  child['this'] = child;
7673
7824
  child.$$listeners = {};
7674
7825
  child.$parent = this;
7675
- child.$$asyncQueue = [];
7676
7826
  child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
7677
7827
  child.$$prevSibling = this.$$childTail;
7678
7828
  if (this.$$childHead) {
@@ -7776,6 +7926,14 @@ function $RootScopeProvider(){
7776
7926
  watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
7777
7927
  }
7778
7928
 
7929
+ if (typeof watchExp == 'string' && get.constant) {
7930
+ var originalFn = watcher.fn;
7931
+ watcher.fn = function(newVal, oldVal, scope) {
7932
+ originalFn.call(this, newVal, oldVal, scope);
7933
+ arrayRemove(array, watcher);
7934
+ };
7935
+ }
7936
+
7779
7937
  if (!array) {
7780
7938
  array = scope.$$watchers = [];
7781
7939
  }
@@ -7839,7 +7997,7 @@ function $RootScopeProvider(){
7839
7997
  $digest: function() {
7840
7998
  var watch, value, last,
7841
7999
  watchers,
7842
- asyncQueue,
8000
+ asyncQueue = this.$$asyncQueue,
7843
8001
  length,
7844
8002
  dirty, ttl = TTL,
7845
8003
  next, current, target = this,
@@ -7848,18 +8006,19 @@ function $RootScopeProvider(){
7848
8006
 
7849
8007
  beginPhase('$digest');
7850
8008
 
7851
- do {
8009
+ do { // "while dirty" loop
7852
8010
  dirty = false;
7853
8011
  current = target;
7854
- do {
7855
- asyncQueue = current.$$asyncQueue;
7856
- while(asyncQueue.length) {
7857
- try {
7858
- current.$eval(asyncQueue.shift());
7859
- } catch (e) {
7860
- $exceptionHandler(e);
7861
- }
8012
+
8013
+ while(asyncQueue.length) {
8014
+ try {
8015
+ current.$eval(asyncQueue.shift());
8016
+ } catch (e) {
8017
+ $exceptionHandler(e);
7862
8018
  }
8019
+ }
8020
+
8021
+ do { // "traverse the scopes" loop
7863
8022
  if ((watchers = current.$$watchers)) {
7864
8023
  // process our watches
7865
8024
  length = watchers.length;
@@ -8296,6 +8455,7 @@ function $RootScopeProvider(){
8296
8455
  *
8297
8456
  * @name ng.$sniffer
8298
8457
  * @requires $window
8458
+ * @requires $document
8299
8459
  *
8300
8460
  * @property {boolean} history Does the browser support html5 history api ?
8301
8461
  * @property {boolean} hashchange Does the browser support hashchange event ?
@@ -8304,9 +8464,10 @@ function $RootScopeProvider(){
8304
8464
  * This is very simple implementation of testing browser's features.
8305
8465
  */
8306
8466
  function $SnifferProvider() {
8307
- this.$get = ['$window', function($window) {
8467
+ this.$get = ['$window', '$document', function($window, $document) {
8308
8468
  var eventSupport = {},
8309
- android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]);
8469
+ android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]),
8470
+ document = $document[0];
8310
8471
 
8311
8472
  return {
8312
8473
  // Android has history.pushState, but it does not update location correctly
@@ -8316,7 +8477,7 @@ function $SnifferProvider() {
8316
8477
  history: !!($window.history && $window.history.pushState && !(android < 4)),
8317
8478
  hashchange: 'onhashchange' in $window &&
8318
8479
  // IE8 compatible mode lies
8319
- (!$window.document.documentMode || $window.document.documentMode > 7),
8480
+ (!document.documentMode || document.documentMode > 7),
8320
8481
  hasEvent: function(event) {
8321
8482
  // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
8322
8483
  // it. In particular the event is not fired when backspace or delete key are pressed or
@@ -8324,14 +8485,13 @@ function $SnifferProvider() {
8324
8485
  if (event == 'input' && msie == 9) return false;
8325
8486
 
8326
8487
  if (isUndefined(eventSupport[event])) {
8327
- var divElm = $window.document.createElement('div');
8488
+ var divElm = document.createElement('div');
8328
8489
  eventSupport[event] = 'on' + event in divElm;
8329
8490
  }
8330
8491
 
8331
8492
  return eventSupport[event];
8332
8493
  },
8333
- // TODO(i): currently there is no way to feature detect CSP without triggering alerts
8334
- csp: false
8494
+ csp: document.securityPolicy ? document.securityPolicy.isActive : false
8335
8495
  };
8336
8496
  }];
8337
8497
  }
@@ -8392,6 +8552,43 @@ function parseHeaders(headers) {
8392
8552
  }
8393
8553
 
8394
8554
 
8555
+ var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/;
8556
+
8557
+
8558
+ /**
8559
+ * Parse a request and location URL and determine whether this is a same-domain request.
8560
+ *
8561
+ * @param {string} requestUrl The url of the request.
8562
+ * @param {string} locationUrl The current browser location url.
8563
+ * @returns {boolean} Whether the request is for the same domain.
8564
+ */
8565
+ function isSameDomain(requestUrl, locationUrl) {
8566
+ var match = IS_SAME_DOMAIN_URL_MATCH.exec(requestUrl);
8567
+ // if requestUrl is relative, the regex does not match.
8568
+ if (match == null) return true;
8569
+
8570
+ var domain1 = {
8571
+ protocol: match[2],
8572
+ host: match[4],
8573
+ port: int(match[6]) || DEFAULT_PORTS[match[2]] || null,
8574
+ // IE8 sets unmatched groups to '' instead of undefined.
8575
+ relativeProtocol: match[2] === undefined || match[2] === ''
8576
+ };
8577
+
8578
+ match = URL_MATCH.exec(locationUrl);
8579
+ var domain2 = {
8580
+ protocol: match[1],
8581
+ host: match[3],
8582
+ port: int(match[5]) || DEFAULT_PORTS[match[1]] || null
8583
+ };
8584
+
8585
+ return (domain1.protocol == domain2.protocol || domain1.relativeProtocol) &&
8586
+ domain1.host == domain2.host &&
8587
+ (domain1.port == domain2.port || (domain1.relativeProtocol &&
8588
+ domain2.port == DEFAULT_PORTS[domain2.protocol]));
8589
+ }
8590
+
8591
+
8395
8592
  /**
8396
8593
  * Returns a function that provides access to parsed headers.
8397
8594
  *
@@ -8451,7 +8648,7 @@ function $HttpProvider() {
8451
8648
  JSON_END = /[\}\]]\s*$/,
8452
8649
  PROTECTION_PREFIX = /^\)\]\}',?\n/;
8453
8650
 
8454
- var $config = this.defaults = {
8651
+ var defaults = this.defaults = {
8455
8652
  // transform incoming response data
8456
8653
  transformResponse: [function(data) {
8457
8654
  if (isString(data)) {
@@ -8471,12 +8668,14 @@ function $HttpProvider() {
8471
8668
  // default headers
8472
8669
  headers: {
8473
8670
  common: {
8474
- 'Accept': 'application/json, text/plain, */*',
8475
- 'X-Requested-With': 'XMLHttpRequest'
8671
+ 'Accept': 'application/json, text/plain, */*'
8476
8672
  },
8477
8673
  post: {'Content-Type': 'application/json;charset=utf-8'},
8478
8674
  put: {'Content-Type': 'application/json;charset=utf-8'}
8479
- }
8675
+ },
8676
+
8677
+ xsrfCookieName: 'XSRF-TOKEN',
8678
+ xsrfHeaderName: 'X-XSRF-TOKEN'
8480
8679
  };
8481
8680
 
8482
8681
  var providerResponseInterceptors = this.responseInterceptors = [];
@@ -8578,7 +8777,6 @@ function $HttpProvider() {
8578
8777
  *
8579
8778
  * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
8580
8779
  * - `Accept: application/json, text/plain, * / *`
8581
- * - `X-Requested-With: XMLHttpRequest`
8582
8780
  * - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
8583
8781
  * - `Content-Type: application/json`
8584
8782
  * - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
@@ -8711,9 +8909,10 @@ function $HttpProvider() {
8711
8909
  * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
8712
8910
  * an unauthorized site can gain your user's private data. Angular provides following mechanism
8713
8911
  * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
8714
- * called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
8715
- * runs on your domain could read the cookie, your server can be assured that the XHR came from
8716
- * JavaScript running on your domain.
8912
+ * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
8913
+ * JavaScript that runs on your domain could read the cookie, your server can be assured that
8914
+ * the XHR came from JavaScript running on your domain. The header will not be set for
8915
+ * cross-domain requests.
8717
8916
  *
8718
8917
  * To take advantage of this, your server needs to set a token in a JavaScript readable session
8719
8918
  * cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
@@ -8723,6 +8922,9 @@ function $HttpProvider() {
8723
8922
  * up its own tokens). We recommend that the token is a digest of your site's authentication
8724
8923
  * cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
8725
8924
  *
8925
+ * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
8926
+ * properties of either $httpProvider.defaults, or the per-request config object.
8927
+ *
8726
8928
  *
8727
8929
  * @param {object} config Object describing the request to be made and how it should be
8728
8930
  * processed. The object has following properties:
@@ -8733,6 +8935,8 @@ function $HttpProvider() {
8733
8935
  * `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified.
8734
8936
  * - **data** – `{string|Object}` – Data to be sent as the request message data.
8735
8937
  * - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server.
8938
+ * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
8939
+ * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
8736
8940
  * - **transformRequest** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
8737
8941
  * transform function or an array of such functions. The transform function takes the http
8738
8942
  * request body and headers and returns its transformed (typically serialized) version.
@@ -8747,6 +8951,8 @@ function $HttpProvider() {
8747
8951
  * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
8748
8952
  * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
8749
8953
  * requests with credentials} for more information.
8954
+ * - **responseType** - `{string}` - see {@link
8955
+ * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
8750
8956
  *
8751
8957
  * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
8752
8958
  * standard `then` method and two http specific methods: `success` and `error`. The `then`
@@ -8839,10 +9045,17 @@ function $HttpProvider() {
8839
9045
  function $http(config) {
8840
9046
  config.method = uppercase(config.method);
8841
9047
 
8842
- var reqTransformFn = config.transformRequest || $config.transformRequest,
8843
- respTransformFn = config.transformResponse || $config.transformResponse,
8844
- defHeaders = $config.headers,
8845
- reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
9048
+ var xsrfHeader = {},
9049
+ xsrfCookieName = config.xsrfCookieName || defaults.xsrfCookieName,
9050
+ xsrfHeaderName = config.xsrfHeaderName || defaults.xsrfHeaderName,
9051
+ xsrfToken = isSameDomain(config.url, $browser.url()) ?
9052
+ $browser.cookies()[xsrfCookieName] : undefined;
9053
+ xsrfHeader[xsrfHeaderName] = xsrfToken;
9054
+
9055
+ var reqTransformFn = config.transformRequest || defaults.transformRequest,
9056
+ respTransformFn = config.transformResponse || defaults.transformResponse,
9057
+ defHeaders = defaults.headers,
9058
+ reqHeaders = extend(xsrfHeader,
8846
9059
  defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
8847
9060
  reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
8848
9061
  promise;
@@ -8852,6 +9065,10 @@ function $HttpProvider() {
8852
9065
  delete reqHeaders['Content-Type'];
8853
9066
  }
8854
9067
 
9068
+ if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
9069
+ config.withCredentials = defaults.withCredentials;
9070
+ }
9071
+
8855
9072
  // send request
8856
9073
  promise = sendReq(config, reqData, reqHeaders);
8857
9074
 
@@ -8983,11 +9200,11 @@ function $HttpProvider() {
8983
9200
  *
8984
9201
  * @description
8985
9202
  * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
8986
- * default headers as well as request and response transformations.
9203
+ * default headers, withCredentials as well as request and response transformations.
8987
9204
  *
8988
9205
  * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
8989
9206
  */
8990
- $http.defaults = $config;
9207
+ $http.defaults = defaults;
8991
9208
 
8992
9209
 
8993
9210
  return $http;
@@ -9022,7 +9239,7 @@ function $HttpProvider() {
9022
9239
  * Makes the request
9023
9240
  *
9024
9241
  * !!! ACCESSES CLOSURE VARS:
9025
- * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
9242
+ * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
9026
9243
  */
9027
9244
  function sendReq(config, reqData, reqHeaders) {
9028
9245
  var deferred = $q.defer(),
@@ -9063,7 +9280,7 @@ function $HttpProvider() {
9063
9280
  // if we won't have the response in cache, send the request to the backend
9064
9281
  if (!cachedResp) {
9065
9282
  $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
9066
- config.withCredentials);
9283
+ config.withCredentials, config.responseType);
9067
9284
  }
9068
9285
 
9069
9286
  return promise;
@@ -9118,10 +9335,15 @@ function $HttpProvider() {
9118
9335
  var parts = [];
9119
9336
  forEachSorted(params, function(value, key) {
9120
9337
  if (value == null || value == undefined) return;
9121
- if (isObject(value)) {
9122
- value = toJson(value);
9123
- }
9124
- parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
9338
+ if (!isArray(value)) value = [value];
9339
+
9340
+ forEach(value, function(v) {
9341
+ if (isObject(v)) {
9342
+ v = toJson(v);
9343
+ }
9344
+ parts.push(encodeUriQuery(key) + '=' +
9345
+ encodeUriQuery(v));
9346
+ });
9125
9347
  });
9126
9348
  return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
9127
9349
  }
@@ -9163,7 +9385,7 @@ function $HttpBackendProvider() {
9163
9385
 
9164
9386
  function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
9165
9387
  // TODO(vojta): fix the signature
9166
- return function(method, url, post, callback, headers, timeout, withCredentials) {
9388
+ return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
9167
9389
  $browser.$$incOutstandingRequestCount();
9168
9390
  url = url || $browser.url();
9169
9391
 
@@ -9218,7 +9440,7 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
9218
9440
  }
9219
9441
  // end of the workaround.
9220
9442
 
9221
- completeRequest(callback, status || xhr.status, xhr.responseText,
9443
+ completeRequest(callback, status || xhr.status, xhr.response || xhr.responseText,
9222
9444
  responseHeaders);
9223
9445
  }
9224
9446
  };
@@ -9227,6 +9449,10 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
9227
9449
  xhr.withCredentials = true;
9228
9450
  }
9229
9451
 
9452
+ if (responseType) {
9453
+ xhr.responseType = responseType;
9454
+ }
9455
+
9230
9456
  xhr.send(post || '');
9231
9457
 
9232
9458
  if (timeout > 0) {
@@ -9568,6 +9794,22 @@ function $FilterProvider($provide) {
9568
9794
  * called for each element of `array`. The final result is an array of those elements that
9569
9795
  * the predicate returned true for.
9570
9796
  *
9797
+ * @param {function(expected, actual)|true|undefined} comparator Comparator which is used in
9798
+ * determining if the expected value (from the filter expression) and actual value (from
9799
+ * the object in the array) should be considered a match.
9800
+ *
9801
+ * Can be one of:
9802
+ *
9803
+ * - `function(expected, actual)`:
9804
+ * The function will be given the object value and the predicate value to compare and
9805
+ * should return true if the item should be included in filtered result.
9806
+ *
9807
+ * - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`.
9808
+ * this is essentially strict comparison of expected and actual.
9809
+ *
9810
+ * - `false|undefined`: A short hand for a function which will look for a substring match in case
9811
+ * insensitive way.
9812
+ *
9571
9813
  * @example
9572
9814
  <doc:example>
9573
9815
  <doc:source>
@@ -9575,7 +9817,8 @@ function $FilterProvider($provide) {
9575
9817
  {name:'Mary', phone:'800-BIG-MARY'},
9576
9818
  {name:'Mike', phone:'555-4321'},
9577
9819
  {name:'Adam', phone:'555-5678'},
9578
- {name:'Julie', phone:'555-8765'}]"></div>
9820
+ {name:'Julie', phone:'555-8765'},
9821
+ {name:'Juliette', phone:'555-5678'}]"></div>
9579
9822
 
9580
9823
  Search: <input ng-model="searchText">
9581
9824
  <table id="searchTextResults">
@@ -9589,9 +9832,10 @@ function $FilterProvider($provide) {
9589
9832
  Any: <input ng-model="search.$"> <br>
9590
9833
  Name only <input ng-model="search.name"><br>
9591
9834
  Phone only <input ng-model="search.phone"å><br>
9835
+ Equality <input type="checkbox" ng-model="strict"><br>
9592
9836
  <table id="searchObjResults">
9593
9837
  <tr><th>Name</th><th>Phone</th><tr>
9594
- <tr ng-repeat="friend in friends | filter:search">
9838
+ <tr ng-repeat="friend in friends | filter:search:strict">
9595
9839
  <td>{{friend.name}}</td>
9596
9840
  <td>{{friend.phone}}</td>
9597
9841
  <tr>
@@ -9611,13 +9855,19 @@ function $FilterProvider($provide) {
9611
9855
  it('should search in specific fields when filtering with a predicate object', function() {
9612
9856
  input('search.$').enter('i');
9613
9857
  expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
9614
- toEqual(['Mary', 'Mike', 'Julie']);
9858
+ toEqual(['Mary', 'Mike', 'Julie', 'Juliette']);
9859
+ });
9860
+ it('should use a equal comparison when comparator is true', function() {
9861
+ input('search.name').enter('Julie');
9862
+ input('strict').check();
9863
+ expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
9864
+ toEqual(['Julie']);
9615
9865
  });
9616
9866
  </doc:scenario>
9617
9867
  </doc:example>
9618
9868
  */
9619
9869
  function filterFilter() {
9620
- return function(array, expression) {
9870
+ return function(array, expression, comperator) {
9621
9871
  if (!isArray(array)) return array;
9622
9872
  var predicates = [];
9623
9873
  predicates.check = function(value) {
@@ -9628,20 +9878,43 @@ function filterFilter() {
9628
9878
  }
9629
9879
  return true;
9630
9880
  };
9881
+ switch(typeof comperator) {
9882
+ case "function":
9883
+ break;
9884
+ case "boolean":
9885
+ if(comperator == true) {
9886
+ comperator = function(obj, text) {
9887
+ return angular.equals(obj, text);
9888
+ }
9889
+ break;
9890
+ }
9891
+ default:
9892
+ comperator = function(obj, text) {
9893
+ text = (''+text).toLowerCase();
9894
+ return (''+obj).toLowerCase().indexOf(text) > -1
9895
+ };
9896
+ }
9631
9897
  var search = function(obj, text){
9632
- if (text.charAt(0) === '!') {
9898
+ if (typeof text == 'string' && text.charAt(0) === '!') {
9633
9899
  return !search(obj, text.substr(1));
9634
9900
  }
9635
9901
  switch (typeof obj) {
9636
9902
  case "boolean":
9637
9903
  case "number":
9638
9904
  case "string":
9639
- return ('' + obj).toLowerCase().indexOf(text) > -1;
9905
+ return comperator(obj, text);
9640
9906
  case "object":
9641
- for ( var objKey in obj) {
9642
- if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
9643
- return true;
9644
- }
9907
+ switch (typeof text) {
9908
+ case "object":
9909
+ return comperator(obj, text);
9910
+ break;
9911
+ default:
9912
+ for ( var objKey in obj) {
9913
+ if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
9914
+ return true;
9915
+ }
9916
+ }
9917
+ break;
9645
9918
  }
9646
9919
  return false;
9647
9920
  case "array":
@@ -9664,19 +9937,18 @@ function filterFilter() {
9664
9937
  for (var key in expression) {
9665
9938
  if (key == '$') {
9666
9939
  (function() {
9667
- var text = (''+expression[key]).toLowerCase();
9668
- if (!text) return;
9940
+ if (!expression[key]) return;
9941
+ var path = key
9669
9942
  predicates.push(function(value) {
9670
- return search(value, text);
9943
+ return search(value, expression[path]);
9671
9944
  });
9672
9945
  })();
9673
9946
  } else {
9674
9947
  (function() {
9948
+ if (!expression[key]) return;
9675
9949
  var path = key;
9676
- var text = (''+expression[key]).toLowerCase();
9677
- if (!text) return;
9678
9950
  predicates.push(function(value) {
9679
- return search(getter(value, path), text);
9951
+ return search(getter(value,path), expression[path]);
9680
9952
  });
9681
9953
  })();
9682
9954
  }
@@ -9940,6 +10212,9 @@ var DATE_FORMATS = {
9940
10212
  m: dateGetter('Minutes', 1),
9941
10213
  ss: dateGetter('Seconds', 2),
9942
10214
  s: dateGetter('Seconds', 1),
10215
+ // while ISO 8601 requires fractions to be prefixed with `.` or `,`
10216
+ // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
10217
+ sss: dateGetter('Milliseconds', 3),
9943
10218
  EEEE: dateStrGetter('Day'),
9944
10219
  EEE: dateStrGetter('Day', true),
9945
10220
  a: ampmGetter,
@@ -9978,6 +10253,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9978
10253
  * * `'m'`: Minute in hour (0-59)
9979
10254
  * * `'ss'`: Second in minute, padded (00-59)
9980
10255
  * * `'s'`: Second in minute (0-59)
10256
+ * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
9981
10257
  * * `'a'`: am/pm marker
9982
10258
  * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200)
9983
10259
  *
@@ -10034,18 +10310,22 @@ function dateFilter($locale) {
10034
10310
 
10035
10311
 
10036
10312
  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
10037
- function jsonStringToDate(string){
10313
+ // 1 2 3 4 5 6 7 8 9 10 11
10314
+ function jsonStringToDate(string) {
10038
10315
  var match;
10039
10316
  if (match = string.match(R_ISO8601_STR)) {
10040
10317
  var date = new Date(0),
10041
10318
  tzHour = 0,
10042
- tzMin = 0;
10319
+ tzMin = 0,
10320
+ dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
10321
+ timeSetter = match[8] ? date.setUTCHours : date.setHours;
10322
+
10043
10323
  if (match[9]) {
10044
10324
  tzHour = int(match[9] + match[10]);
10045
10325
  tzMin = int(match[9] + match[11]);
10046
10326
  }
10047
- date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
10048
- date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
10327
+ dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
10328
+ timeSetter.call(date, int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
10049
10329
  return date;
10050
10330
  }
10051
10331
  return string;
@@ -10159,20 +10439,20 @@ var uppercaseFilter = valueFn(uppercase);
10159
10439
  * @function
10160
10440
  *
10161
10441
  * @description
10162
- * Creates a new array containing only a specified number of elements in an array. The elements
10163
- * are taken from either the beginning or the end of the source array, as specified by the
10164
- * value and sign (positive or negative) of `limit`.
10442
+ * Creates a new array or string containing only a specified number of elements. The elements
10443
+ * are taken from either the beginning or the end of the source array or string, as specified by
10444
+ * the value and sign (positive or negative) of `limit`.
10165
10445
  *
10166
10446
  * Note: This function is used to augment the `Array` type in Angular expressions. See
10167
10447
  * {@link ng.$filter} for more information about Angular arrays.
10168
10448
  *
10169
- * @param {Array} array Source array to be limited.
10170
- * @param {string|Number} limit The length of the returned array. If the `limit` number is
10171
- * positive, `limit` number of items from the beginning of the source array are copied.
10172
- * If the number is negative, `limit` number of items from the end of the source array are
10173
- * copied. The `limit` will be trimmed if it exceeds `array.length`
10174
- * @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
10175
- * elements.
10449
+ * @param {Array|string} input Source array or string to be limited.
10450
+ * @param {string|number} limit The length of the returned array or string. If the `limit` number
10451
+ * is positive, `limit` number of items from the beginning of the source array/string are copied.
10452
+ * If the number is negative, `limit` number of items from the end of the source array/string
10453
+ * are copied. The `limit` will be trimmed if it exceeds `array.length`
10454
+ * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
10455
+ * had less than `limit` elements.
10176
10456
  *
10177
10457
  * @example
10178
10458
  <doc:example>
@@ -10180,59 +10460,76 @@ var uppercaseFilter = valueFn(uppercase);
10180
10460
  <script>
10181
10461
  function Ctrl($scope) {
10182
10462
  $scope.numbers = [1,2,3,4,5,6,7,8,9];
10183
- $scope.limit = 3;
10463
+ $scope.letters = "abcdefghi";
10464
+ $scope.numLimit = 3;
10465
+ $scope.letterLimit = 3;
10184
10466
  }
10185
10467
  </script>
10186
10468
  <div ng-controller="Ctrl">
10187
- Limit {{numbers}} to: <input type="integer" ng-model="limit">
10188
- <p>Output: {{ numbers | limitTo:limit }}</p>
10469
+ Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
10470
+ <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
10471
+ Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
10472
+ <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
10189
10473
  </div>
10190
10474
  </doc:source>
10191
10475
  <doc:scenario>
10192
- it('should limit the numer array to first three items', function() {
10193
- expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3');
10194
- expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]');
10476
+ it('should limit the number array to first three items', function() {
10477
+ expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3');
10478
+ expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3');
10479
+ expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]');
10480
+ expect(binding('letters | limitTo:letterLimit')).toEqual('abc');
10195
10481
  });
10196
10482
 
10197
10483
  it('should update the output when -3 is entered', function() {
10198
- input('limit').enter(-3);
10199
- expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]');
10484
+ input('numLimit').enter(-3);
10485
+ input('letterLimit').enter(-3);
10486
+ expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]');
10487
+ expect(binding('letters | limitTo:letterLimit')).toEqual('ghi');
10200
10488
  });
10201
10489
 
10202
10490
  it('should not exceed the maximum size of input array', function() {
10203
- input('limit').enter(100);
10204
- expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]');
10491
+ input('numLimit').enter(100);
10492
+ input('letterLimit').enter(100);
10493
+ expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]');
10494
+ expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi');
10205
10495
  });
10206
10496
  </doc:scenario>
10207
10497
  </doc:example>
10208
10498
  */
10209
10499
  function limitToFilter(){
10210
- return function(array, limit) {
10211
- if (!(array instanceof Array)) return array;
10500
+ return function(input, limit) {
10501
+ if (!isArray(input) && !isString(input)) return input;
10502
+
10212
10503
  limit = int(limit);
10504
+
10505
+ if (isString(input)) {
10506
+ //NaN check on limit
10507
+ if (limit) {
10508
+ return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
10509
+ } else {
10510
+ return "";
10511
+ }
10512
+ }
10513
+
10213
10514
  var out = [],
10214
10515
  i, n;
10215
10516
 
10216
- // check that array is iterable
10217
- if (!array || !(array instanceof Array))
10218
- return out;
10219
-
10220
10517
  // if abs(limit) exceeds maximum length, trim it
10221
- if (limit > array.length)
10222
- limit = array.length;
10223
- else if (limit < -array.length)
10224
- limit = -array.length;
10518
+ if (limit > input.length)
10519
+ limit = input.length;
10520
+ else if (limit < -input.length)
10521
+ limit = -input.length;
10225
10522
 
10226
10523
  if (limit > 0) {
10227
10524
  i = 0;
10228
10525
  n = limit;
10229
10526
  } else {
10230
- i = array.length + limit;
10231
- n = array.length;
10527
+ i = input.length + limit;
10528
+ n = input.length;
10232
10529
  }
10233
10530
 
10234
10531
  for (; i<n; i++) {
10235
- out.push(array[i]);
10532
+ out.push(input[i]);
10236
10533
  }
10237
10534
 
10238
10535
  return out;
@@ -10700,6 +10997,37 @@ var htmlAnchorDirective = valueFn({
10700
10997
  * @param {string} expression Angular expression that will be evaluated.
10701
10998
  */
10702
10999
 
11000
+ /**
11001
+ * @ngdoc directive
11002
+ * @name ng.directive:ngOpen
11003
+ * @restrict A
11004
+ *
11005
+ * @description
11006
+ * The HTML specs do not require browsers to preserve the special attributes such as open.
11007
+ * (The presence of them means true and absence means false)
11008
+ * This prevents the angular compiler from correctly retrieving the binding expression.
11009
+ * To solve this problem, we introduce the `ngOpen` directive.
11010
+ *
11011
+ * @example
11012
+ <doc:example>
11013
+ <doc:source>
11014
+ Check me check multiple: <input type="checkbox" ng-model="open"><br/>
11015
+ <details id="details" ng-open="open">
11016
+ <summary>Show/Hide me</summary>
11017
+ </details>
11018
+ </doc:source>
11019
+ <doc:scenario>
11020
+ it('should toggle open', function() {
11021
+ expect(element('#details').prop('open')).toBeFalsy();
11022
+ input('open').check();
11023
+ expect(element('#details').prop('open')).toBeTruthy();
11024
+ });
11025
+ </doc:scenario>
11026
+ </doc:example>
11027
+ *
11028
+ * @element DETAILS
11029
+ * @param {string} expression Angular expression that will be evaluated.
11030
+ */
10703
11031
 
10704
11032
  var ngAttributeAliasDirectives = {};
10705
11033
 
@@ -10750,7 +11078,8 @@ var nullFormCtrl = {
10750
11078
  $addControl: noop,
10751
11079
  $removeControl: noop,
10752
11080
  $setValidity: noop,
10753
- $setDirty: noop
11081
+ $setDirty: noop,
11082
+ $setPristine: noop
10754
11083
  };
10755
11084
 
10756
11085
  /**
@@ -10782,7 +11111,8 @@ function FormController(element, attrs) {
10782
11111
  var form = this,
10783
11112
  parentForm = element.parent().controller('form') || nullFormCtrl,
10784
11113
  invalidCount = 0, // used to easily determine if we are valid
10785
- errors = form.$error = {};
11114
+ errors = form.$error = {},
11115
+ controls = [];
10786
11116
 
10787
11117
  // init state
10788
11118
  form.$name = attrs.name;
@@ -10806,6 +11136,8 @@ function FormController(element, attrs) {
10806
11136
  }
10807
11137
 
10808
11138
  form.$addControl = function(control) {
11139
+ controls.push(control);
11140
+
10809
11141
  if (control.$name && !form.hasOwnProperty(control.$name)) {
10810
11142
  form[control.$name] = control;
10811
11143
  }
@@ -10818,6 +11150,8 @@ function FormController(element, attrs) {
10818
11150
  forEach(errors, function(queue, validationToken) {
10819
11151
  form.$setValidity(validationToken, true, control);
10820
11152
  });
11153
+
11154
+ arrayRemove(controls, control);
10821
11155
  };
10822
11156
 
10823
11157
  form.$setValidity = function(validationToken, isValid, control) {
@@ -10865,6 +11199,29 @@ function FormController(element, attrs) {
10865
11199
  parentForm.$setDirty();
10866
11200
  };
10867
11201
 
11202
+ /**
11203
+ * @ngdoc function
11204
+ * @name ng.directive:form.FormController#$setPristine
11205
+ * @methodOf ng.directive:form.FormController
11206
+ *
11207
+ * @description
11208
+ * Sets the form to its pristine state.
11209
+ *
11210
+ * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
11211
+ * state (ng-pristine class). This method will also propagate to all the controls contained
11212
+ * in this form.
11213
+ *
11214
+ * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
11215
+ * saving or resetting it.
11216
+ */
11217
+ form.$setPristine = function () {
11218
+ element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
11219
+ form.$dirty = false;
11220
+ form.$pristine = true;
11221
+ forEach(controls, function(control) {
11222
+ control.$setPristine();
11223
+ });
11224
+ };
10868
11225
  }
10869
11226
 
10870
11227
 
@@ -11061,6 +11418,8 @@ var inputType = {
11061
11418
  * patterns defined as scope expressions.
11062
11419
  * @param {string=} ngChange Angular expression to be executed when input changes due to user
11063
11420
  * interaction with the input element.
11421
+ * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the
11422
+ * input.
11064
11423
  *
11065
11424
  * @example
11066
11425
  <doc:example>
@@ -11068,12 +11427,12 @@ var inputType = {
11068
11427
  <script>
11069
11428
  function Ctrl($scope) {
11070
11429
  $scope.text = 'guest';
11071
- $scope.word = /^\w*$/;
11430
+ $scope.word = /^\s*\w*\s*$/;
11072
11431
  }
11073
11432
  </script>
11074
11433
  <form name="myForm" ng-controller="Ctrl">
11075
11434
  Single word: <input type="text" name="input" ng-model="text"
11076
- ng-pattern="word" required>
11435
+ ng-pattern="word" required ng-trim="false">
11077
11436
  <span class="error" ng-show="myForm.input.$error.required">
11078
11437
  Required!</span>
11079
11438
  <span class="error" ng-show="myForm.input.$error.pattern">
@@ -11102,6 +11461,12 @@ var inputType = {
11102
11461
  input('text').enter('hello world');
11103
11462
  expect(binding('myForm.input.$valid')).toEqual('false');
11104
11463
  });
11464
+
11465
+ it('should not be trimmed', function() {
11466
+ input('text').enter('untrimmed ');
11467
+ expect(binding('text')).toEqual('untrimmed ');
11468
+ expect(binding('myForm.input.$valid')).toEqual('true');
11469
+ });
11105
11470
  </doc:scenario>
11106
11471
  </doc:example>
11107
11472
  */
@@ -11415,7 +11780,14 @@ function isEmpty(value) {
11415
11780
  function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11416
11781
 
11417
11782
  var listener = function() {
11418
- var value = trim(element.val());
11783
+ var value = element.val();
11784
+
11785
+ // By default we will trim the value
11786
+ // If the attribute ng-trim exists we will avoid trimming
11787
+ // e.g. <input ng-model="foo" ng-trim="false">
11788
+ if (toBoolean(attr.ngTrim || 'T')) {
11789
+ value = trim(value);
11790
+ }
11419
11791
 
11420
11792
  if (ctrl.$viewValue !== value) {
11421
11793
  scope.$apply(function() {
@@ -11996,6 +12368,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
11996
12368
  parentForm.$setValidity(validationErrorKey, isValid, this);
11997
12369
  };
11998
12370
 
12371
+ /**
12372
+ * @ngdoc function
12373
+ * @name ng.directive:ngModel.NgModelController#$setPristine
12374
+ * @methodOf ng.directive:ngModel.NgModelController
12375
+ *
12376
+ * @description
12377
+ * Sets the control to its pristine state.
12378
+ *
12379
+ * This method can be called to remove the 'ng-dirty' class and set the control to its pristine
12380
+ * state (ng-pristine class).
12381
+ */
12382
+ this.$setPristine = function () {
12383
+ this.$dirty = false;
12384
+ this.$pristine = true;
12385
+ $element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
12386
+ };
11999
12387
 
12000
12388
  /**
12001
12389
  * @ngdoc function
@@ -12868,7 +13256,7 @@ var ngCspDirective = ['$sniffer', function($sniffer) {
12868
13256
  */
12869
13257
  var ngEventDirectives = {};
12870
13258
  forEach(
12871
- 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
13259
+ 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup'.split(' '),
12872
13260
  function(name) {
12873
13261
  var directiveName = directiveNormalize('ng-' + name);
12874
13262
  ngEventDirectives[directiveName] = ['$parse', function($parse) {
@@ -12995,6 +13383,38 @@ forEach(
12995
13383
  */
12996
13384
 
12997
13385
 
13386
+ /**
13387
+ * @ngdoc directive
13388
+ * @name ng.directive:ngKeydown
13389
+ *
13390
+ * @description
13391
+ * Specify custom behavior on keydown event.
13392
+ *
13393
+ * @element ANY
13394
+ * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
13395
+ * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
13396
+ *
13397
+ * @example
13398
+ * See {@link ng.directive:ngClick ngClick}
13399
+ */
13400
+
13401
+
13402
+ /**
13403
+ * @ngdoc directive
13404
+ * @name ng.directive:ngKeyup
13405
+ *
13406
+ * @description
13407
+ * Specify custom behavior on keyup event.
13408
+ *
13409
+ * @element ANY
13410
+ * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
13411
+ * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
13412
+ *
13413
+ * @example
13414
+ * See {@link ng.directive:ngClick ngClick}
13415
+ */
13416
+
13417
+
12998
13418
  /**
12999
13419
  * @ngdoc directive
13000
13420
  * @name ng.directive:ngSubmit
@@ -13789,8 +14209,11 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
13789
14209
  * On child elments add:
13790
14210
  *
13791
14211
  * * `ngSwitchWhen`: the case statement to match against. If match then this
13792
- * case will be displayed.
13793
- * * `ngSwitchDefault`: the default case when no other casses match.
14212
+ * case will be displayed. If the same match appears multiple times, all the
14213
+ * elements will be displayed.
14214
+ * * `ngSwitchDefault`: the default case when no other case match. If there
14215
+ * are multiple default cases, all of them will be displayed when no other
14216
+ * case match.
13794
14217
  *
13795
14218
  * @example
13796
14219
  <doc:example>
@@ -13838,22 +14261,28 @@ var ngSwitchDirective = valueFn({
13838
14261
  }],
13839
14262
  link: function(scope, element, attr, ctrl) {
13840
14263
  var watchExpr = attr.ngSwitch || attr.on,
13841
- selectedTransclude,
13842
- selectedElement,
13843
- selectedScope;
14264
+ selectedTranscludes,
14265
+ selectedElements,
14266
+ selectedScopes = [];
13844
14267
 
13845
14268
  scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
13846
- if (selectedElement) {
13847
- selectedScope.$destroy();
13848
- selectedElement.remove();
13849
- selectedElement = selectedScope = null;
14269
+ for (var i= 0, ii=selectedScopes.length; i<ii; i++) {
14270
+ selectedScopes[i].$destroy();
14271
+ selectedElements[i].remove();
13850
14272
  }
13851
- if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
14273
+
14274
+ selectedElements = [];
14275
+ selectedScopes = [];
14276
+
14277
+ if ((selectedTranscludes = ctrl.cases['!' + value] || ctrl.cases['?'])) {
13852
14278
  scope.$eval(attr.change);
13853
- selectedScope = scope.$new();
13854
- selectedTransclude(selectedScope, function(caseElement) {
13855
- selectedElement = caseElement;
13856
- element.append(caseElement);
14279
+ forEach(selectedTranscludes, function(selectedTransclude) {
14280
+ var selectedScope = scope.$new();
14281
+ selectedScopes.push(selectedScope);
14282
+ selectedTransclude(selectedScope, function(caseElement) {
14283
+ selectedElements.push(caseElement);
14284
+ element.append(caseElement);
14285
+ });
13857
14286
  });
13858
14287
  }
13859
14288
  });
@@ -13866,7 +14295,8 @@ var ngSwitchWhenDirective = ngDirective({
13866
14295
  require: '^ngSwitch',
13867
14296
  compile: function(element, attrs, transclude) {
13868
14297
  return function(scope, element, attr, ctrl) {
13869
- ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
14298
+ ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
14299
+ ctrl.cases['!' + attrs.ngSwitchWhen].push(transclude);
13870
14300
  };
13871
14301
  }
13872
14302
  });
@@ -13877,7 +14307,8 @@ var ngSwitchDefaultDirective = ngDirective({
13877
14307
  require: '^ngSwitch',
13878
14308
  compile: function(element, attrs, transclude) {
13879
14309
  return function(scope, element, attr, ctrl) {
13880
- ctrl.cases['?'] = transclude;
14310
+ ctrl.cases['?'] = (ctrl.cases['?'] || []);
14311
+ ctrl.cases['?'].push(transclude);
13881
14312
  };
13882
14313
  }
13883
14314
  });