mongo_browser 0.2.0.rc2 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +1 -1
  3. data/.travis.yml +6 -1
  4. data/CHANGELOG.md +15 -0
  5. data/{grunt.js → Gruntfile.js} +10 -8
  6. data/Procfile +1 -0
  7. data/README.md +44 -2
  8. data/Rakefile +1 -12
  9. data/app/assets/images/ajax-loader.gif +0 -0
  10. data/app/assets/javascripts/app/controllers/{breadcrumbs.js.coffee → breadcrumbs_controller.js.coffee} +4 -7
  11. data/app/assets/javascripts/app/controllers/collections/index_controller.js.coffee +38 -0
  12. data/app/assets/javascripts/app/controllers/collections/stats_controller.js.coffee +17 -0
  13. data/app/assets/javascripts/app/controllers/{databases.js.coffee → databases/index_controller.js.coffee} +11 -15
  14. data/app/assets/javascripts/app/controllers/databases/stats_controller.js.coffee +15 -0
  15. data/app/assets/javascripts/app/controllers/documents/index_controller.js.coffee +54 -0
  16. data/app/assets/javascripts/app/controllers/documents/show_controller.js.coffee +38 -0
  17. data/app/assets/javascripts/app/controllers/main_controller.js.coffee +15 -0
  18. data/app/assets/javascripts/app/controllers/servers/show_controller.js.coffee +17 -0
  19. data/app/assets/javascripts/app/directives.js.coffee +23 -0
  20. data/app/assets/javascripts/app/filters.js.coffee +14 -1
  21. data/app/assets/javascripts/app/modules/alerts.js.coffee +58 -0
  22. data/app/assets/javascripts/app/modules/pager.js.coffee +2 -2
  23. data/app/assets/javascripts/app/modules/spinner.js.coffee +29 -0
  24. data/app/assets/javascripts/app/modules/table_filter.js.coffee +4 -4
  25. data/app/assets/javascripts/app/resources.js.coffee +14 -8
  26. data/app/assets/javascripts/app/services.js.coffee +11 -33
  27. data/app/assets/javascripts/application.js.coffee +62 -34
  28. data/app/assets/javascripts/application.test.js.coffee +5 -0
  29. data/app/assets/javascripts/compiled_templates.js.coffee +1 -0
  30. data/app/assets/javascripts/vendor.js.coffee +0 -1
  31. data/app/assets/stylesheets/application.css.scss +36 -18
  32. data/{public/index.html → app/views/index.erb} +8 -8
  33. data/bin/mongo_browser +2 -13
  34. data/config.ru +3 -1
  35. data/lib/mongo_browser.rb +1 -0
  36. data/lib/mongo_browser/api.rb +11 -0
  37. data/lib/mongo_browser/api/collections.rb +34 -0
  38. data/lib/mongo_browser/api/databases.rb +32 -0
  39. data/lib/mongo_browser/api/documents.rb +37 -0
  40. data/lib/mongo_browser/api/mongo.rb +41 -0
  41. data/lib/mongo_browser/application.rb +8 -174
  42. data/lib/mongo_browser/application/development.rb +32 -0
  43. data/lib/mongo_browser/cli.rb +48 -0
  44. data/lib/mongo_browser/entities.rb +43 -0
  45. data/lib/mongo_browser/models/collection.rb +7 -12
  46. data/lib/mongo_browser/models/document.rb +5 -1
  47. data/lib/mongo_browser/models/pager.rb +22 -9
  48. data/lib/mongo_browser/version.rb +1 -1
  49. data/mongo_browser.gemspec +22 -15
  50. data/package.json +30 -0
  51. data/public/ng/templates/alerts.html +6 -0
  52. data/public/ng/templates/collections/index.html +39 -0
  53. data/public/ng/templates/collections/stats.html +18 -0
  54. data/public/ng/templates/databases/index.html +35 -0
  55. data/public/ng/templates/databases/stats.html +18 -0
  56. data/public/ng/templates/documents/index.html +40 -0
  57. data/public/ng/templates/documents/show.html +17 -0
  58. data/{app/assets → public/ng}/templates/pager.html +0 -0
  59. data/{app/assets/templates/server_info.html → public/ng/templates/server/show.html} +1 -1
  60. data/{app/assets → public/ng}/templates/table_filter.html +0 -0
  61. data/script/ci_all +5 -1
  62. data/script/ci_e2e +5 -2
  63. data/script/ci_javascripts +1 -1
  64. data/script/ci_rspec +1 -1
  65. data/spec/javascripts/app/controllers/{breadcrumbs_spec.js.coffee → breadcrumbs_controller_spec.js.coffee} +1 -1
  66. data/spec/javascripts/app/controllers/collections/index_controller_spec.js.coffee +95 -0
  67. data/spec/javascripts/app/controllers/collections/stats_controller_spec.js.coffee +34 -0
  68. data/spec/javascripts/app/controllers/databases/index_controller_spec.js.coffee +93 -0
  69. data/spec/javascripts/app/controllers/databases/stats_controller_spec.js.coffee +30 -0
  70. data/spec/javascripts/app/controllers/documents/index_controller_spec.js.coffee +108 -0
  71. data/spec/javascripts/app/controllers/documents/show_controller_spec.js.coffee +94 -0
  72. data/spec/javascripts/app/controllers/{main_spec.js.coffee → main_controller_spec.js.coffee} +2 -2
  73. data/spec/javascripts/app/controllers/{server_info_spec.js.coffee → server/show_controller_spec.js.coffee} +5 -6
  74. data/spec/javascripts/app/directives_spec.js.coffee +108 -24
  75. data/spec/javascripts/app/filters_spec.js.coffee +31 -5
  76. data/spec/javascripts/app/modules/alerts_spec.js.coffee +138 -0
  77. data/spec/javascripts/app/modules/dialogs_spec.js.coffee +1 -2
  78. data/spec/javascripts/app/modules/pager_spec.js.coffee +0 -1
  79. data/spec/javascripts/app/modules/spinner_spec.js.coffee +65 -0
  80. data/spec/javascripts/app/modules/table_filter_spec.js.coffee +9 -9
  81. data/spec/javascripts/app/resources_spec.js.coffee +99 -0
  82. data/spec/javascripts/app/services_spec.js.coffee +31 -71
  83. data/spec/javascripts/config/{testacular-e2e.conf.js → karma-e2e.conf.js} +1 -1
  84. data/spec/javascripts/config/{testacular.conf.js → karma.conf.js} +2 -3
  85. data/spec/javascripts/e2e/collection_stats_scenario.js.coffee +12 -0
  86. data/spec/javascripts/e2e/collections_scenario.js.coffee +59 -20
  87. data/spec/javascripts/e2e/database_stats_scenario.js.coffee +11 -0
  88. data/spec/javascripts/e2e/databases_scenario.js.coffee +37 -36
  89. data/spec/javascripts/e2e/document_show_scenario.js.coffee +31 -0
  90. data/spec/javascripts/e2e/documents_pagination_scenario.js.coffee +33 -0
  91. data/spec/javascripts/e2e/documents_scenario.js.coffee +43 -4
  92. data/spec/javascripts/e2e/server_info_scenario.js.coffee +8 -2
  93. data/spec/javascripts/helpers/mocks.js.coffee +2 -0
  94. data/spec/javascripts/helpers_e2e/dsl.js.coffee +20 -0
  95. data/spec/javascripts/lib/angular-mocks.js +64 -16
  96. data/spec/javascripts/lib/angular-scenario.js +724 -561
  97. data/spec/javascripts/runner.html +5 -5
  98. data/spec/lib/api/collections_spec.rb +62 -0
  99. data/spec/lib/api/databases_spec.rb +58 -0
  100. data/spec/lib/api/documents_spec.rb +135 -0
  101. data/spec/lib/api/mongo_spec.rb +27 -0
  102. data/spec/lib/cli_spec.rb +19 -0
  103. data/spec/lib/entities_spec.rb +39 -0
  104. data/spec/lib/models/collection_spec.rb +16 -10
  105. data/spec/lib/models/database_spec.rb +4 -4
  106. data/spec/lib/models/document_spec.rb +5 -5
  107. data/spec/lib/models/pager_spec.rb +20 -11
  108. data/spec/spec_helper.rb +7 -15
  109. data/spec/support/api_example_group.rb +45 -0
  110. data/spec/support/fixtures.rb +10 -6
  111. data/spec/support/matchers/expose.rb +18 -0
  112. data/vendor/assets/javascripts/angular/angular-bootstrap.js +1 -1
  113. data/vendor/assets/javascripts/angular/angular-resource.js +78 -56
  114. data/vendor/assets/javascripts/angular/angular-sanitize.js +3 -1
  115. data/vendor/assets/javascripts/angular/angular.js +720 -404
  116. metadata +323 -183
  117. data/app/assets/javascripts/app.js.coffee +0 -8
  118. data/app/assets/javascripts/app/controllers.js.coffee +0 -2
  119. data/app/assets/javascripts/app/controllers/alerts.js.coffee +0 -12
  120. data/app/assets/javascripts/app/controllers/collections.js.coffee +0 -40
  121. data/app/assets/javascripts/app/controllers/documents.js.coffee +0 -49
  122. data/app/assets/javascripts/app/controllers/main.js.coffee +0 -10
  123. data/app/assets/javascripts/app/controllers/server_info.js.coffee +0 -14
  124. data/app/assets/javascripts/templates.js.coffee +0 -1
  125. data/app/assets/javascripts/templates/.gitkeep +0 -0
  126. data/app/assets/templates/collections.html +0 -53
  127. data/app/assets/templates/databases.html +0 -32
  128. data/app/assets/templates/documents.html +0 -45
  129. data/config-e2e.ru +0 -20
  130. data/spec/features/collections_list_spec.rb +0 -64
  131. data/spec/features/documents_list_spec.rb +0 -139
  132. data/spec/features/server_info_spec.rb +0 -23
  133. data/spec/javascripts/app/controllers/alerts_spec.js.coffee +0 -36
  134. data/spec/javascripts/app/controllers/collections_spec.js.coffee +0 -78
  135. data/spec/javascripts/app/controllers/databases_spec.js.coffee +0 -55
  136. data/spec/javascripts/app/controllers/documents_spec.js.coffee +0 -62
  137. data/spec/javascripts/helpers_e2e/app_element.js.coffee +0 -6
  138. data/spec/support/feature_example_group.rb +0 -53
  139. data/spec/support/matchers/have_flash_message.rb +0 -16
  140. data/spec/support/mongod.rb +0 -91
  141. data/spec/support/mongodb.conf +0 -47
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.3
2
+ * @license AngularJS v1.0.7
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -34,12 +34,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
34
34
 
35
35
  var manualLowercase = function(s) {
36
36
  return isString(s)
37
- ? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
37
+ ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
38
38
  : s;
39
39
  };
40
40
  var manualUppercase = function(s) {
41
41
  return isString(s)
42
- ? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
42
+ ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
43
43
  : s;
44
44
  };
45
45
 
@@ -52,11 +52,8 @@ if ('i' !== 'I'.toLowerCase()) {
52
52
  uppercase = manualUppercase;
53
53
  }
54
54
 
55
- function fromCharCode(code) {return String.fromCharCode(code);}
56
55
 
57
-
58
- var Error = window.Error,
59
- /** holds major version number for IE or NaN for real browsers */
56
+ var /** holds major version number for IE or NaN for real browsers */
60
57
  msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
61
58
  jqLite, // delay binding since jQuery could be loaded after us.
62
59
  jQuery, // delay binding
@@ -70,6 +67,29 @@ var Error = window.Error,
70
67
  nodeName_,
71
68
  uid = ['0', '0', '0'];
72
69
 
70
+
71
+ /**
72
+ * @private
73
+ * @param {*} obj
74
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
75
+ */
76
+ function isArrayLike(obj) {
77
+ if (!obj || (typeof obj.length !== 'number')) return false;
78
+
79
+ // We have on object which has length property. Should we treat it as array?
80
+ if (typeof obj.hasOwnProperty != 'function' &&
81
+ typeof obj.constructor != 'function') {
82
+ // This is here for IE8: it is a bogus object treat it as array;
83
+ return true;
84
+ } else {
85
+ return obj instanceof JQLite || // JQLite
86
+ (jQuery && obj instanceof jQuery) || // jQuery
87
+ toString.call(obj) !== '[object Object]' || // some browser native object
88
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
89
+ }
90
+ }
91
+
92
+
73
93
  /**
74
94
  * @ngdoc function
75
95
  * @name angular.forEach
@@ -108,7 +128,7 @@ function forEach(obj, iterator, context) {
108
128
  }
109
129
  } else if (obj.forEach && obj.forEach !== forEach) {
110
130
  obj.forEach(iterator, context);
111
- } else if (isObject(obj) && isNumber(obj.length)) {
131
+ } else if (isArrayLike(obj)) {
112
132
  for (key = 0; key < obj.length; key++)
113
133
  iterator.call(context, obj[key], key);
114
134
  } else {
@@ -153,7 +173,7 @@ function reverseParams(iteratorFn) {
153
173
  /**
154
174
  * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
155
175
  * characters such as '012ABC'. The reason why we are not using simply a number counter is that
156
- * the number string gets longer over time, and it can also overflow, where as the the nextId
176
+ * the number string gets longer over time, and it can also overflow, where as the nextId
157
177
  * will grow much slower, it is a string, and it will never overflow.
158
178
  *
159
179
  * @returns an unique alpha-numeric string
@@ -180,6 +200,21 @@ function nextUid() {
180
200
  return uid.join('');
181
201
  }
182
202
 
203
+
204
+ /**
205
+ * Set or clear the hashkey for an object.
206
+ * @param obj object
207
+ * @param h the hashkey (!truthy to delete the hashkey)
208
+ */
209
+ function setHashKey(obj, h) {
210
+ if (h) {
211
+ obj.$$hashKey = h;
212
+ }
213
+ else {
214
+ delete obj.$$hashKey;
215
+ }
216
+ }
217
+
183
218
  /**
184
219
  * @ngdoc function
185
220
  * @name angular.extend
@@ -191,8 +226,10 @@ function nextUid() {
191
226
  *
192
227
  * @param {Object} dst Destination object.
193
228
  * @param {...Object} src Source object(s).
229
+ * @returns {Object} Reference to `dst`.
194
230
  */
