mongo_browser 0.2.0.rc2 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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>');