195
231
  function extend(dst) {
232
+ var h = dst.$$hashKey;
196
233
  forEach(arguments, function(obj){
197
234
  if (obj !== dst) {
198
235
  forEach(obj, function(value, key){
@@ -200,6 +237,8 @@ function extend(dst) {
200
237
  });
201
238
  }
202
239
  });
240
+
241
+ setHashKey(dst,h);
203
242
  return dst;
204
243
  }
205
244
 
@@ -549,19 +588,19 @@ function copy(source, destination){
549
588
  } else {
550
589
  if (source === destination) throw Error("Can't copy equivalent objects or arrays");
551
590
  if (isArray(source)) {
552
- while(destination.length) {
553
- destination.pop();
554
- }
591
+ destination.length = 0;
555
592
  for ( var i = 0; i < source.length; i++) {
556
593
  destination.push(copy(source[i]));
557
594
  }
558
595
  } else {
596
+ var h = destination.$$hashKey;
559
597
  forEach(destination, function(value, key){
560
598
  delete destination[key];
561
599
  });
562
600
  for ( var key in source) {
563
601
  destination[key] = copy(source[key]);
564
602
  }
603
+ setHashKey(destination,h);
565
604
  }
566
605
  }
567
606
  return destination;
@@ -601,7 +640,7 @@ function shallowCopy(src, dst) {
601
640
  * During a property comparision, properties of `function` type and properties with names
602
641
  * that begin with `$` are ignored.
603
642
  *
604
- * Scope and DOMWindow objects are being compared only be identify (`===`).
643
+ * Scope and DOMWindow objects are being compared only by identify (`===`).
605
644
  *
606
645
  * @param {*} o1 Object or value to compare.
607
646
  * @param {*} o2 Object or value to compare.
@@ -627,13 +666,15 @@ function equals(o1, o2) {
627
666
  if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
628
667
  keySet = {};
629
668
  for(key in o1) {
630
- if (key.charAt(0) !== '$' && !isFunction(o1[key]) && !equals(o1[key], o2[key])) {
631
- return false;
632
- }
669
+ if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
670
+ if (!equals(o1[key], o2[key])) return false;
633
671
  keySet[key] = true;
634
672
  }
635
673
  for(key in o2) {
636
- if (!keySet[key] && key.charAt(0) !== '$' && !isFunction(o2[key])) return false;
674
+ if (!keySet[key] &&
675
+ key.charAt(0) !== '$' &&
676
+ o2[key] !== undefined &&
677
+ !isFunction(o2[key])) return false;
637
678
  }
638
679
  return true;
639
680
  }
@@ -659,7 +700,7 @@ function sliceArgs(args, startIndex) {
659
700
  *
660
701
  * @description
661
702
  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
662
- * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
703
+ * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
663
704
  * known as [function currying](http://en.wikipedia.org/wiki/Currying).
664
705
  *
665
706
  * @param {Object} self Context which `fn` should be evaluated in.
@@ -760,9 +801,18 @@ function startingTag(element) {
760
801
  // are not allowed to have children. So we just ignore it.
761
802
  element.html('');
762
803
  } catch(e) {}
763
- return jqLite('<div>').append(element).html().
764
- match(/^(<[^>]+>)/)[1].
765
- replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
804
+ // As Per DOM Standards
805
+ var TEXT_NODE = 3;
806
+ var elemHtml = jqLite('<div>').append(element).html();
807
+ try {
808
+ return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
809
+ elemHtml.
810
+ match(/^(<[^>]+>)/)[1].
811
+ replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
812
+ } catch(e) {
813
+ return lowercase(elemHtml);
814
+ }
815
+
766
816
  }
767
817
 
768
818
 
@@ -829,7 +879,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
829
879
  replace(/%3A/gi, ':').
830
880
  replace(/%24/g, '$').
831
881
  replace(/%2C/gi, ',').
832
- replace((pctEncodeSpaces ? null : /%20/g), '+');
882
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
833
883
  }
834
884
 
835
885
 
@@ -843,10 +893,10 @@ function encodeUriQuery(val, pctEncodeSpaces) {
843
893
  *
844
894
  * @description
845
895
  *
846
- * Use this directive to auto-bootstrap on application. Only
896
+ * Use this directive to auto-bootstrap an application. Only
847
897
  * one directive can be used per HTML document. The directive
848
898
  * designates the root of the application and is typically placed
849
- * ot the root of the page.
899
+ * at the root of the page.
850
900
  *
851
901
  * In the example below if the `ngApp` directive would not be placed
852
902
  * on the `html` element then the document would not be compiled
@@ -918,22 +968,38 @@ function angularInit(element, bootstrap) {
918
968
  * @returns {AUTO.$injector} Returns the newly created injector for this app.
919
969
  */
920
970
  function bootstrap(element, modules) {
921
- element = jqLite(element);
922
- modules = modules || [];
923
- modules.unshift(['$provide', function($provide) {
924
- $provide.value('$rootElement', element);
925
- }]);
926
- modules.unshift('ng');
927
- var injector = createInjector(modules);
928
- injector.invoke(
929
- ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
930
- scope.$apply(function() {
931
- element.data('$injector', injector);
932
- compile(element)(scope);
933
- });
934
- }]
935
- );
936
- return injector;
971
+ var resumeBootstrapInternal = function() {
972
+ element = jqLite(element);
973
+ modules = modules || [];
974
+ modules.unshift(['$provide', function($provide) {
975
+ $provide.value('$rootElement', element);
976
+ }]);
977
+ modules.unshift('ng');
978
+ var injector = createInjector(modules);
979
+ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
980
+ function(scope, element, compile, injector) {
981
+ scope.$apply(function() {
982
+ element.data('$injector', injector);
983
+ compile(element)(scope);
984
+ });
985
+ }]
986
+ );
987
+ return injector;
988
+ };
989
+
990
+ var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
991
+
992
+ if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
993
+ return resumeBootstrapInternal();
994
+ }
995
+
996
+ window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
997
+ angular.resumeBootstrap = function(extraModules) {
998
+ forEach(extraModules, function(module) {
999
+ modules.push(module);
1000
+ });
1001
+ resumeBootstrapInternal();
1002
+ };
937
1003
  }
938
1004
 
939
1005
  var SNAKE_CASE_REGEXP = /[A-Z]/g;
@@ -966,7 +1032,7 @@ function bindJQuery() {
966
1032
  }
967
1033
 
968
1034
  /**
969
- * throw error of the argument is falsy.
1035
+ * throw error if the argument is falsy.
970
1036
  */
971
1037
  function assertArg(arg, name, reason) {
972
1038
  if (!arg) {
@@ -1247,11 +1313,11 @@ function setupModuleLoader(window) {
1247
1313
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
1248
1314
  */
1249
1315
  var version = {
1250
- full: '1.0.3', // all of these placeholder strings will be replaced by rake's
1251
- major: 1, // compile task
1316
+ full: '1.0.7', // all of these placeholder strings will be replaced by grunt's
1317
+ major: 1, // package task
1252
1318
  minor: 0,
1253
- dot: 3,
1254
- codeName: 'bouncy-thunder'
1319
+ dot: 7,
1320
+ codeName: 'monochromatic-rainbow'
1255
1321
  };
1256
1322
 
1257
1323
 
@@ -1396,18 +1462,18 @@ function publishExternalAPI(angular){
1396
1462
  * - [after()](http://api.jquery.com/after/)
1397
1463
  * - [append()](http://api.jquery.com/append/)
1398
1464
  * - [attr()](http://api.jquery.com/attr/)
1399
- * - [bind()](http://api.jquery.com/bind/)
1400
- * - [children()](http://api.jquery.com/children/)
1465
+ * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
1466
+ * - [children()](http://api.jquery.com/children/) - Does not support selectors
1401
1467
  * - [clone()](http://api.jquery.com/clone/)
1402
1468
  * - [contents()](http://api.jquery.com/contents/)
1403
1469
  * - [css()](http://api.jquery.com/css/)
1404
1470
  * - [data()](http://api.jquery.com/data/)
1405
1471
  * - [eq()](http://api.jquery.com/eq/)
1406
- * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
1472
+ * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
1407
1473
  * - [hasClass()](http://api.jquery.com/hasClass/)
1408
1474
  * - [html()](http://api.jquery.com/html/)
1409
- * - [next()](http://api.jquery.com/next/)
1410
- * - [parent()](http://api.jquery.com/parent/)
1475
+ * - [next()](http://api.jquery.com/next/) - Does not support selectors
1476
+ * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
1411
1477
  * - [prepend()](http://api.jquery.com/prepend/)
1412
1478
  * - [prop()](http://api.jquery.com/prop/)
1413
1479
  * - [ready()](http://api.jquery.com/ready/)
@@ -1419,11 +1485,11 @@ function publishExternalAPI(angular){
1419
1485
  * - [text()](http://api.jquery.com/text/)
1420
1486
  * - [toggleClass()](http://api.jquery.com/toggleClass/)
1421
1487
  * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
1422
- * - [unbind()](http://api.jquery.com/unbind/)
1488
+ * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
1423
1489
  * - [val()](http://api.jquery.com/val/)
1424
1490
  * - [wrap()](http://api.jquery.com/wrap/)
1425
1491
  *
1426
- * ## In addtion to the above, Angular privides an additional method to both jQuery and jQuery lite:
1492
+ * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
1427
1493
  *
1428
1494
  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
1429
1495
  * retrieves controller associated with the `ngController` directive. If `name` is provided as
@@ -1966,23 +2032,43 @@ forEach({
1966
2032
 
1967
2033
  if (!eventFns) {
1968
2034
  if (type == 'mouseenter' || type == 'mouseleave') {
1969
- var counter = 0;
2035
+ var contains = document.body.contains || document.body.compareDocumentPosition ?
2036
+ function( a, b ) {
2037
+ var adown = a.nodeType === 9 ? a.documentElement : a,
2038
+ bup = b && b.parentNode;
2039
+ return a === bup || !!( bup && bup.nodeType === 1 && (
2040
+ adown.contains ?
2041
+ adown.contains( bup ) :
2042
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
2043
+ ));
2044
+ } :
2045
+ function( a, b ) {
2046
+ if ( b ) {
2047
+ while ( (b = b.parentNode) ) {
2048
+ if ( b === a ) {
2049
+ return true;
2050
+ }
2051
+ }
2052
+ }
2053
+ return false;
2054
+ };
1970
2055
 
1971
- events.mouseenter = [];
1972
- events.mouseleave = [];
2056
+ events[type] = [];
2057
+
2058
+ // Refer to jQuery's implementation of mouseenter & mouseleave
2059
+ // Read about mouseenter and mouseleave:
2060
+ // http://www.quirksmode.org/js/events_mouse.html#link8
2061
+ var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
2062
+ bindFn(element, eventmap[type], function(event) {
2063
+ var ret, target = this, related = event.relatedTarget;
2064
+ // For mousenter/leave call the handler if related is outside the target.
2065
+ // NB: No relatedTarget if the mouse left/entered the browser window
2066
+ if ( !related || (related !== target && !contains(target, related)) ){
2067
+ handle(event, type);
2068
+ }
1973
2069
 
1974
- bindFn(element, 'mouseover', function(event) {
1975
- counter++;
1976
- if (counter == 1) {
1977
- handle(event, 'mouseenter');
1978
- }
1979
- });
1980
- bindFn(element, 'mouseout', function(event) {
1981
- counter --;
1982
- if (counter == 0) {
1983
- handle(event, 'mouseleave');
1984
- }
1985
2070
  });
2071
+
1986
2072
  } else {
1987
2073
  addEventListenerFn(element, type, handle);
1988
2074
  events[type] = [];
@@ -2011,14 +2097,14 @@ forEach({
2011
2097
  children: function(element) {
2012
2098
  var children = [];
2013
2099
  forEach(element.childNodes, function(element){
2014
- if (element.nodeName != '#text')
2100
+ if (element.nodeType === 1)
2015
2101
  children.push(element);
2016
2102
  });
2017
2103
  return children;
2018
2104
  },
2019
2105
 
2020
2106
  contents: function(element) {
2021
- return element.childNodes;
2107
+ return element.childNodes || [];
2022
2108
  },
2023
2109
 
2024
2110
  append: function(element, node) {
@@ -2081,7 +2167,16 @@ forEach({
2081
2167
  },
2082
2168
 
2083
2169
  next: function(element) {
2084
- return element.nextSibling;
2170
+ if (element.nextElementSibling) {
2171
+ return element.nextElementSibling;
2172
+ }
2173
+
2174
+ // IE8 doesn't have nextElementSibling
2175
+ var elm = element.nextSibling;
2176
+ while (elm != null && elm.nodeType !== 1) {
2177
+ elm = elm.nextSibling;
2178
+ }
2179
+ return elm;
2085
2180
  },
2086
2181
 
2087
2182
  find: function(element, selector) {
@@ -2289,7 +2384,7 @@ function annotate(fn) {
2289
2384
  }
2290
2385
  } else if (isArray(fn)) {
2291
2386
  last = fn.length - 1;
2292
- assertArgFn(fn[last], 'fn')
2387
+ assertArgFn(fn[last], 'fn');
2293
2388
  $inject = fn.slice(0, last);
2294
2389
  } else {
2295
2390
  assertArgFn(fn, 'fn', true);
@@ -2323,19 +2418,19 @@ function annotate(fn) {
2323
2418
  * # Injection Function Annotation
2324
2419
  *
2325
2420
  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
2326
- * following ways are all valid way of annotating function with injection arguments and are equivalent.
2421
+ * following are all valid ways of annotating function with injection arguments and are equivalent.
2327
2422
  *
2328
2423
  * <pre>
2329
2424
  * // inferred (only works if code not minified/obfuscated)
2330
- * $inject.invoke(function(serviceA){});
2425
+ * $injector.invoke(function(serviceA){});
2331
2426
  *
2332
2427
  * // annotated
2333
2428
  * function explicit(serviceA) {};
2334
2429
  * explicit.$inject = ['serviceA'];
2335
- * $inject.invoke(explicit);
2430
+ * $injector.invoke(explicit);
2336
2431
  *
2337
2432
  * // inline
2338
- * $inject.invoke(['serviceA', function(serviceA){}]);
2433
+ * $injector.invoke(['serviceA', function(serviceA){}]);
2339
2434
  * </pre>
2340
2435
  *
2341
2436
  * ## Inference
@@ -2419,7 +2514,7 @@ function annotate(fn) {
2419
2514
  * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
2420
2515
  * are supported.
2421
2516
  *
2422
- * # The `$injector` property
2517
+ * # The `$inject` property
2423
2518
  *
2424
2519
  * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
2425
2520
  * services to be injected into the function.
@@ -2452,7 +2547,7 @@ function annotate(fn) {
2452
2547
  * // ...
2453
2548
  * };
2454
2549
  * tmpFn.$inject = ['$compile', '$rootScope'];
2455
- * injector.invoke(tempFn);
2550
+ * injector.invoke(tmpFn);
2456
2551
  *
2457
2552
  * // To better support inline function the inline annotation is supported
2458
2553
  * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
@@ -2481,7 +2576,7 @@ function annotate(fn) {
2481
2576
  * @description
2482
2577
  *
2483
2578
  * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
2484
- * The providers share the same name as the instance they create with the `Provider` suffixed to them.
2579
+ * The providers share the same name as the instance they create with `Provider` suffixed to them.
2485
2580
  *
2486
2581
  * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
2487
2582
  * a service. The Provider can have additional methods which would allow for configuration of the provider.
@@ -2505,7 +2600,7 @@ function annotate(fn) {
2505
2600
  *
2506
2601
  * beforeEach(module(function($provide) {
2507
2602
  * $provide.provider('greet', GreetProvider);
2508
- * });
2603
+ * }));
2509
2604
  *
2510
2605
  * it('should greet', inject(function(greet) {
2511
2606
  * expect(greet('angular')).toEqual('Hello angular!');
@@ -2518,9 +2613,7 @@ function annotate(fn) {
2518
2613
  * inject(function(greet) {
2519
2614
  * expect(greet('angular')).toEqual('Ahoj angular!');
2520
2615
  * });
2521
- * )};
2522
- *
2523
- * });
2616
+ * });
2524
2617
  * </pre>
2525
2618
  */
2526
2619
 
@@ -2614,7 +2707,7 @@ function annotate(fn) {
2614
2707
  *
2615
2708
  * @param {string} name The name of the service to decorate.
2616
2709
  * @param {function()} decorator This function will be invoked when the service needs to be
2617
- * instanciated. The function is called using the {@link AUTO.$injector#invoke
2710
+ * instantiated. The function is called using the {@link AUTO.$injector#invoke
2618
2711
  * injector.invoke} method and is therefore fully injectable. Local injection arguments:
2619
2712
  *
2620
2713
  * * `$delegate` - The original service instance, which can be monkey patched, configured,
@@ -2667,7 +2760,7 @@ function createInjector(modulesToLoad) {
2667
2760
  }
2668
2761
 
2669
2762
  function provider(name, provider_) {
2670
- if (isFunction(provider_)) {
2763
+ if (isFunction(provider_) || isArray(provider_)) {
2671
2764
  provider_ = providerInjector.instantiate(provider_);
2672
2765
  }
2673
2766
  if (!provider_.$get) {
@@ -2784,7 +2877,7 @@ function createInjector(modulesToLoad) {
2784
2877
  args.push(
2785
2878
  locals && locals.hasOwnProperty(key)
2786
2879
  ? locals[key]
2787
- : getService(key, path)
2880
+ : getService(key)
2788
2881
  );
2789
2882
  }
2790
2883
  if (!fn.$inject) {
@@ -2814,6 +2907,8 @@ function createInjector(modulesToLoad) {
2814
2907
  var Constructor = function() {},
2815
2908
  instance, returnedValue;
2816
2909
 
2910
+ // Check if Type is annotated and use just the given function at n-1 as parameter
2911
+ // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
2817
2912
  Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
2818
2913
  instance = new Constructor();
2819
2914
  returnedValue = invoke(Type, instance, locals);
@@ -2829,6 +2924,7 @@ function createInjector(modulesToLoad) {
2829
2924
  };
2830
2925
  }
2831
2926
  }
2927
+
2832
2928
  /**
2833
2929
  * @ngdoc function
2834
2930
  * @name ng.$anchorScroll
@@ -2884,7 +2980,7 @@ function $AnchorScrollProvider() {
2884
2980
  }
2885
2981
 
2886
2982
  // does not scroll when user clicks on anchor link that is currently on
2887
- // (no url change, no $locaiton.hash() change), browser native does scroll
2983
+ // (no url change, no $location.hash() change), browser native does scroll
2888
2984
  if (autoScrollingEnabled) {
2889
2985
  $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
2890
2986
  function autoScrollWatchAction() {
@@ -3133,7 +3229,7 @@ function Browser(window, document, $log, $sniffer) {
3133
3229
  */
3134
3230
  self.baseHref = function() {
3135
3231
  var href = baseElement.attr('href');
3136
- return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
3232
+ return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
3137
3233
  };
3138
3234
 
3139
3235
  //////////////////////////////////////////////////////////////
@@ -3172,14 +3268,15 @@ function Browser(window, document, $log, $sniffer) {
3172
3268
  } else {
3173
3269
  if (isString(value)) {
3174
3270
  cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1;
3271
+
3272
+ // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
3273
+ // - 300 cookies
3274
+ // - 20 cookies per unique domain
3275
+ // - 4096 bytes per cookie
3175
3276
  if (cookieLength > 4096) {
3176
3277
  $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+
3177
3278
  cookieLength + " > 4096 bytes)!");
3178
3279
  }
3179
- if (lastCookies.length > 20) {
3180
- $log.warn("Cookie '"+ name +"' possibly not set or overflowed because too many cookies " +
3181
- "were already set (" + lastCookies.length + " > 20 )");
3182
- }
3183
3280
  }
3184
3281
  }
3185
3282
  } else {
@@ -3192,7 +3289,13 @@ function Browser(window, document, $log, $sniffer) {
3192
3289
  cookie = cookieArray[i];
3193
3290
  index = cookie.indexOf('=');
3194
3291
  if (index > 0) { //ignore nameless cookies
3195
- lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
3292
+ var name = unescape(cookie.substring(0, index));
3293
+ // the first value that is seen for a cookie is the most
3294
+ // specific one. values for the same cookie name that
3295
+ // follow are for less specific paths.
3296
+ if (lastCookies[name] === undefined) {
3297
+ lastCookies[name] = unescape(cookie.substring(index + 1));
3298
+ }
3196
3299
  }
3197
3300
  }
3198
3301
  }
@@ -3256,6 +3359,7 @@ function $BrowserProvider(){
3256
3359
  return new Browser($window, $document, $log, $sniffer);
3257
3360
  }];
3258
3361
  }
3362
+
3259
3363
  /**
3260
3364
  * @ngdoc object
3261
3365
  * @name ng.$cacheFactory
@@ -3582,7 +3686,8 @@ function $CompileProvider($provide) {
3582
3686
  Suffix = 'Directive',
3583
3687
  COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
3584
3688
  CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
3585
- MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
3689
+ MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
3690
+ urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
3586
3691
 
3587
3692
 
3588
3693
  /**
@@ -3636,11 +3741,41 @@ function $CompileProvider($provide) {
3636
3741
  };
3637
3742
 
3638
3743
 
3744
+ /**
3745
+ * @ngdoc function
3746
+ * @name ng.$compileProvider#urlSanitizationWhitelist
3747
+ * @methodOf ng.$compileProvider
3748
+ * @function
3749
+ *
3750
+ * @description
3751
+ * Retrieves or overrides the default regular expression that is used for whitelisting of safe
3752
+ * urls during a[href] sanitization.
3753
+ *
3754
+ * The sanitization is a security measure aimed at prevent XSS attacks via html links.
3755
+ *
3756
+ * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
3757
+ * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
3758
+ * expression. If a match is found the original url is written into the dom. Otherwise the
3759
+ * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
3760
+ *
3761
+ * @param {RegExp=} regexp New regexp to whitelist urls with.
3762
+ * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
3763
+ * chaining otherwise.
3764
+ */
3765
+ this.urlSanitizationWhitelist = function(regexp) {
3766
+ if (isDefined(regexp)) {
3767
+ urlSanitizationWhitelist = regexp;
3768
+ return this;
3769
+ }
3770
+ return urlSanitizationWhitelist;
3771
+ };
3772
+
3773
+
3639
3774
  this.$get = [
3640
3775
  '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
3641
- '$controller', '$rootScope',
3776
+ '$controller', '$rootScope', '$document',
3642
3777
  function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
3643
- $controller, $rootScope) {
3778
+ $controller, $rootScope, $document) {
3644
3779
 
3645
3780
  var Attributes = function(element, attr) {
3646
3781
  this.$$element = element;
@@ -3662,7 +3797,8 @@ function $CompileProvider($provide) {
3662
3797
  */
3663
3798
  $set: function(key, value, writeAttr, attrName) {
3664
3799
  var booleanKey = getBooleanAttrName(this.$$element[0], key),
3665
- $$observers = this.$$observers;
3800
+ $$observers = this.$$observers,
3801
+ normalizedVal;
3666
3802
 
3667
3803
  if (booleanKey) {
3668
3804
  this.$$element.prop(key, value);
@@ -3681,6 +3817,19 @@ function $CompileProvider($provide) {
3681
3817
  }
3682
3818
  }
3683
3819
 
3820
+
3821
+ // sanitize a[href] values
3822
+ if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
3823
+ urlSanitizationNode.setAttribute('href', value);
3824
+
3825
+ // href property always returns normalized absolute url, so we can match against that
3826
+ normalizedVal = urlSanitizationNode.href;
3827
+ if (!normalizedVal.match(urlSanitizationWhitelist)) {
3828
+ this[key] = value = 'unsafe:' + normalizedVal;
3829
+ }
3830
+ }
3831
+
3832
+
3684
3833
  if (writeAttr !== false) {
3685
3834
  if (value === null || value === undefined) {
3686
3835
  this.$$element.removeAttr(attrName);
@@ -3724,7 +3873,8 @@ function $CompileProvider($provide) {
3724
3873
  }
3725
3874
  };
3726
3875
 
3727
- var startSymbol = $interpolate.startSymbol(),
3876
+ var urlSanitizationNode = $document[0].createElement('a'),
3877
+ startSymbol = $interpolate.startSymbol(),
3728
3878
  endSymbol = $interpolate.endSymbol(),
3729
3879
  denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
3730
3880
  ? identity
@@ -3739,13 +3889,13 @@ function $CompileProvider($provide) {
3739
3889
 
3740
3890
  function compile($compileNodes, transcludeFn, maxPriority) {
3741
3891
  if (!($compileNodes instanceof jqLite)) {
3742
- // jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
3892
+ // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
3743
3893
  $compileNodes = jqLite($compileNodes);
3744
3894
  }
3745
3895
  // We can not compile top level text elements since text nodes can be merged and we will
3746
3896
  // not be able to attach scope data to them, so we will wrap them in <span>
3747
3897
  forEach($compileNodes, function(node, index){
3748
- if (node.nodeType == 3 /* text node */) {
3898
+ if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
3749
3899
  $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
3750
3900
  }
3751
3901
  });
@@ -3757,7 +3907,14 @@ function $CompileProvider($provide) {
3757
3907
  var $linkNode = cloneConnectFn
3758
3908
  ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
3759
3909
  : $compileNodes;
3760
- $linkNode.data('$scope', scope);
3910
+
3911
+ // Attach scope only to non-text nodes.
3912
+ for(var i = 0, ii = $linkNode.length; i<ii; i++) {
3913
+ var node = $linkNode[i];
3914
+ if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
3915
+ $linkNode.eq(i).data('$scope', scope);
3916
+ }
3917
+ }
3761
3918
  safeAddClass($linkNode, 'ng-scope');
3762
3919
  if (cloneConnectFn) cloneConnectFn($linkNode, scope);
3763
3920
  if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
@@ -3784,7 +3941,7 @@ function $CompileProvider($provide) {
3784
3941
  * functions return values - the linking functions - are combined into a composite linking
3785
3942
  * function, which is the a linking function for the node.
3786
3943
  *
3787
- * @param {NodeList} nodeList an array of nodes to compile
3944
+ * @param {NodeList} nodeList an array of nodes or NodeList to compile
3788
3945
  * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
3789
3946
  * scope argument is auto-generated to the new child of the transcluded parent scope.
3790
3947
  * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
@@ -3794,68 +3951,75 @@ function $CompileProvider($provide) {
3794
3951
  * @returns {?function} A composite linking function of all of the matched directives or null.
3795
3952
  */
3796
3953
  function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
3797
- var linkFns = [],
3798
- nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
3954
+ var linkFns = [],
3955
+ nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
3799
3956
 
3800
- for(var i = 0; i < nodeList.length; i++) {
3801
- attrs = new Attributes();
3957
+ for(var i = 0; i < nodeList.length; i++) {
3958
+ attrs = new Attributes();
3802
3959
 
3803
- // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
3804
- directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
3960
+ // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
3961
+ directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
3805
3962
 
3806
- nodeLinkFn = (directives.length)
3807
- ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
3808
- : null;
3963
+ nodeLinkFn = (directives.length)
3964
+ ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
3965
+ : null;
3809
3966
 
3810
- childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
3811
- ? null
3812
- : compileNodes(nodeList[i].childNodes,
3813
- nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
3967
+ childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
3968
+ ? null
3969
+ : compileNodes(nodeList[i].childNodes,
3970
+ nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
3814
3971
 
3815
- linkFns.push(nodeLinkFn);
3816
- linkFns.push(childLinkFn);
3817
- linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
3818
- }
3972
+ linkFns.push(nodeLinkFn);
3973
+ linkFns.push(childLinkFn);
3974
+ linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
3975
+ }
3819
3976
 
3820
- // return a linking function if we have found anything, null otherwise
3821
- return linkFnFound ? compositeLinkFn : null;
3977
+ // return a linking function if we have found anything, null otherwise
3978
+ return linkFnFound ? compositeLinkFn : null;
3822
3979
 
3823
- function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
3824
- var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn;
3980
+ function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
3981
+ var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;
3825
3982
 
3826
- for(var i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
3827
- node = nodeList[n];
3828
- nodeLinkFn = linkFns[i++];
3829
- childLinkFn = linkFns[i++];
3983
+ // copy nodeList so that linking doesn't break due to live list updates.
3984
+ var stableNodeList = [];
3985
+ for (i = 0, ii = nodeList.length; i < ii; i++) {
3986
+ stableNodeList.push(nodeList[i]);
3987
+ }
3830
3988
 
3831
- if (nodeLinkFn) {
3832
- if (nodeLinkFn.scope) {
3833
- childScope = scope.$new(isObject(nodeLinkFn.scope));
3834
- jqLite(node).data('$scope', childScope);
3835
- } else {
3836
- childScope = scope;
3837
- }
3838
- childTranscludeFn = nodeLinkFn.transclude;
3839
- if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
3840
- nodeLinkFn(childLinkFn, childScope, node, $rootElement,
3841
- (function(transcludeFn) {
3842
- return function(cloneFn) {
3843
- var transcludeScope = scope.$new();
3844
-
3845
- return transcludeFn(transcludeScope, cloneFn).
3846
- bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
3989
+ for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
3990
+ node = stableNodeList[n];
3991
+ nodeLinkFn = linkFns[i++];
3992
+ childLinkFn = linkFns[i++];
3993
+
3994
+ if (nodeLinkFn) {
3995
+ if (nodeLinkFn.scope) {
3996
+ childScope = scope.$new(isObject(nodeLinkFn.scope));
3997
+ jqLite(node).data('$scope', childScope);
3998
+ } else {
3999
+ childScope = scope;
4000
+ }
4001
+ childTranscludeFn = nodeLinkFn.transclude;
4002
+ if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
4003
+ nodeLinkFn(childLinkFn, childScope, node, $rootElement,
4004
+ (function(transcludeFn) {
4005
+ return function(cloneFn) {
4006
+ var transcludeScope = scope.$new();
4007
+ transcludeScope.$$transcluded = true;
4008
+
4009
+ return transcludeFn(transcludeScope, cloneFn).
4010
+ bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
3847
4011
  };
3848
4012
  })(childTranscludeFn || transcludeFn)
3849
- );
3850
- } else {
3851
- nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
3852
- }
3853
- } else if (childLinkFn) {
3854
- childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
3855
- }
3856
- }
3857
- }
3858
- }
4013
+ );
4014
+ } else {
4015
+ nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
4016
+ }
4017
+ } else if (childLinkFn) {
4018
+ childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
4019
+ }
4020
+ }
4021
+ }
4022
+ }
3859
4023
 
3860
4024
 
3861
4025
  /**
@@ -3936,9 +4100,9 @@ function $CompileProvider($provide) {
3936
4100
 
3937
4101
 
3938
4102
  /**
3939
- * Once the directives have been collected their compile functions is executed. This method
4103
+ * Once the directives have been collected, their compile functions are executed. This method
3940
4104
  * is responsible for inlining directive templates as well as terminating the application
3941
- * of the directives if the terminal directive has been reached..
4105
+ * of the directives if the terminal directive has been reached.
3942
4106
  *
3943
4107
  * @param {Array} directives Array of collected directives to execute their compile function.
3944
4108
  * this needs to be pre-sorted by priority order.
@@ -3946,11 +4110,11 @@ function $CompileProvider($provide) {
3946
4110
  * @param {Object} templateAttrs The shared attribute function
3947
4111
  * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
3948
4112
  * scope argument is auto-generated to the new child of the transcluded parent scope.
3949
- * @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
3950
- * argument has the root jqLite array so that we can replace widgets on it.
4113
+ * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
4114
+ * argument has the root jqLite array so that we can replace nodes on it.
3951
4115
  * @returns linkFn
3952
4116
  */
3953
- function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
4117
+ function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
3954
4118
  var terminalPriority = -Number.MAX_VALUE,
3955
4119
  preLinkFns = [],
3956
4120
  postLinkFns = [],
@@ -4002,9 +4166,9 @@ function $CompileProvider($provide) {
4002
4166
  if (directiveValue == 'element') {
4003
4167
  $template = jqLite(compileNode);
4004
4168
  $compileNode = templateAttrs.$$element =
4005
- jqLite('<!-- ' + directiveName + ': ' + templateAttrs[directiveName] + ' -->');
4169
+ jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
4006
4170
  compileNode = $compileNode[0];
4007
- replaceWith($rootElement, jqLite($template[0]), compileNode);
4171
+ replaceWith(jqCollection, jqLite($template[0]), compileNode);
4008
4172
  childTranscludeFn = compile($template, transcludeFn, terminalPriority);
4009
4173
  } else {
4010
4174
  $template = jqLite(JQLiteClone(compileNode)).contents();
@@ -4028,7 +4192,7 @@ function $CompileProvider($provide) {
4028
4192
  throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
4029
4193
  }
4030
4194
 
4031
- replaceWith($rootElement, $compileNode, compileNode);
4195
+ replaceWith(jqCollection, $compileNode, compileNode);
4032
4196
 
4033
4197
  var newTemplateAttrs = {$attr: {}};
4034
4198
 
@@ -4056,7 +4220,7 @@ function $CompileProvider($provide) {
4056
4220
  assertNoDuplicate('template', templateDirective, directive, $compileNode);
4057
4221
  templateDirective = directive;
4058
4222
  nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
4059
- nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
4223
+ nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
4060
4224
  childTranscludeFn);
4061
4225
  ii = directives.length;
4062
4226
  } else if (directive.compile) {
@@ -4146,6 +4310,8 @@ function $CompileProvider($provide) {
4146
4310
  lastValue,
4147
4311
  parentGet, parentSet;
4148
4312
 
4313
+ scope.$$isolateBindings[scopeName] = mode + attrName;
4314
+
4149
4315
  switch (mode) {
4150
4316
 
4151
4317
  case '@': {
@@ -4187,7 +4353,7 @@ function $CompileProvider($provide) {
4187
4353
  parentGet = $parse(attrs[attrName]);
4188
4354
  scope[scopeName] = function(locals) {
4189
4355
  return parentGet(parentScope, locals);
4190
- }
4356
+ };
4191
4357
  break;
4192
4358
  }
4193
4359
 
@@ -4356,8 +4522,8 @@ function $CompileProvider($provide) {
4356
4522
  }
4357
4523
 
4358
4524
  directives.unshift(derivedSyncDirective);
4359
- afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
4360
- afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
4525
+ afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
4526
+ afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
4361
4527
 
4362
4528
 
4363
4529
  while(linkQueue.length) {
@@ -4436,10 +4602,10 @@ function $CompileProvider($provide) {
4436
4602
  function addAttrInterpolateDirective(node, directives, value, name) {
4437
4603
  var interpolateFn = $interpolate(value, true);
4438
4604
 
4439
-
4440
4605
  // no interpolation found -> ignore
4441
4606
  if (!interpolateFn) return;
4442
4607
 
4608
+
4443
4609
  directives.push({
4444
4610
  priority: 100,
4445
4611
  compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
@@ -4622,7 +4788,7 @@ function $ControllerProvider() {
4622
4788
  * @description
4623
4789
  * `$controller` service is responsible for instantiating controllers.
4624
4790
  *
4625
- * It's just simple call to {@link AUTO.$injector $injector}, but extracted into
4791
+ * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
4626
4792
  * a service, so that one can override this service with {@link https://gist.github.com/1649788
4627
4793
  * BC version}.
4628
4794
  */
@@ -4667,14 +4833,15 @@ function $DocumentProvider(){
4667
4833
  * the browser console.
4668
4834
  *
4669
4835
  * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
4670
- * {@link ngMock.$exceptionHandler mock $exceptionHandler}
4836
+ * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
4671
4837
  *
4672
4838
  * @param {Error} exception Exception associated with the error.
4673
4839
  * @param {string=} cause optional information about the context in which
4674
4840
  * the error was thrown.
4841
+ *
4675
4842
  */
4676
4843
  function $ExceptionHandlerProvider() {
4677
- this.$get = ['$log', function($log){
4844
+ this.$get = ['$log', function($log) {
4678
4845
  return function(exception, cause) {
4679
4846
  $log.error.apply($log, arguments);
4680
4847
  };
@@ -4862,7 +5029,7 @@ function $InterpolateProvider() {
4862
5029
  }];
4863
5030
  }
4864
5031
 
4865
- var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
5032
+ var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
4866
5033
  PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
4867
5034
  HASH_MATCH = PATH_MATCH,
4868
5035
  DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
@@ -4941,7 +5108,8 @@ function convertToHashbangUrl(url, basePath, hashPrefix) {
4941
5108
  var match = matchUrl(url);
4942
5109
 
4943
5110
  // already hashbang url
4944
- if (decodeURIComponent(match.path) == basePath) {
5111
+ if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
5112
+ match.hash.indexOf(hashPrefix) === 0) {
4945
5113
  return url;
4946
5114
  // convert html5 url -> hashbang url
4947
5115
  } else {
@@ -5438,6 +5606,10 @@ function $LocationProvider(){
5438
5606
  // update $location when $browser url changes
5439
5607
  $browser.onUrlChange(function(newUrl) {
5440
5608
  if ($location.absUrl() != newUrl) {
5609
+ if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
5610
+ $browser.url($location.absUrl());
5611
+ return;
5612
+ }
5441
5613
  $rootScope.$evalAsync(function() {
5442
5614
  var oldUrl = $location.absUrl();
5443
5615
 
@@ -5746,10 +5918,10 @@ function lex(text, csp){
5746
5918
  function readIdent() {
5747
5919
  var ident = "",
5748
5920
  start = index,
5749
- lastDot, peekIndex, methodName;
5921
+ lastDot, peekIndex, methodName, ch;
5750
5922
 
5751
5923
  while (index < text.length) {
5752
- var ch = text.charAt(index);
5924
+ ch = text.charAt(index);
5753
5925
  if (ch == '.' || isIdent(ch) || isNumber(ch)) {
5754
5926
  if (ch == '.') lastDot = index;
5755
5927
  ident += ch;
@@ -5763,7 +5935,7 @@ function lex(text, csp){
5763
5935
  if (lastDot) {
5764
5936
  peekIndex = index;
5765
5937
  while(peekIndex < text.length) {
5766
- var ch = text.charAt(peekIndex);
5938
+ ch = text.charAt(peekIndex);
5767
5939
  if (ch == '(') {
5768
5940
  methodName = ident.substr(lastDot - start + 1);
5769
5941
  ident = ident.substr(0, lastDot - start);
@@ -6016,8 +6188,8 @@ function parser(text, json, $filter, csp){
6016
6188
  text.substring(0, token.index) + "] can not be assigned to", token);
6017
6189
  }
6018
6190
  right = logicalOR();
6019
- return function(self, locals){
6020
- return left.assign(self, right(self, locals), locals);
6191
+ return function(scope, locals){
6192
+ return left.assign(scope, right(scope, locals), locals);
6021
6193
  };
6022
6194
  } else {
6023
6195
  return left;
@@ -6134,12 +6306,12 @@ function parser(text, json, $filter, csp){
6134
6306
  var field = expect().text;
6135
6307
  var getter = getterFn(field, csp);
6136
6308
  return extend(
6137
- function(self, locals) {
6138
- return getter(object(self, locals), locals);
6309
+ function(scope, locals, self) {
6310
+ return getter(self || object(scope, locals), locals);
6139
6311
  },
6140
6312
  {
6141
- assign:function(self, value, locals) {
6142
- return setter(object(self, locals), field, value);
6313
+ assign:function(scope, value, locals) {
6314
+ return setter(object(scope, locals), field, value);
6143
6315
  }
6144
6316
  }
6145
6317
  );
@@ -6180,14 +6352,14 @@ function parser(text, json, $filter, csp){
6180
6352
  } while (expect(','));
6181
6353
  }
6182
6354
  consume(')');
6183
- return function(self, locals){
6355
+ return function(scope, locals){
6184
6356
  var args = [],
6185
- context = contextGetter ? contextGetter(self, locals) : self;
6357
+ context = contextGetter ? contextGetter(scope, locals) : scope;
6186
6358
 
6187
6359
  for ( var i = 0; i < argsFn.length; i++) {
6188
- args.push(argsFn[i](self, locals));
6360
+ args.push(argsFn[i](scope, locals));
6189
6361
  }
6190
- var fnPtr = fn(self, locals) || noop;
6362
+ var fnPtr = fn(scope, locals, context) || noop;
6191
6363
  // IE stupidity!
6192
6364
  return fnPtr.apply
6193
6365
  ? fnPtr.apply(context, args)
@@ -6229,8 +6401,7 @@ function parser(text, json, $filter, csp){
6229
6401
  var object = {};
6230
6402
  for ( var i = 0; i < keyValues.length; i++) {
6231
6403
  var keyValue = keyValues[i];
6232
- var value = keyValue.value(self, locals);
6233
- object[keyValue.key] = value;
6404
+ object[keyValue.key] = keyValue.value(self, locals);
6234
6405
  }
6235
6406
  return object;
6236
6407
  };
@@ -6352,7 +6523,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
6352
6523
  }
6353
6524
  return pathVal;
6354
6525
  };
6355
- };
6526
+ }
6356
6527
 
6357
6528
  function getterFn(path, csp) {
6358
6529
  if (getterFnCache.hasOwnProperty(path)) {
@@ -6367,7 +6538,7 @@ function getterFn(path, csp) {
6367
6538
  fn = (pathKeysLength < 6)
6368
6539
  ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
6369
6540
  : function(scope, locals) {
6370
- var i = 0, val
6541
+ var i = 0, val;
6371
6542
  do {
6372
6543
  val = cspSafeGetterFn(
6373
6544
  pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
@@ -6432,9 +6603,10 @@ function getterFn(path, csp) {
6432
6603
  * @param {string} expression String expression to compile.
6433
6604
  * @returns {function(context, locals)} a function which represents the compiled expression:
6434
6605
  *
6435
- * * `context`: an object against which any expressions embedded in the strings are evaluated
6436
- * against (Topically a scope object).
6437
- * * `locals`: local variables context object, useful for overriding values in `context`.
6606
+ * * `context` – `{object}` – an object against which any expressions embedded in the strings
6607
+ * are evaluated against (tipically a scope object).
6608
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values in
6609
+ * `context`.
6438
6610
  *
6439
6611
  * The return function also has an `assign` property, if the expression is assignable, which
6440
6612
  * allows one to set values to expressions.
@@ -6470,8 +6642,8 @@ function $ParseProvider() {
6470
6642
  * interface for interacting with an object that represents the result of an action that is
6471
6643
  * performed asynchronously, and may or may not be finished at any given point in time.
6472
6644
  *
6473
- * From the perspective of dealing with error handling, deferred and promise apis are to
6474
- * asynchronous programing what `try`, `catch` and `throw` keywords are to synchronous programing.
6645
+ * From the perspective of dealing with error handling, deferred and promise APIs are to
6646
+ * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
6475
6647
  *
6476
6648
  * <pre>
6477
6649
  * // for the purpose of this example let's assume that variables `$q` and `scope` are
@@ -6505,7 +6677,7 @@ function $ParseProvider() {
6505
6677
  *
6506
6678
  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
6507
6679
  * comes in the way of
6508
- * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
6680
+ * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
6509
6681
  *
6510
6682
  * Additionally the promise api allows for composition that is very hard to do with the
6511
6683
  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
@@ -6517,7 +6689,7 @@ function $ParseProvider() {
6517
6689
  *
6518
6690
  * A new instance of deferred is constructed by calling `$q.defer()`.
6519
6691
  *
6520
- * The purpose of the deferred object is to expose the associated Promise instance as well as apis
6692
+ * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
6521
6693
  * that can be used for signaling the successful or unsuccessful completion of the task.
6522
6694
  *
6523
6695
  * **Methods**
@@ -6560,7 +6732,7 @@ function $ParseProvider() {
6560
6732
  * return result + 1;
6561
6733
  * });
6562
6734
  *
6563
- * // promiseB will be resolved immediately after promiseA is resolved and it's value will be
6735
+ * // promiseB will be resolved immediately after promiseA is resolved and its value will be
6564
6736
  * // the result of promiseA incremented by 1
6565
6737
  * </pre>
6566
6738
  *
@@ -6579,8 +6751,32 @@ function $ParseProvider() {
6579
6751
  * models and avoiding unnecessary browser repaints, which would result in flickering UI.
6580
6752
  * - $q promises are recognized by the templating engine in angular, which means that in templates
6581
6753
  * you can treat promises attached to a scope as if they were the resulting values.
6582
- * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains
6754
+ * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
6583
6755
  * all the important functionality needed for common async tasks.
6756
+ *
6757
+ * # Testing
6758
+ *
6759
+ * <pre>
6760
+ * it('should simulate promise', inject(function($q, $rootScope) {
6761
+ * var deferred = $q.defer();
6762
+ * var promise = deferred.promise;
6763
+ * var resolvedValue;
6764
+ *
6765
+ * promise.then(function(value) { resolvedValue = value; });
6766
+ * expect(resolvedValue).toBeUndefined();
6767
+ *
6768
+ * // Simulate resolving of promise
6769
+ * deferred.resolve(123);
6770
+ * // Note that the 'then' function does not get called synchronously.
6771
+ * // This is because we want the promise API to always be async, whether or not
6772
+ * // it got called synchronously or asynchronously.
6773
+ * expect(resolvedValue).toBeUndefined();
6774
+ *
6775
+ * // Propagate promise resolution to 'then' functions using $apply().
6776
+ * $rootScope.$apply();
6777
+ * expect(resolvedValue).toEqual(123);
6778
+ * });
6779
+ * </pre>
6584
6780
  */
6585
6781
  function $QProvider() {
6586
6782
 
@@ -6746,14 +6942,11 @@ function qFactory(nextTick, exceptionHandler) {
6746
6942
  * @methodOf ng.$q
6747
6943
  * @description
6748
6944
  * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
6749
- * This is useful when you are dealing with on object that might or might not be a promise, or if
6945
+ * This is useful when you are dealing with an object that might or might not be a promise, or if
6750
6946
  * the promise comes from a source that can't be trusted.
6751
6947
  *
6752
6948
  * @param {*} value Value or a promise
6753
- * @returns {Promise} Returns a single promise that will be resolved with an array of values,
6754
- * each value coresponding to the promise at the same index in the `promises` array. If any of
6755
- * the promises is resolved with a rejection, this resulting promise will be resolved with the
6756
- * same rejection.
6949
+ * @returns {Promise} Returns a promise of the passed value or promise
6757
6950
  */
6758
6951
  var when = function(value, callback, errback) {
6759
6952
  var result = defer(),
@@ -6813,7 +7006,7 @@ function qFactory(nextTick, exceptionHandler) {
6813
7006
  *
6814
7007
  * @param {Array.<Promise>} promises An array of promises.
6815
7008
  * @returns {Promise} Returns a single promise that will be resolved with an array of values,
6816
- * each value coresponding to the promise at the same index in the `promises` array. If any of
7009
+ * each value corresponding to the promise at the same index in the `promises` array. If any of
6817
7010
  * the promises is resolved with a rejection, this resulting promise will be resolved with the
6818
7011
  * same rejection.
6819
7012
  */
@@ -6867,8 +7060,13 @@ function $RouteProvider(){
6867
7060
  *
6868
7061
  * @param {string} path Route path (matched against `$location.path`). If `$location.path`
6869
7062
  * contains redundant trailing slash or is missing one, the route will still match and the
6870
- * `$location.path` will be updated to add or drop the trailing slash to exacly match the
7063
+ * `$location.path` will be updated to add or drop the trailing slash to exactly match the
6871
7064
  * route definition.
7065
+ *
7066
+ * `path` can contain named groups starting with a colon (`:name`). All characters up to the
7067
+ * next slash are matched and stored in `$routeParams` under the given `name` when the route
7068
+ * matches.
7069
+ *
6872
7070
  * @param {Object} route Mapping information to be assigned to `$route.current` on route
6873
7071
  * match.
6874
7072
  *
@@ -7105,8 +7303,9 @@ function $RouteProvider(){
7105
7303
  * {@link ng.directive:ngView ngView} listens for the directive
7106
7304
  * to instantiate the controller and render the view.
7107
7305
  *
7306
+ * @param {Object} angularEvent Synthetic event object.
7108
7307
  * @param {Route} current Current route information.
7109
- * @param {Route} previous Previous route information.
7308
+ * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
7110
7309
  */
7111
7310
 
7112
7311
  /**
@@ -7133,8 +7332,7 @@ function $RouteProvider(){
7133
7332
  * instance of the Controller.
7134
7333
  */
7135
7334
 
7136
- var matcher = switchRouteMatcher,
7137
- forceReload = false,
7335
+ var forceReload = false,
7138
7336
  $route = {
7139
7337
  routes: routes,
7140
7338
 
@@ -7162,21 +7360,36 @@ function $RouteProvider(){
7162
7360
 
7163
7361
  /////////////////////////////////////////////////////
7164
7362
 
7363
+ /**
7364
+ * @param on {string} current url
7365
+ * @param when {string} route when template to match the url against
7366
+ * @return {?Object}
7367
+ */
7165
7368
  function switchRouteMatcher(on, when) {
7166
7369
  // TODO(i): this code is convoluted and inefficient, we should construct the route matching
7167
7370
  // regex only once and then reuse it
7168
- var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
7371
+
7372
+ // Escape regexp special characters.
7373
+ when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
7374
+ var regex = '',
7169
7375
  params = [],
7170
7376
  dst = {};
7171
- forEach(when.split(/\W/), function(param) {
7172
- if (param) {
7173
- var paramRegExp = new RegExp(":" + param + "([\\W])");
7174
- if (regex.match(paramRegExp)) {
7175
- regex = regex.replace(paramRegExp, "([^\\/]*)$1");
7176
- params.push(param);
7177
- }
7178
- }
7179
- });
7377
+
7378
+ var re = /:(\w+)/g,
7379
+ paramMatch,
7380
+ lastMatchedIndex = 0;
7381
+
7382
+ while ((paramMatch = re.exec(when)) !== null) {
7383
+ // Find each :param in `when` and replace it with a capturing group.
7384
+ // Append all other sections of when unchanged.
7385
+ regex += when.slice(lastMatchedIndex, paramMatch.index);
7386
+ regex += '([^\\/]*)';
7387
+ params.push(paramMatch[1]);
7388
+ lastMatchedIndex = re.lastIndex;
7389
+ }
7390
+ // Append trailing path part.
7391
+ regex += when.substr(lastMatchedIndex);
7392
+
7180
7393
  var match = on.match(new RegExp(regex));
7181
7394
  if (match) {
7182
7395
  forEach(params, function(name, index) {
@@ -7190,7 +7403,7 @@ function $RouteProvider(){
7190
7403
  var next = parseRoute(),
7191
7404
  last = $route.current;
7192
7405
 
7193
- if (next && last && next.$route === last.$route
7406
+ if (next && last && next.$$route === last.$$route
7194
7407
  && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
7195
7408
  last.params = next.params;
7196
7409
  copy(last.params, $routeParams);
@@ -7265,11 +7478,11 @@ function $RouteProvider(){
7265
7478
  // Match a route
7266
7479
  var params, match;
7267
7480
  forEach(routes, function(route, path) {
7268
- if (!match && (params = matcher($location.path(), path))) {
7481
+ if (!match && (params = switchRouteMatcher($location.path(), path))) {
7269
7482
  match = inherit(route, {
7270
7483
  params: extend({}, $location.search(), params),
7271
7484
  pathParams: params});
7272
- match.$route = route;
7485
+ match.$$route = route;
7273
7486
  }
7274
7487
  });
7275
7488
  // No route matched; fallback to "otherwise" route
@@ -7329,22 +7542,22 @@ function $RouteParamsProvider() {
7329
7542
  /**
7330
7543
  * DESIGN NOTES
7331
7544
  *
7332
- * The design decisions behind the scope ware heavily favored for speed and memory consumption.
7545
+ * The design decisions behind the scope are heavily favored for speed and memory consumption.
7333
7546
  *
7334
7547
  * The typical use of scope is to watch the expressions, which most of the time return the same
7335
7548
  * value as last time so we optimize the operation.
7336
7549
  *
7337
- * Closures construction is expensive from speed as well as memory:
7338
- * - no closures, instead ups prototypical inheritance for API
7550
+ * Closures construction is expensive in terms of speed as well as memory:
7551
+ * - No closures, instead use prototypical inheritance for API
7339
7552
  * - Internal state needs to be stored on scope directly, which means that private state is
7340
7553
  * exposed as $$____ properties
7341
7554
  *
7342
7555
  * Loop operations are optimized by using while(count--) { ... }
7343
7556
  * - this means that in order to keep the same order of execution as addition we have to add
7344
- * items to the array at the begging (shift) instead of at the end (push)
7557
+ * items to the array at the beginning (shift) instead of at the end (push)
7345
7558
  *
7346
7559
  * Child scopes are created and removed often
7347
- * - Using array would be slow since inserts in meddle are expensive so we use linked list
7560
+ * - Using an array would be slow since inserts in middle are expensive so we use linked list
7348
7561
  *
7349
7562
  * There are few watches then a lot of observers. This is why you don't want the observer to be
7350
7563
  * implemented in the same way as watch. Watch requires return of initialization function which
@@ -7366,7 +7579,7 @@ function $RouteParamsProvider() {
7366
7579
  * @methodOf ng.$rootScopeProvider
7367
7580
  * @description
7368
7581
  *
7369
- * Sets the number of digest iteration the scope should attempt to execute before giving up and
7582
+ * Sets the number of digest iterations the scope should attempt to execute before giving up and
7370
7583
  * assuming that the model is unstable.
7371
7584
  *
7372
7585
  * The current default is 10 iterations.
@@ -7417,7 +7630,7 @@ function $RootScopeProvider(){
7417
7630
  expect(scope.greeting).toEqual(undefined);
7418
7631
 
7419
7632
  scope.$watch('name', function() {
7420
- this.greeting = this.salutation + ' ' + this.name + '!';
7633
+ scope.greeting = scope.salutation + ' ' + scope.name + '!';
7421
7634
  }); // initialize the watch
7422
7635
 
7423
7636
  expect(scope.greeting).toEqual(undefined);
@@ -7460,8 +7673,10 @@ function $RootScopeProvider(){
7460
7673
  this.$$nextSibling = this.$$prevSibling =
7461
7674
  this.$$childHead = this.$$childTail = null;
7462
7675
  this['this'] = this.$root = this;
7676
+ this.$$destroyed = false;
7463
7677
  this.$$asyncQueue = [];
7464
7678
  this.$$listeners = {};
7679
+ this.$$isolateBindings = {};
7465
7680
  }
7466
7681
 
7467
7682
  /**
@@ -7578,7 +7793,7 @@ function $RootScopeProvider(){
7578
7793
  scope.counter = 0;
7579
7794
 
7580
7795
  expect(scope.counter).toEqual(0);
7581
- scope.$watch('name', function(newValue, oldValue) { counter = counter + 1; });
7796
+ scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
7582
7797
  expect(scope.counter).toEqual(0);
7583
7798
 
7584
7799
  scope.$digest();
@@ -7644,7 +7859,7 @@ function $RootScopeProvider(){
7644
7859
  * @function
7645
7860
  *
7646
7861
  * @description
7647
- * Process all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
7862
+ * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
7648
7863
  * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
7649
7864
  * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
7650
7865
  * firing. This means that it is possible to get into an infinite loop. This function will throw
@@ -7671,7 +7886,7 @@ function $RootScopeProvider(){
7671
7886
 
7672
7887
  expect(scope.counter).toEqual(0);
7673
7888
  scope.$watch('name', function(newValue, oldValue) {
7674
- counter = counter + 1;
7889
+ scope.counter = scope.counter + 1;
7675
7890
  });
7676
7891
  expect(scope.counter).toEqual(0);
7677
7892
 
@@ -7793,10 +8008,12 @@ function $RootScopeProvider(){
7793
8008
  * perform any necessary cleanup.
7794
8009
  */
7795
8010
  $destroy: function() {
7796
- if ($rootScope == this) return; // we can't remove the root node;
8011
+ // we can't destroy the root scope or a scope that has been already destroyed
8012
+ if ($rootScope == this || this.$$destroyed) return;
7797
8013
  var parent = this.$parent;
7798
8014
 
7799
8015
  this.$broadcast('$destroy');
8016
+ this.$$destroyed = true;
7800
8017
 
7801
8018
  if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
7802
8019
  if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
@@ -7941,10 +8158,6 @@ function $RootScopeProvider(){
7941
8158
  * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
7942
8159
  * event life cycle.
7943
8160
  *
7944
- * @param {string} name Event name to listen on.
7945
- * @param {function(event)} listener Function to call when the event is emitted.
7946
- * @returns {function()} Returns a deregistration function for this listener.
7947
- *
7948
8161
  * The event listener function format is: `function(event, args...)`. The `event` object
7949
8162
  * passed into the listener has the following attributes:
7950
8163
  *
@@ -7955,6 +8168,10 @@ function $RootScopeProvider(){
7955
8168
  * propagation (available only for events that were `$emit`-ed).
7956
8169
  * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
7957
8170
  * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
8171
+ *
8172
+ * @param {string} name Event name to listen on.
8173
+ * @param {function(event, args...)} listener Function to call when the event is emitted.
8174
+ * @returns {function()} Returns a deregistration function for this listener.
7958
8175
  */
7959
8176
  $on: function(name, listener) {
7960
8177
  var namedListeners = this.$$listeners[name];
@@ -7984,7 +8201,7 @@ function $RootScopeProvider(){
7984
8201
  * Afterwards, the event traverses upwards toward the root scope and calls all registered
7985
8202
  * listeners along the way. The event will stop propagating if one of the listeners cancels it.
7986
8203
  *
7987
- * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
8204
+ * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
7988
8205
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
7989
8206
  *
7990
8207
  * @param {string} name Event name to emit.
@@ -8053,7 +8270,7 @@ function $RootScopeProvider(){
8053
8270
  * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
8054
8271
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
8055
8272
  *
8056
- * @param {string} name Event name to emit.
8273
+ * @param {string} name Event name to broadcast.
8057
8274
  * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
8058
8275
  * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
8059
8276
  */
@@ -8132,7 +8349,7 @@ function $RootScopeProvider(){
8132
8349
 
8133
8350
  /**
8134
8351
  * function used as an initial value for watchers.
8135
- * because it's uniqueue we can easily tell it apart from other values
8352
+ * because it's unique we can easily tell it apart from other values
8136
8353
  */
8137
8354
  function initWatchVal() {}
8138
8355
  }];
@@ -8199,10 +8416,23 @@ function $SnifferProvider() {
8199
8416
  * @example
8200
8417
  <doc:example>
8201
8418
  <doc:source>
8202
- <input ng-init="$window = $service('$window'); greeting='Hello World!'" type="text" ng-model="greeting" />
8203
- <button ng-click="$window.alert(greeting)">ALERT</button>
8419
+ <script>
8420
+ function Ctrl($scope, $window) {
8421
+ $scope.$window = $window;
8422
+ $scope.greeting = 'Hello, World!';
8423
+ }
8424
+ </script>
8425
+ <div ng-controller="Ctrl">
8426
+ <input type="text" ng-model="greeting" />
8427
+ <button ng-click="$window.alert(greeting)">ALERT</button>
8428
+ </div>
8204
8429
  </doc:source>
8205
8430
  <doc:scenario>
8431
+ it('should display the greeting in the input box', function() {
8432
+ input('greeting').enter('Hello, E2E Tests');
8433
+ // If we click the button it will block the test runner
8434
+ // element(':button').click();
8435
+ });
8206
8436
  </doc:scenario>
8207
8437
  </doc:example>
8208
8438
  */
@@ -8346,7 +8576,7 @@ function $HttpProvider() {
8346
8576
  /**
8347
8577
  * @ngdoc function
8348
8578
  * @name ng.$http
8349
- * @requires $httpBacked
8579
+ * @requires $httpBackend
8350
8580
  * @requires $browser
8351
8581
  * @requires $cacheFactory
8352
8582
  * @requires $rootScope
@@ -8355,7 +8585,7 @@ function $HttpProvider() {
8355
8585
  *
8356
8586
  * @description
8357
8587
  * The `$http` service is a core Angular service that facilitates communication with the remote
8358
- * HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest
8588
+ * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
8359
8589
  * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
8360
8590
  *
8361
8591
  * For unit testing applications that use `$http` service, see
@@ -8365,13 +8595,13 @@ function $HttpProvider() {
8365
8595
  * $resource} service.
8366
8596
  *
8367
8597
  * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
8368
- * the $q service. While for simple usage patters this doesn't matter much, for advanced usage,
8369
- * it is important to familiarize yourself with these apis and guarantees they provide.
8598
+ * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
8599
+ * it is important to familiarize yourself with these APIs and the guarantees they provide.
8370
8600
  *
8371
8601
  *
8372
8602
  * # General usage
8373
8603
  * The `$http` service is a function which takes a single argument — a configuration object —
8374
- * that is used to generate an http request and returns a {@link ng.$q promise}
8604
+ * that is used to generate an HTTP request and returns a {@link ng.$q promise}
8375
8605
  * with two $http specific methods: `success` and `error`.
8376
8606
  *
8377
8607
  * <pre>
@@ -8382,22 +8612,25 @@ function $HttpProvider() {
8382
8612
  * }).
8383
8613
  * error(function(data, status, headers, config) {
8384
8614
  * // called asynchronously if an error occurs
8385
- * // or server returns response with status
8386
- * // code outside of the <200, 400) range
8615
+ * // or server returns response with an error status.
8387
8616
  * });
8388
8617
  * </pre>
8389
8618
  *
8390
- * Since the returned value of calling the $http function is a Promise object, you can also use
8619
+ * Since the returned value of calling the $http function is a `promise`, you can also use
8391
8620
  * the `then` method to register callbacks, and these callbacks will receive a single argument –
8392
- * an object representing the response. See the api signature and type info below for more
8621
+ * an object representing the response. See the API signature and type info below for more
8393
8622
  * details.
8394
8623
  *
8624
+ * A response status code between 200 and 299 is considered a success status and
8625
+ * will result in the success callback being called. Note that if the response is a redirect,
8626
+ * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
8627
+ * called for such responses.
8395
8628
  *
8396
8629
  * # Shortcut methods
8397
8630
  *
8398
- * Since all invocation of the $http service require definition of the http method and url and
8399
- * POST and PUT requests require response body/data to be provided as well, shortcut methods
8400
- * were created to simplify using the api:
8631
+ * Since all invocations of the $http service require passing in an HTTP method and URL, and
8632
+ * POST/PUT requests require request data to be provided as well, shortcut methods
8633
+ * were created:
8401
8634
  *
8402
8635
  * <pre>
8403
8636
  * $http.get('/someUrl').success(successCallback);
@@ -8416,25 +8649,25 @@ function $HttpProvider() {
8416
8649
  *
8417
8650
  * # Setting HTTP Headers
8418
8651
  *
8419
- * The $http service will automatically add certain http headers to all requests. These defaults
8652
+ * The $http service will automatically add certain HTTP headers to all requests. These defaults
8420
8653
  * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
8421
8654
  * object, which currently contains this default configuration:
8422
8655
  *
8423
8656
  * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
8424
8657
  * - `Accept: application/json, text/plain, * / *`
8425
8658
  * - `X-Requested-With: XMLHttpRequest`
8426
- * - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
8659
+ * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
8427
8660
  * - `Content-Type: application/json`
8428
- * - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
8661
+ * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
8429
8662
  * - `Content-Type: application/json`
8430
8663
  *
8431
- * To add or overwrite these defaults, simply add or remove a property from this configuration
8664
+ * To add or overwrite these defaults, simply add or remove a property from these configuration
8432
8665
  * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
8433
- * with name equal to the lower-cased http method name, e.g.
8666
+ * with the lowercased HTTP method name as the key, e.g.
8434
8667
  * `$httpProvider.defaults.headers.get['My-Header']='value'`.
8435
8668
  *
8436
- * Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar
8437
- * fassion as described above.
8669
+ * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
8670
+ * fashion.
8438
8671
  *
8439
8672
  *
8440
8673
  * # Transforming Requests and Responses
@@ -8444,32 +8677,36 @@ function $HttpProvider() {
8444
8677
  *
8445
8678
  * Request transformations:
8446
8679
  *
8447
- * - if the `data` property of the request config object contains an object, serialize it into
8680
+ * - If the `data` property of the request configuration object contains an object, serialize it into
8448
8681
  * JSON format.
8449
8682
  *
8450
8683
  * Response transformations:
8451
8684
  *
8452
- * - if XSRF prefix is detected, strip it (see Security Considerations section below)
8453
- * - if json response is detected, deserialize it using a JSON parser
8685
+ * - If XSRF prefix is detected, strip it (see Security Considerations section below).
8686
+ * - If JSON response is detected, deserialize it using a JSON parser.
8454
8687
  *
8455
- * To override these transformation locally, specify transform functions as `transformRequest`
8456
- * and/or `transformResponse` properties of the config object. To globally override the default
8457
- * transforms, override the `$httpProvider.defaults.transformRequest` and
8458
- * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`.
8688
+ * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
8689
+ * `$httpProvider.defaults.transformResponse` properties. These properties are by default an
8690
+ * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
8691
+ * transformation chain. You can also decide to completely override any default transformations by assigning your
8692
+ * transformation functions to these properties directly without the array wrapper.
8693
+ *
8694
+ * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
8695
+ * `transformResponse` properties of the configuration object passed into `$http`.
8459
8696
  *
8460
8697
  *
8461
8698
  * # Caching
8462
8699
  *
8463
- * To enable caching set the configuration property `cache` to `true`. When the cache is
8700
+ * To enable caching, set the configuration property `cache` to `true`. When the cache is
8464
8701
  * enabled, `$http` stores the response from the server in local cache. Next time the
8465
8702
  * response is served from the cache without sending a request to the server.
8466
8703
  *
8467
8704
  * Note that even if the response is served from cache, delivery of the data is asynchronous in
8468
8705
  * the same way that real requests are.
8469
8706
  *
8470
- * If there are multiple GET requests for the same url that should be cached using the same
8707
+ * If there are multiple GET requests for the same URL that should be cached using the same
8471
8708
  * cache, but the cache is not populated yet, only one request to the server will be made and
8472
- * the remaining requests will be fulfilled using the response for the first request.
8709
+ * the remaining requests will be fulfilled using the response from the first request.
8473
8710
  *
8474
8711
  *
8475
8712
  * # Response interceptors
@@ -8521,7 +8758,7 @@ function $HttpProvider() {
8521
8758
  * When designing web applications, consider security threats from:
8522
8759
  *
8523
8760
  * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
8524
- * JSON Vulnerability}
8761
+ * JSON vulnerability}
8525
8762
  * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
8526
8763
  *
8527
8764
  * Both server and the client must cooperate in order to eliminate these threats. Angular comes
@@ -8531,8 +8768,8 @@ function $HttpProvider() {
8531
8768
  * ## JSON Vulnerability Protection
8532
8769
  *
8533
8770
  * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
8534
- * JSON Vulnerability} allows third party web-site to turn your JSON resource URL into
8535
- * {@link http://en.wikipedia.org/wiki/JSON#JSONP JSONP} request under some conditions. To
8771
+ * JSON vulnerability} allows third party website to turn your JSON resource URL into
8772
+ * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
8536
8773
  * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
8537
8774
  * Angular will automatically strip the prefix before processing it as JSON.
8538
8775
  *
@@ -8553,19 +8790,19 @@ function $HttpProvider() {
8553
8790
  * ## Cross Site Request Forgery (XSRF) Protection
8554
8791
  *
8555
8792
  * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
8556
- * an unauthorized site can gain your user's private data. Angular provides following mechanism
8793
+ * an unauthorized site can gain your user's private data. Angular provides a mechanism
8557
8794
  * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
8558
8795
  * called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
8559
8796
  * runs on your domain could read the cookie, your server can be assured that the XHR came from
8560
8797
  * JavaScript running on your domain.
8561
8798
  *
8562
8799
  * To take advantage of this, your server needs to set a token in a JavaScript readable session
8563
- * cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
8800
+ * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
8564
8801
  * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
8565
- * that only JavaScript running on your domain could have read the token. The token must be
8566
- * unique for each user and must be verifiable by the server (to prevent the JavaScript making
8802
+ * that only JavaScript running on your domain could have sent the request. The token must be
8803
+ * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
8567
8804
  * up its own tokens). We recommend that the token is a digest of your site's authentication
8568
- * cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
8805
+ * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
8569
8806
  *
8570
8807
  *
8571
8808
  * @param {object} config Object describing the request to be made and how it should be
@@ -8743,7 +8980,7 @@ function $HttpProvider() {
8743
8980
  * @methodOf ng.$http
8744
8981
  *
8745
8982
  * @description
8746
- * Shortcut method to perform `GET` request
8983
+ * Shortcut method to perform `GET` request.
8747
8984
  *
8748
8985
  * @param {string} url Relative or absolute URL specifying the destination of the request
8749
8986
  * @param {Object=} config Optional configuration object
@@ -8756,7 +8993,7 @@ function $HttpProvider() {
8756
8993
  * @methodOf ng.$http
8757
8994
  *
8758
8995
  * @description
8759
- * Shortcut method to perform `DELETE` request
8996
+ * Shortcut method to perform `DELETE` request.
8760
8997
  *
8761
8998
  * @param {string} url Relative or absolute URL specifying the destination of the request
8762
8999
  * @param {Object=} config Optional configuration object
@@ -8769,7 +9006,7 @@ function $HttpProvider() {
8769
9006
  * @methodOf ng.$http
8770
9007
  *
8771
9008
  * @description
8772
- * Shortcut method to perform `HEAD` request
9009
+ * Shortcut method to perform `HEAD` request.
8773
9010
  *
8774
9011
  * @param {string} url Relative or absolute URL specifying the destination of the request
8775
9012
  * @param {Object=} config Optional configuration object
@@ -8782,7 +9019,7 @@ function $HttpProvider() {
8782
9019
  * @methodOf ng.$http
8783
9020
  *
8784
9021
  * @description
8785
- * Shortcut method to perform `JSONP` request
9022
+ * Shortcut method to perform `JSONP` request.
8786
9023
  *
8787
9024
  * @param {string} url Relative or absolute URL specifying the destination of the request.
8788
9025
  * Should contain `JSON_CALLBACK` string.
@@ -8797,7 +9034,7 @@ function $HttpProvider() {
8797
9034
  * @methodOf ng.$http
8798
9035
  *
8799
9036
  * @description
8800
- * Shortcut method to perform `POST` request
9037
+ * Shortcut method to perform `POST` request.
8801
9038
  *
8802
9039
  * @param {string} url Relative or absolute URL specifying the destination of the request
8803
9040
  * @param {*} data Request content
@@ -8811,7 +9048,7 @@ function $HttpProvider() {
8811
9048
  * @methodOf ng.$http
8812
9049
  *
8813
9050
  * @description
8814
- * Shortcut method to perform `PUT` request
9051
+ * Shortcut method to perform `PUT` request.
8815
9052
  *
8816
9053
  * @param {string} url Relative or absolute URL specifying the destination of the request
8817
9054
  * @param {*} data Request content
@@ -8863,7 +9100,7 @@ function $HttpProvider() {
8863
9100
 
8864
9101
 
8865
9102
  /**
8866
- * Makes the request
9103
+ * Makes the request.
8867
9104
  *
8868
9105
  * !!! ACCESSES CLOSURE VARS:
8869
9106
  * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
@@ -8973,6 +9210,7 @@ function $HttpProvider() {
8973
9210
 
8974
9211
  }];
8975
9212
  }
9213
+
8976
9214
  var XHR = window.XMLHttpRequest || function() {
8977
9215
  try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
8978
9216
  try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
@@ -9040,8 +9278,30 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
9040
9278
  // always async
9041
9279
  xhr.onreadystatechange = function() {
9042
9280
  if (xhr.readyState == 4) {
9043
- completeRequest(
9044
- callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
9281
+ var responseHeaders = xhr.getAllResponseHeaders();
9282
+
9283
+ // TODO(vojta): remove once Firefox 21 gets released.
9284
+ // begin: workaround to overcome Firefox CORS http response headers bug
9285
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=608735
9286
+ // Firefox already patched in nightly. Should land in Firefox 21.
9287
+
9288
+ // CORS "simple response headers" http://www.w3.org/TR/cors/
9289
+ var value,
9290
+ simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
9291
+ "Expires", "Last-Modified", "Pragma"];
9292
+ if (!responseHeaders) {
9293
+ responseHeaders = "";
9294
+ forEach(simpleHeaders, function (header) {
9295
+ var value = xhr.getResponseHeader(header);
9296
+ if (value) {
9297
+ responseHeaders += header + ": " + value + "\n";
9298
+ }
9299
+ });
9300
+ }
9301
+ // end of the workaround.
9302
+
9303
+ completeRequest(callback, status || xhr.status, xhr.responseText,
9304
+ responseHeaders);
9045
9305
  }
9046
9306
  };
9047
9307
 
@@ -9187,17 +9447,17 @@ function $TimeoutProvider() {
9187
9447
  * block and delegates any exceptions to
9188
9448
  * {@link ng.$exceptionHandler $exceptionHandler} service.
9189
9449
  *
9190
- * The return value of registering a timeout function is a promise which will be resolved when
9450
+ * The return value of registering a timeout function is a promise, which will be resolved when
9191
9451
  * the timeout is reached and the timeout function is executed.
9192
9452
  *
9193
- * To cancel a the timeout request, call `$timeout.cancel(promise)`.
9453
+ * To cancel a timeout request, call `$timeout.cancel(promise)`.
9194
9454
  *
9195
9455
  * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
9196
9456
  * synchronously flush the queue of deferred functions.
9197
9457
  *
9198
- * @param {function()} fn A function, who's execution should be delayed.
9458
+ * @param {function()} fn A function, whose execution should be delayed.
9199
9459
  * @param {number=} [delay=0] Delay in milliseconds.
9200
- * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise
9460
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
9201
9461
  * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
9202
9462
  * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
9203
9463
  * promise will be resolved with is the return value of the `fn` function.
@@ -9237,7 +9497,7 @@ function $TimeoutProvider() {
9237
9497
  * @methodOf ng.$timeout
9238
9498
  *
9239
9499
  * @description
9240
- * Cancels a task associated with the `promise`. As a result of this the promise will be
9500
+ * Cancels a task associated with the `promise`. As a result of this, the promise will be
9241
9501
  * resolved with a rejection.
9242
9502
  *
9243
9503
  * @param {Promise=} promise Promise returned by the `$timeout` function.
@@ -9263,7 +9523,7 @@ function $TimeoutProvider() {
9263
9523
  *
9264
9524
  * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
9265
9525
  * achieve this a filter definition consists of a factory function which is annotated with dependencies and is
9266
- * responsible for creating a the filter function.
9526
+ * responsible for creating a filter function.
9267
9527
  *
9268
9528
  * <pre>
9269
9529
  * // Filter registration
@@ -9325,7 +9585,7 @@ function $TimeoutProvider() {
9325
9585
  *
9326
9586
  * The general syntax in templates is as follows:
9327
9587
  *
9328
- * {{ expression | [ filter_name ] }}
9588
+ * {{ expression [| filter_name[:parameter_value] ... ] }}
9329
9589
  *
9330
9590
  * @param {String} name Name of the filter function to retrieve
9331
9591
  * @return {Function} the filter function
@@ -9401,22 +9661,22 @@ function $FilterProvider($provide) {
9401
9661
 
9402
9662
  Search: <input ng-model="searchText">
9403
9663
  <table id="searchTextResults">
9404
- <tr><th>Name</th><th>Phone</th><tr>
9664
+ <tr><th>Name</th><th>Phone</th></tr>
9405
9665
  <tr ng-repeat="friend in friends | filter:searchText">
9406
9666
  <td>{{friend.name}}</td>
9407
9667
  <td>{{friend.phone}}</td>
9408
- <tr>
9668
+ </tr>
9409
9669
  </table>
9410
9670
  <hr>
9411
9671
  Any: <input ng-model="search.$"> <br>
9412
9672
  Name only <input ng-model="search.name"><br>
9413
- Phone only <input ng-model="search.phone"å><br>
9673
+ Phone only <input ng-model="search.phone"><br>
9414
9674
  <table id="searchObjResults">
9415
- <tr><th>Name</th><th>Phone</th><tr>
9675
+ <tr><th>Name</th><th>Phone</th></tr>
9416
9676
  <tr ng-repeat="friend in friends | filter:search">
9417
9677
  <td>{{friend.name}}</td>
9418
9678
  <td>{{friend.phone}}</td>
9419
- <tr>
9679
+ </tr>
9420
9680
  </table>
9421
9681
  </doc:source>
9422
9682
  <doc:scenario>
@@ -9440,7 +9700,7 @@ function $FilterProvider($provide) {
9440
9700
  */
9441
9701
  function filterFilter() {
9442
9702
  return function(array, expression) {
9443
- if (!(array instanceof Array)) return array;
9703
+ if (!isArray(array)) return array;
9444
9704
  var predicates = [];
9445
9705
  predicates.check = function(value) {
9446
9706
  for (var j = 0; j < predicates.length; j++) {
@@ -9689,7 +9949,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
9689
9949
  fraction += '0';
9690
9950
  }
9691
9951
 
9692
- if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize);
9952
+ if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
9693
9953
  }
9694
9954
 
9695
9955
  parts.push(isNegative ? pattern.negPre : pattern.posPre);
@@ -9713,6 +9973,7 @@ function padNumber(num, digits, trim) {
9713
9973
 
9714
9974
 
9715
9975
  function dateGetter(name, size, offset, trim) {
9976
+ offset = offset || 0;
9716
9977
  return function(date) {
9717
9978
  var value = date['get' + name]();
9718
9979
  if (offset > 0 || value > -offset)
@@ -9732,8 +9993,13 @@ function dateStrGetter(name, shortForm) {
9732
9993
  }
9733
9994
 
9734
9995
  function timeZoneGetter(date) {
9735
- var offset = date.getTimezoneOffset();
9736
- return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
9996
+ var zone = -1 * date.getTimezoneOffset();
9997
+ var paddedZone = (zone >= 0) ? "+" : "";
9998
+
9999
+ paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
10000
+ padNumber(Math.abs(zone % 60), 2);
10001
+
10002
+ return paddedZone;
9737
10003
  }
9738
10004
 
9739
10005
  function ampmGetter(date, formats) {
@@ -9797,7 +10063,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9797
10063
  * * `'ss'`: Second in minute, padded (00-59)
9798
10064
  * * `'s'`: Second in minute (0-59)
9799
10065
  * * `'a'`: am/pm marker
9800
- * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200)
10066
+ * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
9801
10067
  *
9802
10068
  * `format` string can also be one of the following predefined
9803
10069
  * {@link guide/i18n localizable formats}:
@@ -9818,8 +10084,9 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9818
10084
  * (e.g. `"h o''clock"`).
9819
10085
  *
9820
10086
  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
9821
- * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
9822
- * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
10087
+ * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
10088
+ * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
10089
+ * specified in the string input, the time is considered to be in the local timezone.
9823
10090
  * @param {string=} format Formatting rules (see Description). If not specified,
9824
10091
  * `mediumDate` is used.
9825
10092
  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
@@ -9839,7 +10106,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9839
10106
  expect(binding("1288323623006 | date:'medium'")).
9840
10107
  toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
9841
10108
  expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
9842
- toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
10109
+ toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
9843
10110
  expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
9844
10111
  toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
9845
10112
  });
@@ -10108,12 +10375,12 @@ function limitToFilter(){
10108
10375
  (<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
10109
10376
  <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
10110
10377
  <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
10111
- <tr>
10378
+ </tr>
10112
10379
  <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
10113
10380
  <td>{{friend.name}}</td>
10114
10381
  <td>{{friend.phone}}</td>
10115
10382
  <td>{{friend.age}}</td>
10116
- <tr>
10383
+ </tr>
10117
10384
  </table>
10118
10385
  </div>
10119
10386
  </doc:source>
@@ -10145,7 +10412,7 @@ function limitToFilter(){
10145
10412
  orderByFilter.$inject = ['$parse'];
10146
10413
  function orderByFilter($parse){
10147
10414
  return function(array, sortPredicate, reverseOrder) {
10148
- if (!(array instanceof Array)) return array;
10415
+ if (!isArray(array)) return array;
10149
10416
  if (!sortPredicate) return array;
10150
10417
  sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
10151
10418
  sortPredicate = map(sortPredicate, function(predicate){
@@ -10213,15 +10480,25 @@ function ngDirective(directive) {
10213
10480
  *
10214
10481
  * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
10215
10482
  * without changing the location or causing page reloads, e.g.:
10216
- * <a href="" ng-click="model.$save()">Save</a>
10483
+ * `<a href="" ng-click="model.$save()">Save</a>`
10217
10484
  */
10218
10485
  var htmlAnchorDirective = valueFn({
10219
10486
  restrict: 'E',
10220
10487
  compile: function(element, attr) {
10221
- // turn <a href ng-click="..">link</a> into a link in IE
10222
- // but only if it doesn't have name attribute, in which case it's an anchor
10223
- if (!attr.href) {
10224
- attr.$set('href', '');
10488
+
10489
+ if (msie <= 8) {
10490
+
10491
+ // turn <a href ng-click="..">link</a> into a stylable link in IE
10492
+ // but only if it doesn't have name attribute, in which case it's an anchor
10493
+ if (!attr.href && !attr.name) {
10494
+ attr.$set('href', '');
10495
+ }
10496
+
10497
+ // add a comment node to anchors to workaround IE bug that causes element content to be reset
10498
+ // to new attribute content if attribute is updated with value containing @ and element also
10499
+ // contains value with @
10500
+ // see issue #1949
10501
+ element.append(document.createComment('IE fix'));
10225
10502
  }
10226
10503
 
10227
10504
  return function(scope, element) {
@@ -10229,7 +10506,6 @@ var htmlAnchorDirective = valueFn({
10229
10506
  // if we have no href url, then don't navigate anywhere.
10230
10507
  if (!element.attr('href')) {
10231
10508
  event.preventDefault();
10232
- return false; // Needed for opera
10233
10509
  }
10234
10510
  });
10235
10511
  }
@@ -10302,7 +10578,7 @@ var htmlAnchorDirective = valueFn({
10302
10578
  it('should execute ng-click but not reload when no href but name specified', function() {
10303
10579
  element('#link-5').click();
10304
10580
  expect(input('value').val()).toEqual('5');
10305
- expect(element('#link-5').attr('href')).toBe('');
10581
+ expect(element('#link-5').attr('href')).toBe(undefined);
10306
10582
  });
10307
10583
 
10308
10584
  it('should only change url when only ng-href', function() {
@@ -10545,8 +10821,9 @@ forEach(['src', 'href'], function(attrName) {
10545
10821
 
10546
10822
  // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
10547
10823
  // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
10548
- // to set the property as well to achieve the desired effect
10549
- if (msie) element.prop(attrName, value);
10824
+ // to set the property as well to achieve the desired effect.
10825
+ // we use attr[attrName] value since $set can sanitize the url.
10826
+ if (msie) element.prop(attrName, attr[attrName]);
10550
10827
  });
10551
10828
  }
10552
10829
  };
@@ -10566,13 +10843,13 @@ var nullFormCtrl = {
10566
10843
  *
10567
10844
  * @property {boolean} $pristine True if user has not interacted with the form yet.
10568
10845
  * @property {boolean} $dirty True if user has already interacted with the form.
10569
- * @property {boolean} $valid True if all of the containg forms and controls are valid.
10846
+ * @property {boolean} $valid True if all of the containing forms and controls are valid.
10570
10847
  * @property {boolean} $invalid True if at least one containing control or form is invalid.
10571
10848
  *
10572
10849
  * @property {Object} $error Is an object hash, containing references to all invalid controls or
10573
10850
  * forms, where:
10574
10851
  *
10575
- * - keys are validation tokens (error names) — such as `REQUIRED`, `URL` or `EMAIL`),
10852
+ * - keys are validation tokens (error names) — such as `required`, `url` or `email`),
10576
10853
  * - values are arrays of controls or forms that are invalid with given error.
10577
10854
  *
10578
10855
  * @description
@@ -10685,7 +10962,7 @@ function FormController(element, attrs) {
10685
10962
  * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
10686
10963
  * sub-group of controls needs to be determined.
10687
10964
  *
10688
- * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
10965
+ * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into
10689
10966
  * related scope, under this name.
10690
10967
  *
10691
10968
  */
@@ -10758,12 +11035,12 @@ function FormController(element, attrs) {
10758
11035
  </script>
10759
11036
  <form name="myForm" ng-controller="Ctrl">
10760
11037
  userType: <input name="input" ng-model="userType" required>
10761
- <span class="error" ng-show="myForm.input.$error.REQUIRED">Required!</span><br>
11038
+ <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
10762
11039
  <tt>userType = {{userType}}</tt><br>
10763
11040
  <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
10764
11041
  <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
10765
11042
  <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
10766
- <tt>myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}</tt><br>
11043
+ <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
10767
11044
  </form>
10768
11045
  </doc:source>
10769
11046
  <doc:scenario>
@@ -10925,8 +11202,8 @@ var inputType = {
10925
11202
  *
10926
11203
  * @param {string} ngModel Assignable angular expression to data-bind to.
10927
11204
  * @param {string=} name Property name of the form under which the control is published.
10928
- * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
10929
- * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
11205
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
11206
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
10930
11207
  * @param {string=} required Sets `required` validation error key if the value is not entered.
10931
11208
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
10932
11209
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -11238,6 +11515,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11238
11515
  } else {
11239
11516
  var timeout;
11240
11517
 
11518
+ var deferListener = function() {
11519
+ if (!timeout) {
11520
+ timeout = $browser.defer(function() {
11521
+ listener();
11522
+ timeout = null;
11523
+ });
11524
+ }
11525
+ };
11526
+
11241
11527
  element.bind('keydown', function(event) {
11242
11528
  var key = event.keyCode;
11243
11529
 
@@ -11245,16 +11531,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
11245
11531
  // command modifiers arrows
11246
11532
  if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
11247
11533
 
11248
- if (!timeout) {
11249
- timeout = $browser.defer(function() {
11250
- listener();
11251
- timeout = null;
11252
- });
11253
- }
11534
+ deferListener();
11254
11535
  });
11255
11536
 
11256
11537
  // if user paste into input using mouse, we need "change" event to catch it
11257
11538
  element.bind('change', listener);
11539
+
11540
+ // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
11541
+ if ($sniffer.hasEvent('paste')) {
11542
+ element.bind('paste cut', deferListener);
11543
+ }
11258
11544
  }
11259
11545
 
11260
11546
 
@@ -11553,7 +11839,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
11553
11839
  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
11554
11840
  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
11555
11841
  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
11556
- <tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br>
11842
+ <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
11557
11843
  <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
11558
11844
  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
11559
11845
  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
@@ -11816,7 +12102,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
11816
12102
  * For example {@link ng.directive:input input} or
11817
12103
  * {@link ng.directive:select select} directives call it.
11818
12104
  *
11819
- * It internally calls all `formatters` and if resulted value is valid, updates the model and
12105
+ * It internally calls all `parsers` and if resulted value is valid, updates the model and
11820
12106
  * calls all registered change listeners.
11821
12107
  *
11822
12108
  * @param {string} value Value from the view.
@@ -12122,7 +12408,7 @@ var ngValueDirective = function() {
12122
12408
  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
12123
12409
  * `{{ expression }}` which is similar but less verbose.
12124
12410
  *
12125
- * Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when
12411
+ * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
12126
12412
  * it's desirable to put bindings into template that is momentarily displayed by the browser in its
12127
12413
  * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
12128
12414
  * bindings invisible to the user while the page is loading.
@@ -12251,6 +12537,7 @@ var ngBindHtmlUnsafeDirective = [function() {
12251
12537
  function classDirective(name, selector) {
12252
12538
  name = 'ngClass' + name;
12253
12539
  return ngDirective(function(scope, element, attr) {
12540
+ var oldVal = undefined;
12254
12541
 
12255
12542
  scope.$watch(attr[name], ngClassWatchAction, true);
12256
12543
 
@@ -12262,9 +12549,9 @@ function classDirective(name, selector) {
12262
12549
 
12263
12550
  if (name !== 'ngClass') {
12264
12551
  scope.$watch('$index', function($index, old$index) {
12265
- var mod = $index % 2;
12266
- if (mod !== old$index % 2) {
12267
- if (mod == selector) {
12552
+ var mod = $index & 1;
12553
+ if (mod !== old$index & 1) {
12554
+ if (mod === selector) {
12268
12555
  addClass(scope.$eval(attr[name]));
12269
12556
  } else {
12270
12557
  removeClass(scope.$eval(attr[name]));
@@ -12274,13 +12561,14 @@ function classDirective(name, selector) {
12274
12561
  }
12275
12562
 
12276
12563
 
12277
- function ngClassWatchAction(newVal, oldVal) {
12564
+ function ngClassWatchAction(newVal) {
12278
12565
  if (selector === true || scope.$index % 2 === selector) {
12279
- if (oldVal && (newVal !== oldVal)) {
12566
+ if (oldVal && !equals(newVal,oldVal)) {
12280
12567
  removeClass(oldVal);
12281
12568
  }
12282
12569
  addClass(newVal);
12283
12570
  }
12571
+ oldVal = copy(newVal);
12284
12572
  }
12285
12573
 
12286
12574
 
@@ -12313,7 +12601,7 @@ function classDirective(name, selector) {
12313
12601
  *
12314
12602
  * The directive won't add duplicate classes if a particular class was already set.
12315
12603
  *
12316
- * When the expression changes, the previously added classes are removed and only then the classes
12604
+ * When the expression changes, the previously added classes are removed and only then the
12317
12605
  * new classes are added.
12318
12606
  *
12319
12607
  * @element ANY
@@ -12406,7 +12694,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
12406
12694
  * @name ng.directive:ngClassEven
12407
12695
  *
12408
12696
  * @description
12409
- * The `ngClassOdd` and `ngClassEven` works exactly as
12697
+ * The `ngClassOdd` and `ngClassEven` directives work exactly as
12410
12698
  * {@link ng.directive:ngClass ngClass}, except it works in
12411
12699
  * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
12412
12700
  *
@@ -12464,7 +12752,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
12464
12752
  * `angular.min.js` files. Following is the css rule:
12465
12753
  *
12466
12754
  * <pre>
12467
- * [ng\:cloak], [ng-cloak], .ng-cloak {
12755
+ * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
12468
12756
  * display: none;
12469
12757
  * }
12470
12758
  * </pre>
@@ -12523,8 +12811,7 @@ var ngCloakDirective = ngDirective({
12523
12811
  * * Controller — The `ngController` directive specifies a Controller class; the class has
12524
12812
  * methods that typically express the business logic behind the application.
12525
12813
  *
12526
- * Note that an alternative way to define controllers is via the `{@link ng.$route}`
12527
- * service.
12814
+ * Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
12528
12815
  *
12529
12816
  * @element ANY
12530
12817
  * @scope
@@ -12615,16 +12902,32 @@ var ngControllerDirective = [function() {
12615
12902
  * @name ng.directive:ngCsp
12616
12903
  * @priority 1000
12617
12904
  *
12905
+ * @element html
12618
12906
  * @description
12619
12907
  * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
12620
- * This directive should be used on the root element of the application (typically the `<html>`
12621
- * element or other element with the {@link ng.directive:ngApp ngApp}
12622
- * directive).
12623
- *
12624
- * If enabled the performance of template expression evaluator will suffer slightly, so don't enable
12625
- * this mode unless you need it.
12626
- *
12627
- * @element html
12908
+ *
12909
+ * This is necessary when developing things like Google Chrome Extensions.
12910
+ *
12911
+ * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
12912
+ * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
12913
+ * any of these restrictions.
12914
+ *
12915
+ * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
12916
+ * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
12917
+ * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
12918
+ * be raised.
12919
+ *
12920
+ * In order to use this feature put `ngCsp` directive on the root element of the application.
12921
+ *
12922
+ * @example
12923
+ * This example shows how to apply the `ngCsp` directive to the `html` tag.
12924
+ <pre>
12925
+ <!doctype html>
12926
+ <html ng-app ng-csp>
12927
+ ...
12928
+ ...
12929
+ </html>
12930
+ </pre>
12628
12931
  */
12629
12932
 
12630
12933
  var ngCspDirective = ['$sniffer', function($sniffer) {
@@ -13249,7 +13552,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
13249
13552
  if (!isNaN(value)) {
13250
13553
  //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
13251
13554
  //check it against pluralization rules in $locale service
13252
- if (!whens[value]) value = $locale.pluralCat(value - offset);
13555
+ if (!(value in whens)) value = $locale.pluralCat(value - offset);
13253
13556
  return whensExpFns[value](scope, element, true);
13254
13557
  } else {
13255
13558
  return '';
@@ -13353,14 +13656,17 @@ var ngRepeatDirective = ngDirective({
13353
13656
  scope.$watch(function ngRepeatWatch(scope){
13354
13657
  var index, length,
13355
13658
  collection = scope.$eval(rhs),
13356
- collectionLength = size(collection, true),
13357
- childScope,
13659
+ cursor = iterStartElement, // current position of the node
13358
13660
  // Same as lastOrder but it has the current state. It will become the
13359
13661
  // lastOrder on the next iteration.
13360
13662
  nextOrder = new HashQueueMap(),
13663
+ arrayBound,
13664
+ childScope,
13361
13665
  key, value, // key/value of iteration
13362
- array, last, // last object information {scope, element, index}
13363
- cursor = iterStartElement; // current position of the node
13666
+ array,
13667
+ last; // last object information {scope, element, index}
13668
+
13669
+
13364
13670
 
13365
13671
  if (!isArray(collection)) {
13366
13672
  // if object, extract keys, sort them and use to determine order of iteration over obj props
@@ -13375,6 +13681,8 @@ var ngRepeatDirective = ngDirective({
13375
13681
  array = collection || [];
13376
13682
  }
13377
13683
 
13684
+ arrayBound = array.length-1;
13685
+
13378
13686
  // we are not using forEach for perf reasons (trying to avoid #call)
13379
13687
  for (index = 0, length = array.length; index < length; index++) {
13380
13688
  key = (collection === array) ? index : array[index];
@@ -13410,7 +13718,7 @@ var ngRepeatDirective = ngDirective({
13410
13718
  childScope.$index = index;
13411
13719
 
13412
13720
  childScope.$first = (index === 0);
13413
- childScope.$last = (index === (collectionLength - 1));
13721
+ childScope.$last = (index === arrayBound);
13414
13722
  childScope.$middle = !(childScope.$first || childScope.$last);
13415
13723
 
13416
13724
  if (!last) {
@@ -13577,11 +13885,13 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
13577
13885
  * @description
13578
13886
  * Conditionally change the DOM structure.
13579
13887
  *
13580
- * @usageContent
13581
- * <ANY ng-switch-when="matchValue1">...</ANY>
13888
+ * @usage
13889
+ * <ANY ng-switch="expression">
13890
+ * <ANY ng-switch-when="matchValue1">...</ANY>
13582
13891
  * <ANY ng-switch-when="matchValue2">...</ANY>
13583
13892
  * ...
13584
13893
  * <ANY ng-switch-default>...</ANY>
13894
+ * </ANY>
13585
13895
  *
13586
13896
  * @scope
13587
13897
  * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
@@ -13631,52 +13941,54 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
13631
13941
  var NG_SWITCH = 'ng-switch';
13632
13942
  var ngSwitchDirective = valueFn({
13633
13943
  restrict: 'EA',
13634
- compile: function(element, attr) {
13944
+ require: 'ngSwitch',
13945
+ // asks for $scope to fool the BC controller module
13946
+ controller: ['$scope', function ngSwitchController() {
13947
+ this.cases = {};
13948
+ }],
13949
+ link: function(scope, element, attr, ctrl) {
13635
13950
  var watchExpr = attr.ngSwitch || attr.on,
13636
- cases = {};
13637
-
13638
- element.data(NG_SWITCH, cases);
13639
- return function(scope, element){
13640
- var selectedTransclude,
13641
- selectedElement,
13642
- selectedScope;
13643
-
13644
- scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
13645
- if (selectedElement) {
13646
- selectedScope.$destroy();
13647
- selectedElement.remove();
13648
- selectedElement = selectedScope = null;
13649
- }
13650
- if ((selectedTransclude = cases['!' + value] || cases['?'])) {
13651
- scope.$eval(attr.change);
13652
- selectedScope = scope.$new();
13653
- selectedTransclude(selectedScope, function(caseElement) {
13654
- selectedElement = caseElement;
13655
- element.append(caseElement);
13656
- });
13657
- }
13658
- });
13659
- };
13951
+ selectedTransclude,
13952
+ selectedElement,
13953
+ selectedScope;
13954
+
13955
+ scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
13956
+ if (selectedElement) {
13957
+ selectedScope.$destroy();
13958
+ selectedElement.remove();
13959
+ selectedElement = selectedScope = null;
13960
+ }
13961
+ if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
13962
+ scope.$eval(attr.change);
13963
+ selectedScope = scope.$new();
13964
+ selectedTransclude(selectedScope, function(caseElement) {
13965
+ selectedElement = caseElement;
13966
+ element.append(caseElement);
13967
+ });
13968
+ }
13969
+ });
13660
13970
  }
13661
13971
  });
13662
13972
 
13663
13973
  var ngSwitchWhenDirective = ngDirective({
13664
13974
  transclude: 'element',
13665
13975
  priority: 500,
13976
+ require: '^ngSwitch',
13666
13977
  compile: function(element, attrs, transclude) {
13667
- var cases = element.inheritedData(NG_SWITCH);
13668
- assertArg(cases);
13669
- cases['!' + attrs.ngSwitchWhen] = transclude;
13978
+ return function(scope, element, attr, ctrl) {
13979
+ ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
13980
+ };
13670
13981
  }
13671
13982
  });
13672
13983
 
13673
13984
  var ngSwitchDefaultDirective = ngDirective({
13674
13985
  transclude: 'element',
13675
13986
  priority: 500,
13987
+ require: '^ngSwitch',
13676
13988
  compile: function(element, attrs, transclude) {
13677
- var cases = element.inheritedData(NG_SWITCH);
13678
- assertArg(cases);
13679
- cases['?'] = transclude;
13989
+ return function(scope, element, attr, ctrl) {
13990
+ ctrl.cases['?'] = transclude;
13991
+ };
13680
13992
  }
13681
13993
  });
13682
13994
 
@@ -13765,7 +14077,7 @@ var ngTranscludeDirective = ngDirective({
13765
14077
  <hr />
13766
14078
 
13767
14079
  <pre>$location.path() = {{$location.path()}}</pre>
13768
- <pre>$route.current.template = {{$route.current.template}}</pre>
14080
+ <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
13769
14081
  <pre>$route.current.params = {{$route.current.params}}</pre>
13770
14082
  <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
13771
14083
  <pre>$routeParams = {{$routeParams}}</pre>
@@ -13884,7 +14196,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
13884
14196
  if (current.controller) {
13885
14197
  locals.$scope = lastScope;
13886
14198
  controller = $controller(current.controller, locals);
13887
- element.contents().data('$ngControllerController', controller);
14199
+ element.children().data('$ngControllerController', controller);
13888
14200
  }
13889
14201
 
13890
14202
  link(lastScope);
@@ -13973,7 +14285,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
13973
14285
  * `select` model to be bound to a non-string value. This is because an option element can currently
13974
14286
  * be bound to string values only.
13975
14287
  *
13976
- * @param {string} name assignable expression to data-bind to.
14288
+ * @param {string} ngModel Assignable angular expression to data-bind to.
14289
+ * @param {string=} name Property name of the form under which the control is published.
13977
14290
  * @param {string=} required The control is considered valid only if value is entered.
13978
14291
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
13979
14292
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -14068,7 +14381,7 @@ var scriptDirective = ['$templateCache', function($templateCache) {
14068
14381
 
14069
14382
  var ngOptionsDirective = valueFn({ terminal: true });
14070
14383
  var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14071
- //00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
14384
+ //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
14072
14385
  var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
14073
14386
  nullModelCtrl = {$setViewValue: noop};
14074
14387
 
@@ -14211,7 +14524,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14211
14524
  var lastView;
14212
14525
  ctrl.$render = function() {
14213
14526
  var items = new HashMap(ctrl.$viewValue);
14214
- forEach(selectElement.children(), function(option) {
14527
+ forEach(selectElement.find('option'), function(option) {
14215
14528
  option.selected = isDefined(items.get(option.value));
14216
14529
  });
14217
14530
  };
@@ -14228,7 +14541,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14228
14541
  selectElement.bind('change', function() {
14229
14542
  scope.$apply(function() {
14230
14543
  var array = [];
14231
- forEach(selectElement.children(), function(option) {
14544
+ forEach(selectElement.find('option'), function(option) {
14232
14545
  if (option.selected) {
14233
14546
  array.push(option.value);
14234
14547
  }
@@ -14340,10 +14653,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14340
14653
 
14341
14654
  if (multiple) {
14342
14655
  selectedSet = new HashMap(modelValue);
14343
- } else if (modelValue === null || nullOption) {
14344
- // if we are not multiselect, and we are null then we have to add the nullOption
14345
- optionGroups[''].push({selected:modelValue === null, id:'', label:''});
14346
- selectedSet = true;
14347
14656
  }
14348
14657
 
14349
14658
  // We now build up the list of options we need (we merge later)
@@ -14368,9 +14677,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14368
14677
  selected: selected // determine if we should be selected
14369
14678
  });
14370
14679
  }
14371
- if (!multiple && !selectedSet) {
14372
- // nothing was selected, we have to insert the undefined item
14373
- optionGroups[''].unshift({id:'?', label:'', selected:true});
14680
+ if (!multiple) {
14681
+ if (nullOption || modelValue === null) {
14682
+ // insert null option if we have a placeholder, or the model is null
14683
+ optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
14684
+ } else if (!selectedSet) {
14685
+ // option could not be found, we have to insert the undefined item
14686
+ optionGroups[''].unshift({id:'?', label:'', selected:true});
14687
+ }
14374
14688
  }
14375
14689
 
14376
14690
  // Now we need to update the list of DOM nodes to match the optionGroups we computed above
@@ -14414,7 +14728,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
14414
14728
  if (existingOption.id !== option.id) {
14415
14729
  lastElement.val(existingOption.id = option.id);
14416
14730
  }
14417
- if (existingOption.element.selected !== option.selected) {
14731
+ // lastElement.prop('selected') provided by jQuery has side-effects
14732
+ if (lastElement[0].selected !== option.selected) {
14418
14733
  lastElement.prop('selected', (existingOption.selected = option.selected));
14419
14734
  }
14420
14735
  } else {
@@ -14517,6 +14832,7 @@ var styleDirective = valueFn({
14517
14832
  restrict: 'E',
14518
14833
  terminal: true
14519
14834
  });
14835
+
14520
14836
  //try to bind to jquery now so that one can write angular.element().read()
14521
14837
  //but we will rebind on bootstrap again.
14522
14838
  bindJQuery();
@@ -14528,4 +14844,4 @@ var styleDirective = valueFn({
14528
14844
  });
14529
14845
 
14530
14846
  })(window, document);
14531
- angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');
14847
+ angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');