mongo_browser 0.2.0.rc2 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +1 -1
  3. data/.travis.yml +6 -1
  4. data/CHANGELOG.md +15 -0
  5. data/{grunt.js → Gruntfile.js} +10 -8
  6. data/Procfile +1 -0
  7. data/README.md +44 -2
  8. data/Rakefile +1 -12
  9. data/app/assets/images/ajax-loader.gif +0 -0
  10. data/app/assets/javascripts/app/controllers/{breadcrumbs.js.coffee → breadcrumbs_controller.js.coffee} +4 -7
  11. data/app/assets/javascripts/app/controllers/collections/index_controller.js.coffee +38 -0
  12. data/app/assets/javascripts/app/controllers/collections/stats_controller.js.coffee +17 -0
  13. data/app/assets/javascripts/app/controllers/{databases.js.coffee → databases/index_controller.js.coffee} +11 -15
  14. data/app/assets/javascripts/app/controllers/databases/stats_controller.js.coffee +15 -0
  15. data/app/assets/javascripts/app/controllers/documents/index_controller.js.coffee +54 -0
  16. data/app/assets/javascripts/app/controllers/documents/show_controller.js.coffee +38 -0
  17. data/app/assets/javascripts/app/controllers/main_controller.js.coffee +15 -0
  18. data/app/assets/javascripts/app/controllers/servers/show_controller.js.coffee +17 -0
  19. data/app/assets/javascripts/app/directives.js.coffee +23 -0
  20. data/app/assets/javascripts/app/filters.js.coffee +14 -1
  21. data/app/assets/javascripts/app/modules/alerts.js.coffee +58 -0
  22. data/app/assets/javascripts/app/modules/pager.js.coffee +2 -2
  23. data/app/assets/javascripts/app/modules/spinner.js.coffee +29 -0
  24. data/app/assets/javascripts/app/modules/table_filter.js.coffee +4 -4
  25. data/app/assets/javascripts/app/resources.js.coffee +14 -8
  26. data/app/assets/javascripts/app/services.js.coffee +11 -33
  27. data/app/assets/javascripts/application.js.coffee +62 -34
  28. data/app/assets/javascripts/application.test.js.coffee +5 -0
  29. data/app/assets/javascripts/compiled_templates.js.coffee +1 -0
  30. data/app/assets/javascripts/vendor.js.coffee +0 -1
  31. data/app/assets/stylesheets/application.css.scss +36 -18
  32. data/{public/index.html → app/views/index.erb} +8 -8
  33. data/bin/mongo_browser +2 -13
  34. data/config.ru +3 -1
  35. data/lib/mongo_browser.rb +1 -0
  36. data/lib/mongo_browser/api.rb +11 -0
  37. data/lib/mongo_browser/api/collections.rb +34 -0
  38. data/lib/mongo_browser/api/databases.rb +32 -0
  39. data/lib/mongo_browser/api/documents.rb +37 -0
  40. data/lib/mongo_browser/api/mongo.rb +41 -0
  41. data/lib/mongo_browser/application.rb +8 -174
  42. data/lib/mongo_browser/application/development.rb +32 -0
  43. data/lib/mongo_browser/cli.rb +48 -0
  44. data/lib/mongo_browser/entities.rb +43 -0
  45. data/lib/mongo_browser/models/collection.rb +7 -12
  46. data/lib/mongo_browser/models/document.rb +5 -1
  47. data/lib/mongo_browser/models/pager.rb +22 -9
  48. data/lib/mongo_browser/version.rb +1 -1
  49. data/mongo_browser.gemspec +22 -15
  50. data/package.json +30 -0
  51. data/public/ng/templates/alerts.html +6 -0
  52. data/public/ng/templates/collections/index.html +39 -0
  53. data/public/ng/templates/collections/stats.html +18 -0
  54. data/public/ng/templates/databases/index.html +35 -0
  55. data/public/ng/templates/databases/stats.html +18 -0
  56. data/public/ng/templates/documents/index.html +40 -0
  57. data/public/ng/templates/documents/show.html +17 -0
  58. data/{app/assets → public/ng}/templates/pager.html +0 -0
  59. data/{app/assets/templates/server_info.html → public/ng/templates/server/show.html} +1 -1
  60. data/{app/assets → public/ng}/templates/table_filter.html +0 -0
  61. data/script/ci_all +5 -1
  62. data/script/ci_e2e +5 -2
  63. data/script/ci_javascripts +1 -1
  64. data/script/ci_rspec +1 -1
  65. data/spec/javascripts/app/controllers/{breadcrumbs_spec.js.coffee → breadcrumbs_controller_spec.js.coffee} +1 -1
  66. data/spec/javascripts/app/controllers/collections/index_controller_spec.js.coffee +95 -0
  67. data/spec/javascripts/app/controllers/collections/stats_controller_spec.js.coffee +34 -0
  68. data/spec/javascripts/app/controllers/databases/index_controller_spec.js.coffee +93 -0
  69. data/spec/javascripts/app/controllers/databases/stats_controller_spec.js.coffee +30 -0
  70. data/spec/javascripts/app/controllers/documents/index_controller_spec.js.coffee +108 -0
  71. data/spec/javascripts/app/controllers/documents/show_controller_spec.js.coffee +94 -0
  72. data/spec/javascripts/app/controllers/{main_spec.js.coffee → main_controller_spec.js.coffee} +2 -2
  73. data/spec/javascripts/app/controllers/{server_info_spec.js.coffee → server/show_controller_spec.js.coffee} +5 -6
  74. data/spec/javascripts/app/directives_spec.js.coffee +108 -24
  75. data/spec/javascripts/app/filters_spec.js.coffee +31 -5
  76. data/spec/javascripts/app/modules/alerts_spec.js.coffee +138 -0
  77. data/spec/javascripts/app/modules/dialogs_spec.js.coffee +1 -2
  78. data/spec/javascripts/app/modules/pager_spec.js.coffee +0 -1
  79. data/spec/javascripts/app/modules/spinner_spec.js.coffee +65 -0
  80. data/spec/javascripts/app/modules/table_filter_spec.js.coffee +9 -9
  81. data/spec/javascripts/app/resources_spec.js.coffee +99 -0
  82. data/spec/javascripts/app/services_spec.js.coffee +31 -71
  83. data/spec/javascripts/config/{testacular-e2e.conf.js → karma-e2e.conf.js} +1 -1
  84. data/spec/javascripts/config/{testacular.conf.js → karma.conf.js} +2 -3
  85. data/spec/javascripts/e2e/collection_stats_scenario.js.coffee +12 -0
  86. data/spec/javascripts/e2e/collections_scenario.js.coffee +59 -20
  87. data/spec/javascripts/e2e/database_stats_scenario.js.coffee +11 -0
  88. data/spec/javascripts/e2e/databases_scenario.js.coffee +37 -36
  89. data/spec/javascripts/e2e/document_show_scenario.js.coffee +31 -0
  90. data/spec/javascripts/e2e/documents_pagination_scenario.js.coffee +33 -0
  91. data/spec/javascripts/e2e/documents_scenario.js.coffee +43 -4
  92. data/spec/javascripts/e2e/server_info_scenario.js.coffee +8 -2
  93. data/spec/javascripts/helpers/mocks.js.coffee +2 -0
  94. data/spec/javascripts/helpers_e2e/dsl.js.coffee +20 -0
  95. data/spec/javascripts/lib/angular-mocks.js +64 -16
  96. data/spec/javascripts/lib/angular-scenario.js +724 -561
  97. data/spec/javascripts/runner.html +5 -5
  98. data/spec/lib/api/collections_spec.rb +62 -0
  99. data/spec/lib/api/databases_spec.rb +58 -0
  100. data/spec/lib/api/documents_spec.rb +135 -0
  101. data/spec/lib/api/mongo_spec.rb +27 -0
  102. data/spec/lib/cli_spec.rb +19 -0
  103. data/spec/lib/entities_spec.rb +39 -0
  104. data/spec/lib/models/collection_spec.rb +16 -10
  105. data/spec/lib/models/database_spec.rb +4 -4
  106. data/spec/lib/models/document_spec.rb +5 -5
  107. data/spec/lib/models/pager_spec.rb +20 -11
  108. data/spec/spec_helper.rb +7 -15
  109. data/spec/support/api_example_group.rb +45 -0
  110. data/spec/support/fixtures.rb +10 -6
  111. data/spec/support/matchers/expose.rb +18 -0
  112. data/vendor/assets/javascripts/angular/angular-bootstrap.js +1 -1
  113. data/vendor/assets/javascripts/angular/angular-resource.js +78 -56
  114. data/vendor/assets/javascripts/angular/angular-sanitize.js +3 -1
  115. data/vendor/assets/javascripts/angular/angular.js +720 -404
  116. metadata +323 -183
  117. data/app/assets/javascripts/app.js.coffee +0 -8
  118. data/app/assets/javascripts/app/controllers.js.coffee +0 -2
  119. data/app/assets/javascripts/app/controllers/alerts.js.coffee +0 -12
  120. data/app/assets/javascripts/app/controllers/collections.js.coffee +0 -40
  121. data/app/assets/javascripts/app/controllers/documents.js.coffee +0 -49
  122. data/app/assets/javascripts/app/controllers/main.js.coffee +0 -10
  123. data/app/assets/javascripts/app/controllers/server_info.js.coffee +0 -14
  124. data/app/assets/javascripts/templates.js.coffee +0 -1
  125. data/app/assets/javascripts/templates/.gitkeep +0 -0
  126. data/app/assets/templates/collections.html +0 -53
  127. data/app/assets/templates/databases.html +0 -32
  128. data/app/assets/templates/documents.html +0 -45
  129. data/config-e2e.ru +0 -20
  130. data/spec/features/collections_list_spec.rb +0 -64
  131. data/spec/features/documents_list_spec.rb +0 -139
  132. data/spec/features/server_info_spec.rb +0 -23
  133. data/spec/javascripts/app/controllers/alerts_spec.js.coffee +0 -36
  134. data/spec/javascripts/app/controllers/collections_spec.js.coffee +0 -78
  135. data/spec/javascripts/app/controllers/databases_spec.js.coffee +0 -55
  136. data/spec/javascripts/app/controllers/documents_spec.js.coffee +0 -62
  137. data/spec/javascripts/helpers_e2e/app_element.js.coffee +0 -6
  138. data/spec/support/feature_example_group.rb +0 -53
  139. data/spec/support/matchers/have_flash_message.rb +0 -16
  140. data/spec/support/mongod.rb +0 -91
  141. data/spec/support/mongodb.conf +0 -47
@@ -9403,8 +9403,9 @@ if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
9403
9403
 
9404
9404
 
9405
9405
  })( window );
9406
+
9406
9407
  /**
9407
- * @license AngularJS v1.0.3
9408
+ * @license AngularJS v1.0.7
9408
9409
  * (c) 2010-2012 Google, Inc. http://angularjs.org
9409
9410
  * License: MIT
9410
9411
  */
@@ -9439,12 +9440,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
9439
9440
 
9440
9441
  var manualLowercase = function(s) {
9441
9442
  return isString(s)
9442
- ? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
9443
+ ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
9443
9444
  : s;
9444
9445
  };
9445
9446
  var manualUppercase = function(s) {
9446
9447
  return isString(s)
9447
- ? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
9448
+ ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
9448
9449
  : s;
9449
9450
  };
9450
9451
 
@@ -9457,11 +9458,8 @@ if ('i' !== 'I'.toLowerCase()) {
9457
9458
  uppercase = manualUppercase;
9458
9459
  }
9459
9460
 
9460
- function fromCharCode(code) {return String.fromCharCode(code);}
9461
-
9462
9461
 
9463
- var Error = window.Error,
9464
- /** holds major version number for IE or NaN for real browsers */
9462
+ var /** holds major version number for IE or NaN for real browsers */
9465
9463
  msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
9466
9464
  jqLite, // delay binding since jQuery could be loaded after us.
9467
9465
  jQuery, // delay binding
@@ -9475,6 +9473,29 @@ var Error = window.Error,
9475
9473
  nodeName_,
9476
9474
  uid = ['0', '0', '0'];
9477
9475
 
9476
+
9477
+ /**
9478
+ * @private
9479
+ * @param {*} obj
9480
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
9481
+ */
9482
+ function isArrayLike(obj) {
9483
+ if (!obj || (typeof obj.length !== 'number')) return false;
9484
+
9485
+ // We have on object which has length property. Should we treat it as array?
9486
+ if (typeof obj.hasOwnProperty != 'function' &&
9487
+ typeof obj.constructor != 'function') {
9488
+ // This is here for IE8: it is a bogus object treat it as array;
9489
+ return true;
9490
+ } else {
9491
+ return obj instanceof JQLite || // JQLite
9492
+ (jQuery && obj instanceof jQuery) || // jQuery
9493
+ toString.call(obj) !== '[object Object]' || // some browser native object
9494
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
9495
+ }
9496
+ }
9497
+
9498
+
9478
9499
  /**
9479
9500
  * @ngdoc function
9480
9501
  * @name angular.forEach
@@ -9513,7 +9534,7 @@ function forEach(obj, iterator, context) {
9513
9534
  }
9514
9535
  } else if (obj.forEach && obj.forEach !== forEach) {
9515
9536
  obj.forEach(iterator, context);
9516
- } else if (isObject(obj) && isNumber(obj.length)) {
9537
+ } else if (isArrayLike(obj)) {
9517
9538
  for (key = 0; key < obj.length; key++)
9518
9539
  iterator.call(context, obj[key], key);
9519
9540
  } else {
@@ -9558,7 +9579,7 @@ function reverseParams(iteratorFn) {
9558
9579
  /**
9559
9580
  * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
9560
9581
  * characters such as '012ABC'. The reason why we are not using simply a number counter is that
9561
- * the number string gets longer over time, and it can also overflow, where as the the nextId
9582
+ * the number string gets longer over time, and it can also overflow, where as the nextId
9562
9583
  * will grow much slower, it is a string, and it will never overflow.
9563
9584
  *
9564
9585
  * @returns an unique alpha-numeric string
@@ -9585,6 +9606,21 @@ function nextUid() {
9585
9606
  return uid.join('');
9586
9607
  }
9587
9608
 
9609
+
9610
+ /**
9611
+ * Set or clear the hashkey for an object.
9612
+ * @param obj object
9613
+ * @param h the hashkey (!truthy to delete the hashkey)
9614
+ */
9615
+ function setHashKey(obj, h) {
9616
+ if (h) {
9617
+ obj.$$hashKey = h;
9618
+ }
9619
+ else {
9620
+ delete obj.$$hashKey;
9621
+ }
9622
+ }
9623
+
9588
9624
  /**
9589
9625
  * @ngdoc function
9590
9626
  * @name angular.extend
@@ -9596,8 +9632,10 @@ function nextUid() {
9596
9632
  *
9597
9633
  * @param {Object} dst Destination object.
9598
9634
  * @param {...Object} src Source object(s).
9635
+ * @returns {Object} Reference to `dst`.
9599
9636
  */
9600
9637
  function extend(dst) {
9638
+ var h = dst.$$hashKey;
9601
9639
  forEach(arguments, function(obj){
9602
9640
  if (obj !== dst) {
9603
9641
  forEach(obj, function(value, key){
@@ -9605,6 +9643,8 @@ function extend(dst) {
9605
9643
  });
9606
9644
  }
9607
9645
  });
9646
+
9647
+ setHashKey(dst,h);
9608
9648
  return dst;
9609
9649
  }
9610
9650
 
@@ -9954,19 +9994,19 @@ function copy(source, destination){
9954
9994
  } else {
9955
9995
  if (source === destination) throw Error("Can't copy equivalent objects or arrays");
9956
9996
  if (isArray(source)) {
9957
- while(destination.length) {
9958
- destination.pop();
9959
- }
9997
+ destination.length = 0;
9960
9998
  for ( var i = 0; i < source.length; i++) {
9961
9999
  destination.push(copy(source[i]));
9962
10000
  }
9963
10001
  } else {
10002
+ var h = destination.$$hashKey;
9964
10003
  forEach(destination, function(value, key){
9965
10004
  delete destination[key];
9966
10005
  });
9967
10006
  for ( var key in source) {
9968
10007
  destination[key] = copy(source[key]);
9969
10008
  }
10009
+ setHashKey(destination,h);
9970
10010
  }
9971
10011
  }
9972
10012
  return destination;
@@ -10006,7 +10046,7 @@ function shallowCopy(src, dst) {
10006
10046
  * During a property comparision, properties of `function` type and properties with names
10007
10047
  * that begin with `$` are ignored.
10008
10048
  *
10009
- * Scope and DOMWindow objects are being compared only be identify (`===`).
10049
+ * Scope and DOMWindow objects are being compared only by identify (`===`).
10010
10050
  *
10011
10051
  * @param {*} o1 Object or value to compare.
10012
10052
  * @param {*} o2 Object or value to compare.
@@ -10032,13 +10072,15 @@ function equals(o1, o2) {
10032
10072
  if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
10033
10073
  keySet = {};
10034
10074
  for(key in o1) {
10035
- if (key.charAt(0) !== '$' && !isFunction(o1[key]) && !equals(o1[key], o2[key])) {
10036
- return false;
10037
- }
10075
+ if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
10076
+ if (!equals(o1[key], o2[key])) return false;
10038
10077
  keySet[key] = true;
10039
10078
  }
10040
10079
  for(key in o2) {
10041
- if (!keySet[key] && key.charAt(0) !== '$' && !isFunction(o2[key])) return false;
10080
+ if (!keySet[key] &&
10081
+ key.charAt(0) !== '$' &&
10082
+ o2[key] !== undefined &&
10083
+ !isFunction(o2[key])) return false;
10042
10084
  }
10043
10085
  return true;
10044
10086
  }
@@ -10064,7 +10106,7 @@ function sliceArgs(args, startIndex) {
10064
10106
  *
10065
10107
  * @description
10066
10108
  * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
10067
- * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
10109
+ * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
10068
10110
  * known as [function currying](http://en.wikipedia.org/wiki/Currying).
10069
10111
  *
10070
10112
  * @param {Object} self Context which `fn` should be evaluated in.
@@ -10165,9 +10207,18 @@ function startingTag(element) {
10165
10207
  // are not allowed to have children. So we just ignore it.
10166
10208
  element.html('');
10167
10209
  } catch(e) {}
10168
- return jqLite('<div>').append(element).html().
10169
- match(/^(<[^>]+>)/)[1].
10170
- replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
10210
+ // As Per DOM Standards
10211
+ var TEXT_NODE = 3;
10212
+ var elemHtml = jqLite('<div>').append(element).html();
10213
+ try {
10214
+ return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
10215
+ elemHtml.
10216
+ match(/^(<[^>]+>)/)[1].
10217
+ replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
10218
+ } catch(e) {
10219
+ return lowercase(elemHtml);
10220
+ }
10221
+
10171
10222
  }
10172
10223
 
10173
10224
 
@@ -10234,7 +10285,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
10234
10285
  replace(/%3A/gi, ':').
10235
10286
  replace(/%24/g, '$').
10236
10287
  replace(/%2C/gi, ',').
10237
- replace((pctEncodeSpaces ? null : /%20/g), '+');
10288
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
10238
10289
  }
10239
10290
 
10240
10291
 
@@ -10248,10 +10299,10 @@ function encodeUriQuery(val, pctEncodeSpaces) {
10248
10299
  *
10249
10300
  * @description
10250
10301
  *
10251
- * Use this directive to auto-bootstrap on application. Only
10302
+ * Use this directive to auto-bootstrap an application. Only
10252
10303
  * one directive can be used per HTML document. The directive
10253
10304
  * designates the root of the application and is typically placed
10254
- * ot the root of the page.
10305
+ * at the root of the page.
10255
10306
  *
10256
10307
  * In the example below if the `ngApp` directive would not be placed
10257
10308
  * on the `html` element then the document would not be compiled
@@ -10323,22 +10374,38 @@ function angularInit(element, bootstrap) {
10323
10374
  * @returns {AUTO.$injector} Returns the newly created injector for this app.
10324
10375
  */
10325
10376
  function bootstrap(element, modules) {
10326
- element = jqLite(element);
10327
- modules = modules || [];
10328
- modules.unshift(['$provide', function($provide) {
10329
- $provide.value('$rootElement', element);
10330
- }]);
10331
- modules.unshift('ng');
10332
- var injector = createInjector(modules);
10333
- injector.invoke(
10334
- ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
10335
- scope.$apply(function() {
10336
- element.data('$injector', injector);
10337
- compile(element)(scope);
10338
- });
10339
- }]
10340
- );
10341
- return injector;
10377
+ var resumeBootstrapInternal = function() {
10378
+ element = jqLite(element);
10379
+ modules = modules || [];
10380
+ modules.unshift(['$provide', function($provide) {
10381
+ $provide.value('$rootElement', element);
10382
+ }]);
10383
+ modules.unshift('ng');
10384
+ var injector = createInjector(modules);
10385
+ injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
10386
+ function(scope, element, compile, injector) {
10387
+ scope.$apply(function() {
10388
+ element.data('$injector', injector);
10389
+ compile(element)(scope);
10390
+ });
10391
+ }]
10392
+ );
10393
+ return injector;
10394
+ };
10395
+
10396
+ var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
10397
+
10398
+ if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
10399
+ return resumeBootstrapInternal();
10400
+ }
10401
+
10402
+ window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
10403
+ angular.resumeBootstrap = function(extraModules) {
10404
+ forEach(extraModules, function(module) {
10405
+ modules.push(module);
10406
+ });
10407
+ resumeBootstrapInternal();
10408
+ };
10342
10409
  }
10343
10410
 
10344
10411
  var SNAKE_CASE_REGEXP = /[A-Z]/g;
@@ -10371,7 +10438,7 @@ function bindJQuery() {
10371
10438
  }
10372
10439
 
10373
10440
  /**
10374
- * throw error of the argument is falsy.
10441
+ * throw error if the argument is falsy.
10375
10442
  */
10376
10443
  function assertArg(arg, name, reason) {
10377
10444
  if (!arg) {
@@ -10652,11 +10719,11 @@ function setupModuleLoader(window) {
10652
10719
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
10653
10720
  */
10654
10721
  var version = {
10655
- full: '1.0.3', // all of these placeholder strings will be replaced by rake's
10656
- major: 1, // compile task
10722
+ full: '1.0.7', // all of these placeholder strings will be replaced by grunt's
10723
+ major: 1, // package task
10657
10724
  minor: 0,
10658
- dot: 3,
10659
- codeName: 'bouncy-thunder'
10725
+ dot: 7,
10726
+ codeName: 'monochromatic-rainbow'
10660
10727
  };
10661
10728
 
10662
10729
 
@@ -10801,18 +10868,18 @@ function publishExternalAPI(angular){
10801
10868
  * - [after()](http://api.jquery.com/after/)
10802
10869
  * - [append()](http://api.jquery.com/append/)
10803
10870
  * - [attr()](http://api.jquery.com/attr/)
10804
- * - [bind()](http://api.jquery.com/bind/)
10805
- * - [children()](http://api.jquery.com/children/)
10871
+ * - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
10872
+ * - [children()](http://api.jquery.com/children/) - Does not support selectors
10806
10873
  * - [clone()](http://api.jquery.com/clone/)
10807
10874
  * - [contents()](http://api.jquery.com/contents/)
10808
10875
  * - [css()](http://api.jquery.com/css/)
10809
10876
  * - [data()](http://api.jquery.com/data/)
10810
10877
  * - [eq()](http://api.jquery.com/eq/)
10811
- * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
10878
+ * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
10812
10879
  * - [hasClass()](http://api.jquery.com/hasClass/)
10813
10880
  * - [html()](http://api.jquery.com/html/)
10814
- * - [next()](http://api.jquery.com/next/)
10815
- * - [parent()](http://api.jquery.com/parent/)
10881
+ * - [next()](http://api.jquery.com/next/) - Does not support selectors
10882
+ * - [parent()](http://api.jquery.com/parent/) - Does not support selectors
10816
10883
  * - [prepend()](http://api.jquery.com/prepend/)
10817
10884
  * - [prop()](http://api.jquery.com/prop/)
10818
10885
  * - [ready()](http://api.jquery.com/ready/)
@@ -10824,11 +10891,11 @@ function publishExternalAPI(angular){
10824
10891
  * - [text()](http://api.jquery.com/text/)
10825
10892
  * - [toggleClass()](http://api.jquery.com/toggleClass/)
10826
10893
  * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
10827
- * - [unbind()](http://api.jquery.com/unbind/)
10894
+ * - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
10828
10895
  * - [val()](http://api.jquery.com/val/)
10829
10896
  * - [wrap()](http://api.jquery.com/wrap/)
10830
10897
  *
10831
- * ## In addtion to the above, Angular privides an additional method to both jQuery and jQuery lite:
10898
+ * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
10832
10899
  *
10833
10900
  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
10834
10901
  * retrieves controller associated with the `ngController` directive. If `name` is provided as
@@ -11371,23 +11438,43 @@ forEach({
11371
11438
 
11372
11439
  if (!eventFns) {
11373
11440
  if (type == 'mouseenter' || type == 'mouseleave') {
11374
- var counter = 0;
11441
+ var contains = document.body.contains || document.body.compareDocumentPosition ?
11442
+ function( a, b ) {
11443
+ var adown = a.nodeType === 9 ? a.documentElement : a,
11444
+ bup = b && b.parentNode;
11445
+ return a === bup || !!( bup && bup.nodeType === 1 && (
11446
+ adown.contains ?
11447
+ adown.contains( bup ) :
11448
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
11449
+ ));
11450
+ } :
11451
+ function( a, b ) {
11452
+ if ( b ) {
11453
+ while ( (b = b.parentNode) ) {
11454
+ if ( b === a ) {
11455
+ return true;
11456
+ }
11457
+ }
11458
+ }
11459
+ return false;
11460
+ };
11375
11461
 
11376
- events.mouseenter = [];
11377
- events.mouseleave = [];
11462
+ events[type] = [];
11463
+
11464
+ // Refer to jQuery's implementation of mouseenter & mouseleave
11465
+ // Read about mouseenter and mouseleave:
11466
+ // http://www.quirksmode.org/js/events_mouse.html#link8
11467
+ var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
11468
+ bindFn(element, eventmap[type], function(event) {
11469
+ var ret, target = this, related = event.relatedTarget;
11470
+ // For mousenter/leave call the handler if related is outside the target.
11471
+ // NB: No relatedTarget if the mouse left/entered the browser window
11472
+ if ( !related || (related !== target && !contains(target, related)) ){
11473
+ handle(event, type);
11474
+ }
11378
11475
 
11379
- bindFn(element, 'mouseover', function(event) {
11380
- counter++;
11381
- if (counter == 1) {
11382
- handle(event, 'mouseenter');
11383
- }
11384
- });
11385
- bindFn(element, 'mouseout', function(event) {
11386
- counter --;
11387
- if (counter == 0) {
11388
- handle(event, 'mouseleave');
11389
- }
11390
11476
  });
11477
+
11391
11478
  } else {
11392
11479
  addEventListenerFn(element, type, handle);
11393
11480
  events[type] = [];
@@ -11416,14 +11503,14 @@ forEach({
11416
11503
  children: function(element) {
11417
11504
  var children = [];
11418
11505
  forEach(element.childNodes, function(element){
11419
- if (element.nodeName != '#text')
11506
+ if (element.nodeType === 1)
11420
11507
  children.push(element);
11421
11508
  });
11422
11509
  return children;
11423
11510
  },
11424
11511
 
11425
11512
  contents: function(element) {
11426
- return element.childNodes;
11513
+ return element.childNodes || [];
11427
11514
  },
11428
11515
 
11429
11516
  append: function(element, node) {
@@ -11486,7 +11573,16 @@ forEach({
11486
11573
  },
11487
11574
 
11488
11575
  next: function(element) {
11489
- return element.nextSibling;
11576
+ if (element.nextElementSibling) {
11577
+ return element.nextElementSibling;
11578
+ }
11579
+
11580
+ // IE8 doesn't have nextElementSibling
11581
+ var elm = element.nextSibling;
11582
+ while (elm != null && elm.nodeType !== 1) {
11583
+ elm = elm.nextSibling;
11584
+ }
11585
+ return elm;
11490
11586
  },
11491
11587
 
11492
11588
  find: function(element, selector) {
@@ -11694,7 +11790,7 @@ function annotate(fn) {
11694
11790
  }
11695
11791
  } else if (isArray(fn)) {
11696
11792
  last = fn.length - 1;
11697
- assertArgFn(fn[last], 'fn')
11793
+ assertArgFn(fn[last], 'fn');
11698
11794
  $inject = fn.slice(0, last);
11699
11795
  } else {
11700
11796
  assertArgFn(fn, 'fn', true);
@@ -11728,19 +11824,19 @@ function annotate(fn) {
11728
11824
  * # Injection Function Annotation
11729
11825
  *
11730
11826
  * JavaScript does not have annotations, and annotations are needed for dependency injection. The
11731
- * following ways are all valid way of annotating function with injection arguments and are equivalent.
11827
+ * following are all valid ways of annotating function with injection arguments and are equivalent.
11732
11828
  *
11733
11829
  * <pre>
11734
11830
  * // inferred (only works if code not minified/obfuscated)
11735
- * $inject.invoke(function(serviceA){});
11831
+ * $injector.invoke(function(serviceA){});
11736
11832
  *
11737
11833
  * // annotated
11738
11834
  * function explicit(serviceA) {};
11739
11835
  * explicit.$inject = ['serviceA'];
11740
- * $inject.invoke(explicit);
11836
+ * $injector.invoke(explicit);
11741
11837
  *
11742
11838
  * // inline
11743
- * $inject.invoke(['serviceA', function(serviceA){}]);
11839
+ * $injector.invoke(['serviceA', function(serviceA){}]);
11744
11840
  * </pre>
11745
11841
  *
11746
11842
  * ## Inference
@@ -11824,7 +11920,7 @@ function annotate(fn) {
11824
11920
  * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
11825
11921
  * are supported.
11826
11922
  *
11827
- * # The `$injector` property
11923
+ * # The `$inject` property
11828
11924
  *
11829
11925
  * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
11830
11926
  * services to be injected into the function.
@@ -11857,7 +11953,7 @@ function annotate(fn) {
11857
11953
  * // ...
11858
11954
  * };
11859
11955
  * tmpFn.$inject = ['$compile', '$rootScope'];
11860
- * injector.invoke(tempFn);
11956
+ * injector.invoke(tmpFn);
11861
11957
  *
11862
11958
  * // To better support inline function the inline annotation is supported
11863
11959
  * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
@@ -11886,7 +11982,7 @@ function annotate(fn) {
11886
11982
  * @description
11887
11983
  *
11888
11984
  * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
11889
- * The providers share the same name as the instance they create with the `Provider` suffixed to them.
11985
+ * The providers share the same name as the instance they create with `Provider` suffixed to them.
11890
11986
  *
11891
11987
  * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
11892
11988
  * a service. The Provider can have additional methods which would allow for configuration of the provider.
@@ -11910,7 +12006,7 @@ function annotate(fn) {
11910
12006
  *
11911
12007
  * beforeEach(module(function($provide) {
11912
12008
  * $provide.provider('greet', GreetProvider);
11913
- * });
12009
+ * }));
11914
12010
  *
11915
12011
  * it('should greet', inject(function(greet) {
11916
12012
  * expect(greet('angular')).toEqual('Hello angular!');
@@ -11923,9 +12019,7 @@ function annotate(fn) {
11923
12019
  * inject(function(greet) {
11924
12020
  * expect(greet('angular')).toEqual('Ahoj angular!');
11925
12021
  * });
11926
- * )};
11927
- *
11928
- * });
12022
+ * });
11929
12023
  * </pre>
11930
12024
  */
11931
12025
 
@@ -12019,7 +12113,7 @@ function annotate(fn) {
12019
12113
  *
12020
12114
  * @param {string} name The name of the service to decorate.
12021
12115
  * @param {function()} decorator This function will be invoked when the service needs to be
12022
- * instanciated. The function is called using the {@link AUTO.$injector#invoke
12116
+ * instantiated. The function is called using the {@link AUTO.$injector#invoke
12023
12117
  * injector.invoke} method and is therefore fully injectable. Local injection arguments:
12024
12118
  *
12025
12119
  * * `$delegate` - The original service instance, which can be monkey patched, configured,
@@ -12072,7 +12166,7 @@ function createInjector(modulesToLoad) {
12072
12166
  }
12073
12167
 
12074
12168
  function provider(name, provider_) {
12075
- if (isFunction(provider_)) {
12169
+ if (isFunction(provider_) || isArray(provider_)) {
12076
12170
  provider_ = providerInjector.instantiate(provider_);
12077
12171
  }
12078
12172
  if (!provider_.$get) {
@@ -12189,7 +12283,7 @@ function createInjector(modulesToLoad) {
12189
12283
  args.push(
12190
12284
  locals && locals.hasOwnProperty(key)
12191
12285
  ? locals[key]
12192
- : getService(key, path)
12286
+ : getService(key)
12193
12287
  );
12194
12288
  }
12195
12289
  if (!fn.$inject) {
@@ -12219,6 +12313,8 @@ function createInjector(modulesToLoad) {
12219
12313
  var Constructor = function() {},
12220
12314
  instance, returnedValue;
12221
12315
 
12316
+ // Check if Type is annotated and use just the given function at n-1 as parameter
12317
+ // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
12222
12318
  Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
12223
12319
  instance = new Constructor();
12224
12320
  returnedValue = invoke(Type, instance, locals);
@@ -12234,6 +12330,7 @@ function createInjector(modulesToLoad) {
12234
12330
  };
12235
12331
  }
12236
12332
  }
12333
+
12237
12334
  /**
12238
12335
  * @ngdoc function
12239
12336
  * @name ng.$anchorScroll
@@ -12289,7 +12386,7 @@ function $AnchorScrollProvider() {
12289
12386
  }
12290
12387
 
12291
12388
  // does not scroll when user clicks on anchor link that is currently on
12292
- // (no url change, no $locaiton.hash() change), browser native does scroll
12389
+ // (no url change, no $location.hash() change), browser native does scroll
12293
12390
  if (autoScrollingEnabled) {
12294
12391
  $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
12295
12392
  function autoScrollWatchAction() {
@@ -12538,7 +12635,7 @@ function Browser(window, document, $log, $sniffer) {
12538
12635
  */
12539
12636
  self.baseHref = function() {
12540
12637
  var href = baseElement.attr('href');
12541
- return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
12638
+ return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
12542
12639
  };
12543
12640
 
12544
12641
  //////////////////////////////////////////////////////////////
@@ -12577,14 +12674,15 @@ function Browser(window, document, $log, $sniffer) {
12577
12674
  } else {
12578
12675
  if (isString(value)) {
12579
12676
  cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1;
12677
+
12678
+ // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
12679
+ // - 300 cookies
12680
+ // - 20 cookies per unique domain
12681
+ // - 4096 bytes per cookie
12580
12682
  if (cookieLength > 4096) {
12581
12683
  $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+
12582
12684
  cookieLength + " > 4096 bytes)!");
12583
12685
  }
12584
- if (lastCookies.length > 20) {
12585
- $log.warn("Cookie '"+ name +"' possibly not set or overflowed because too many cookies " +
12586
- "were already set (" + lastCookies.length + " > 20 )");
12587
- }
12588
12686
  }
12589
12687
  }
12590
12688
  } else {
@@ -12597,7 +12695,13 @@ function Browser(window, document, $log, $sniffer) {
12597
12695
  cookie = cookieArray[i];
12598
12696
  index = cookie.indexOf('=');
12599
12697
  if (index > 0) { //ignore nameless cookies
12600
- lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
12698
+ var name = unescape(cookie.substring(0, index));
12699
+ // the first value that is seen for a cookie is the most
12700
+ // specific one. values for the same cookie name that
12701
+ // follow are for less specific paths.
12702
+ if (lastCookies[name] === undefined) {
12703
+ lastCookies[name] = unescape(cookie.substring(index + 1));
12704
+ }
12601
12705
  }
12602
12706
  }
12603
12707
  }
@@ -12661,6 +12765,7 @@ function $BrowserProvider(){
12661
12765
  return new Browser($window, $document, $log, $sniffer);
12662
12766
  }];
12663
12767
  }
12768
+
12664
12769
  /**
12665
12770
  * @ngdoc object
12666
12771
  * @name ng.$cacheFactory
@@ -12987,7 +13092,8 @@ function $CompileProvider($provide) {
12987
13092
  Suffix = 'Directive',
12988
13093
  COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
12989
13094
  CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
12990
- MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
13095
+ MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
13096
+ urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
12991
13097
 
12992
13098
 
12993
13099
  /**
@@ -13041,11 +13147,41 @@ function $CompileProvider($provide) {
13041
13147
  };
13042
13148
 
13043
13149
 
13150
+ /**
13151
+ * @ngdoc function
13152
+ * @name ng.$compileProvider#urlSanitizationWhitelist
13153
+ * @methodOf ng.$compileProvider
13154
+ * @function
13155
+ *
13156
+ * @description
13157
+ * Retrieves or overrides the default regular expression that is used for whitelisting of safe
13158
+ * urls during a[href] sanitization.
13159
+ *
13160
+ * The sanitization is a security measure aimed at prevent XSS attacks via html links.
13161
+ *
13162
+ * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
13163
+ * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
13164
+ * expression. If a match is found the original url is written into the dom. Otherwise the
13165
+ * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
13166
+ *
13167
+ * @param {RegExp=} regexp New regexp to whitelist urls with.
13168
+ * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
13169
+ * chaining otherwise.
13170
+ */
13171
+ this.urlSanitizationWhitelist = function(regexp) {
13172
+ if (isDefined(regexp)) {
13173
+ urlSanitizationWhitelist = regexp;
13174
+ return this;
13175
+ }
13176
+ return urlSanitizationWhitelist;
13177
+ };
13178
+
13179
+
13044
13180
  this.$get = [
13045
13181
  '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
13046
- '$controller', '$rootScope',
13182
+ '$controller', '$rootScope', '$document',
13047
13183
  function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
13048
- $controller, $rootScope) {
13184
+ $controller, $rootScope, $document) {
13049
13185
 
13050
13186
  var Attributes = function(element, attr) {
13051
13187
  this.$$element = element;
@@ -13067,7 +13203,8 @@ function $CompileProvider($provide) {
13067
13203
  */
13068
13204
  $set: function(key, value, writeAttr, attrName) {
13069
13205
  var booleanKey = getBooleanAttrName(this.$$element[0], key),
13070
- $$observers = this.$$observers;
13206
+ $$observers = this.$$observers,
13207
+ normalizedVal;
13071
13208
 
13072
13209
  if (booleanKey) {
13073
13210
  this.$$element.prop(key, value);
@@ -13086,6 +13223,19 @@ function $CompileProvider($provide) {
13086
13223
  }
13087
13224
  }
13088
13225
 
13226
+
13227
+ // sanitize a[href] values
13228
+ if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
13229
+ urlSanitizationNode.setAttribute('href', value);
13230
+
13231
+ // href property always returns normalized absolute url, so we can match against that
13232
+ normalizedVal = urlSanitizationNode.href;
13233
+ if (!normalizedVal.match(urlSanitizationWhitelist)) {
13234
+ this[key] = value = 'unsafe:' + normalizedVal;
13235
+ }
13236
+ }
13237
+
13238
+
13089
13239
  if (writeAttr !== false) {
13090
13240
  if (value === null || value === undefined) {
13091
13241
  this.$$element.removeAttr(attrName);
@@ -13129,7 +13279,8 @@ function $CompileProvider($provide) {
13129
13279
  }
13130
13280
  };
13131
13281
 
13132
- var startSymbol = $interpolate.startSymbol(),
13282
+ var urlSanitizationNode = $document[0].createElement('a'),
13283
+ startSymbol = $interpolate.startSymbol(),
13133
13284
  endSymbol = $interpolate.endSymbol(),
13134
13285
  denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
13135
13286
  ? identity
@@ -13144,13 +13295,13 @@ function $CompileProvider($provide) {
13144
13295
 
13145
13296
  function compile($compileNodes, transcludeFn, maxPriority) {
13146
13297
  if (!($compileNodes instanceof jqLite)) {
13147
- // jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
13298
+ // jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
13148
13299
  $compileNodes = jqLite($compileNodes);
13149
13300
  }
13150
13301
  // We can not compile top level text elements since text nodes can be merged and we will
13151
13302
  // not be able to attach scope data to them, so we will wrap them in <span>
13152
13303
  forEach($compileNodes, function(node, index){
13153
- if (node.nodeType == 3 /* text node */) {
13304
+ if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
13154
13305
  $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
13155
13306
  }
13156
13307
  });
@@ -13162,7 +13313,14 @@ function $CompileProvider($provide) {
13162
13313
  var $linkNode = cloneConnectFn
13163
13314
  ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
13164
13315
  : $compileNodes;
13165
- $linkNode.data('$scope', scope);
13316
+
13317
+ // Attach scope only to non-text nodes.
13318
+ for(var i = 0, ii = $linkNode.length; i<ii; i++) {
13319
+ var node = $linkNode[i];
13320
+ if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
13321
+ $linkNode.eq(i).data('$scope', scope);
13322
+ }
13323
+ }
13166
13324
  safeAddClass($linkNode, 'ng-scope');
13167
13325
  if (cloneConnectFn) cloneConnectFn($linkNode, scope);
13168
13326
  if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
@@ -13189,7 +13347,7 @@ function $CompileProvider($provide) {
13189
13347
  * functions return values - the linking functions - are combined into a composite linking
13190
13348
  * function, which is the a linking function for the node.
13191
13349
  *
13192
- * @param {NodeList} nodeList an array of nodes to compile
13350
+ * @param {NodeList} nodeList an array of nodes or NodeList to compile
13193
13351
  * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
13194
13352
  * scope argument is auto-generated to the new child of the transcluded parent scope.
13195
13353
  * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
@@ -13199,68 +13357,75 @@ function $CompileProvider($provide) {
13199
13357
  * @returns {?function} A composite linking function of all of the matched directives or null.
13200
13358
  */
13201
13359
  function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
13202
- var linkFns = [],
13203
- nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
13360
+ var linkFns = [],
13361
+ nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
13204
13362
 
13205
- for(var i = 0; i < nodeList.length; i++) {
13206
- attrs = new Attributes();
13363
+ for(var i = 0; i < nodeList.length; i++) {
13364
+ attrs = new Attributes();
13207
13365
 
13208
- // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
13209
- directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
13366
+ // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
13367
+ directives = collectDirectives(nodeList[i], [], attrs, maxPriority);
13210
13368
 
13211
- nodeLinkFn = (directives.length)
13212
- ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
13213
- : null;
13369
+ nodeLinkFn = (directives.length)
13370
+ ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
13371
+ : null;
13214
13372
 
13215
- childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
13216
- ? null
13217
- : compileNodes(nodeList[i].childNodes,
13218
- nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
13373
+ childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
13374
+ ? null
13375
+ : compileNodes(nodeList[i].childNodes,
13376
+ nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
13219
13377
 
13220
- linkFns.push(nodeLinkFn);
13221
- linkFns.push(childLinkFn);
13222
- linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
13223
- }
13378
+ linkFns.push(nodeLinkFn);
13379
+ linkFns.push(childLinkFn);
13380
+ linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
13381
+ }
13224
13382
 
13225
- // return a linking function if we have found anything, null otherwise
13226
- return linkFnFound ? compositeLinkFn : null;
13383
+ // return a linking function if we have found anything, null otherwise
13384
+ return linkFnFound ? compositeLinkFn : null;
13227
13385
 
13228
- function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
13229
- var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn;
13386
+ function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
13387
+ var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn, i, ii, n;
13230
13388
 
13231
- for(var i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
13232
- node = nodeList[n];
13233
- nodeLinkFn = linkFns[i++];
13234
- childLinkFn = linkFns[i++];
13389
+ // copy nodeList so that linking doesn't break due to live list updates.
13390
+ var stableNodeList = [];
13391
+ for (i = 0, ii = nodeList.length; i < ii; i++) {
13392
+ stableNodeList.push(nodeList[i]);
13393
+ }
13235
13394
 
13236
- if (nodeLinkFn) {
13237
- if (nodeLinkFn.scope) {
13238
- childScope = scope.$new(isObject(nodeLinkFn.scope));
13239
- jqLite(node).data('$scope', childScope);
13240
- } else {
13241
- childScope = scope;
13242
- }
13243
- childTranscludeFn = nodeLinkFn.transclude;
13244
- if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
13245
- nodeLinkFn(childLinkFn, childScope, node, $rootElement,
13246
- (function(transcludeFn) {
13247
- return function(cloneFn) {
13248
- var transcludeScope = scope.$new();
13249
-
13250
- return transcludeFn(transcludeScope, cloneFn).
13251
- bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
13395
+ for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
13396
+ node = stableNodeList[n];
13397
+ nodeLinkFn = linkFns[i++];
13398
+ childLinkFn = linkFns[i++];
13399
+
13400
+ if (nodeLinkFn) {
13401
+ if (nodeLinkFn.scope) {
13402
+ childScope = scope.$new(isObject(nodeLinkFn.scope));
13403
+ jqLite(node).data('$scope', childScope);
13404
+ } else {
13405
+ childScope = scope;
13406
+ }
13407
+ childTranscludeFn = nodeLinkFn.transclude;
13408
+ if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
13409
+ nodeLinkFn(childLinkFn, childScope, node, $rootElement,
13410
+ (function(transcludeFn) {
13411
+ return function(cloneFn) {
13412
+ var transcludeScope = scope.$new();
13413
+ transcludeScope.$$transcluded = true;
13414
+
13415
+ return transcludeFn(transcludeScope, cloneFn).
13416
+ bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
13252
13417
  };
13253
13418
  })(childTranscludeFn || transcludeFn)
13254
- );
13255
- } else {
13256
- nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
13257
- }
13258
- } else if (childLinkFn) {
13259
- childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
13260
- }
13261
- }
13262
- }
13263
- }
13419
+ );
13420
+ } else {
13421
+ nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
13422
+ }
13423
+ } else if (childLinkFn) {
13424
+ childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
13425
+ }
13426
+ }
13427
+ }
13428
+ }
13264
13429
 
13265
13430
 
13266
13431
  /**
@@ -13341,9 +13506,9 @@ function $CompileProvider($provide) {
13341
13506
 
13342
13507
 
13343
13508
  /**
13344
- * Once the directives have been collected their compile functions is executed. This method
13509
+ * Once the directives have been collected, their compile functions are executed. This method
13345
13510
  * is responsible for inlining directive templates as well as terminating the application
13346
- * of the directives if the terminal directive has been reached..
13511
+ * of the directives if the terminal directive has been reached.
13347
13512
  *
13348
13513
  * @param {Array} directives Array of collected directives to execute their compile function.
13349
13514
  * this needs to be pre-sorted by priority order.
@@ -13351,11 +13516,11 @@ function $CompileProvider($provide) {
13351
13516
  * @param {Object} templateAttrs The shared attribute function
13352
13517
  * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
13353
13518
  * scope argument is auto-generated to the new child of the transcluded parent scope.
13354
- * @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
13355
- * argument has the root jqLite array so that we can replace widgets on it.
13519
+ * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
13520
+ * argument has the root jqLite array so that we can replace nodes on it.
13356
13521
  * @returns linkFn
13357
13522
  */
13358
- function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
13523
+ function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
13359
13524
  var terminalPriority = -Number.MAX_VALUE,
13360
13525
  preLinkFns = [],
13361
13526
  postLinkFns = [],
@@ -13407,9 +13572,9 @@ function $CompileProvider($provide) {
13407
13572
  if (directiveValue == 'element') {
13408
13573
  $template = jqLite(compileNode);
13409
13574
  $compileNode = templateAttrs.$$element =
13410
- jqLite('<!-- ' + directiveName + ': ' + templateAttrs[directiveName] + ' -->');
13575
+ jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
13411
13576
  compileNode = $compileNode[0];
13412
- replaceWith($rootElement, jqLite($template[0]), compileNode);
13577
+ replaceWith(jqCollection, jqLite($template[0]), compileNode);
13413
13578
  childTranscludeFn = compile($template, transcludeFn, terminalPriority);
13414
13579
  } else {
13415
13580
  $template = jqLite(JQLiteClone(compileNode)).contents();
@@ -13433,7 +13598,7 @@ function $CompileProvider($provide) {
13433
13598
  throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
13434
13599
  }
13435
13600
 
13436
- replaceWith($rootElement, $compileNode, compileNode);
13601
+ replaceWith(jqCollection, $compileNode, compileNode);
13437
13602
 
13438
13603
  var newTemplateAttrs = {$attr: {}};
13439
13604
 
@@ -13461,7 +13626,7 @@ function $CompileProvider($provide) {
13461
13626
  assertNoDuplicate('template', templateDirective, directive, $compileNode);
13462
13627
  templateDirective = directive;
13463
13628
  nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
13464
- nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
13629
+ nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
13465
13630
  childTranscludeFn);
13466
13631
  ii = directives.length;
13467
13632
  } else if (directive.compile) {
@@ -13551,6 +13716,8 @@ function $CompileProvider($provide) {
13551
13716
  lastValue,
13552
13717
  parentGet, parentSet;
13553
13718
 
13719
+ scope.$$isolateBindings[scopeName] = mode + attrName;
13720
+
13554
13721
  switch (mode) {
13555
13722
 
13556
13723
  case '@': {
@@ -13592,7 +13759,7 @@ function $CompileProvider($provide) {
13592
13759
  parentGet = $parse(attrs[attrName]);
13593
13760
  scope[scopeName] = function(locals) {
13594
13761
  return parentGet(parentScope, locals);
13595
- }
13762
+ };
13596
13763
  break;
13597
13764
  }
13598
13765
 
@@ -13761,8 +13928,8 @@ function $CompileProvider($provide) {
13761
13928
  }
13762
13929
 
13763
13930
  directives.unshift(derivedSyncDirective);
13764
- afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
13765
- afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
13931
+ afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
13932
+ afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
13766
13933
 
13767
13934
 
13768
13935
  while(linkQueue.length) {
@@ -13841,10 +14008,10 @@ function $CompileProvider($provide) {
13841
14008
  function addAttrInterpolateDirective(node, directives, value, name) {
13842
14009
  var interpolateFn = $interpolate(value, true);
13843
14010
 
13844
-
13845
14011
  // no interpolation found -> ignore
13846
14012
  if (!interpolateFn) return;
13847
14013
 
14014
+
13848
14015
  directives.push({
13849
14016
  priority: 100,
13850
14017
  compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
@@ -14027,7 +14194,7 @@ function $ControllerProvider() {
14027
14194
  * @description
14028
14195
  * `$controller` service is responsible for instantiating controllers.
14029
14196
  *
14030
- * It's just simple call to {@link AUTO.$injector $injector}, but extracted into
14197
+ * It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
14031
14198
  * a service, so that one can override this service with {@link https://gist.github.com/1649788
14032
14199
  * BC version}.
14033
14200
  */
@@ -14072,14 +14239,15 @@ function $DocumentProvider(){
14072
14239
  * the browser console.
14073
14240
  *
14074
14241
  * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
14075
- * {@link ngMock.$exceptionHandler mock $exceptionHandler}
14242
+ * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
14076
14243
  *
14077
14244
  * @param {Error} exception Exception associated with the error.
14078
14245
  * @param {string=} cause optional information about the context in which
14079
14246
  * the error was thrown.
14247
+ *
14080
14248
  */
14081
14249
  function $ExceptionHandlerProvider() {
14082
- this.$get = ['$log', function($log){
14250
+ this.$get = ['$log', function($log) {
14083
14251
  return function(exception, cause) {
14084
14252
  $log.error.apply($log, arguments);
14085
14253
  };
@@ -14267,7 +14435,7 @@ function $InterpolateProvider() {
14267
14435
  }];
14268
14436
  }
14269
14437
 
14270
- var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
14438
+ var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,
14271
14439
  PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,
14272
14440
  HASH_MATCH = PATH_MATCH,
14273
14441
  DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
@@ -14346,7 +14514,8 @@ function convertToHashbangUrl(url, basePath, hashPrefix) {
14346
14514
  var match = matchUrl(url);
14347
14515
 
14348
14516
  // already hashbang url
14349
- if (decodeURIComponent(match.path) == basePath) {
14517
+ if (decodeURIComponent(match.path) == basePath && !isUndefined(match.hash) &&
14518
+ match.hash.indexOf(hashPrefix) === 0) {
14350
14519
  return url;
14351
14520
  // convert html5 url -> hashbang url
14352
14521
  } else {
@@ -14843,6 +15012,10 @@ function $LocationProvider(){
14843
15012
  // update $location when $browser url changes
14844
15013
  $browser.onUrlChange(function(newUrl) {
14845
15014
  if ($location.absUrl() != newUrl) {
15015
+ if ($rootScope.$broadcast('$locationChangeStart', newUrl, $location.absUrl()).defaultPrevented) {
15016
+ $browser.url($location.absUrl());
15017
+ return;
15018
+ }
14846
15019
  $rootScope.$evalAsync(function() {
14847
15020
  var oldUrl = $location.absUrl();
14848
15021
 
@@ -15151,10 +15324,10 @@ function lex(text, csp){
15151
15324
  function readIdent() {
15152
15325
  var ident = "",
15153
15326
  start = index,
15154
- lastDot, peekIndex, methodName;
15327
+ lastDot, peekIndex, methodName, ch;
15155
15328
 
15156
15329
  while (index < text.length) {
15157
- var ch = text.charAt(index);
15330
+ ch = text.charAt(index);
15158
15331
  if (ch == '.' || isIdent(ch) || isNumber(ch)) {
15159
15332
  if (ch == '.') lastDot = index;
15160
15333
  ident += ch;
@@ -15168,7 +15341,7 @@ function lex(text, csp){
15168
15341
  if (lastDot) {
15169
15342
  peekIndex = index;
15170
15343
  while(peekIndex < text.length) {
15171
- var ch = text.charAt(peekIndex);
15344
+ ch = text.charAt(peekIndex);
15172
15345
  if (ch == '(') {
15173
15346
  methodName = ident.substr(lastDot - start + 1);
15174
15347
  ident = ident.substr(0, lastDot - start);
@@ -15421,8 +15594,8 @@ function parser(text, json, $filter, csp){
15421
15594
  text.substring(0, token.index) + "] can not be assigned to", token);
15422
15595
  }
15423
15596
  right = logicalOR();
15424
- return function(self, locals){
15425
- return left.assign(self, right(self, locals), locals);
15597
+ return function(scope, locals){
15598
+ return left.assign(scope, right(scope, locals), locals);
15426
15599
  };
15427
15600
  } else {
15428
15601
  return left;
@@ -15539,12 +15712,12 @@ function parser(text, json, $filter, csp){
15539
15712
  var field = expect().text;
15540
15713
  var getter = getterFn(field, csp);
15541
15714
  return extend(
15542
- function(self, locals) {
15543
- return getter(object(self, locals), locals);
15715
+ function(scope, locals, self) {
15716
+ return getter(self || object(scope, locals), locals);
15544
15717
  },
15545
15718
  {
15546
- assign:function(self, value, locals) {
15547
- return setter(object(self, locals), field, value);
15719
+ assign:function(scope, value, locals) {
15720
+ return setter(object(scope, locals), field, value);
15548
15721
  }
15549
15722
  }
15550
15723
  );
@@ -15585,14 +15758,14 @@ function parser(text, json, $filter, csp){
15585
15758
  } while (expect(','));
15586
15759
  }
15587
15760
  consume(')');
15588
- return function(self, locals){
15761
+ return function(scope, locals){
15589
15762
  var args = [],
15590
- context = contextGetter ? contextGetter(self, locals) : self;
15763
+ context = contextGetter ? contextGetter(scope, locals) : scope;
15591
15764
 
15592
15765
  for ( var i = 0; i < argsFn.length; i++) {
15593
- args.push(argsFn[i](self, locals));
15766
+ args.push(argsFn[i](scope, locals));
15594
15767
  }
15595
- var fnPtr = fn(self, locals) || noop;
15768
+ var fnPtr = fn(scope, locals, context) || noop;
15596
15769
  // IE stupidity!
15597
15770
  return fnPtr.apply
15598
15771
  ? fnPtr.apply(context, args)
@@ -15634,8 +15807,7 @@ function parser(text, json, $filter, csp){
15634
15807
  var object = {};
15635
15808
  for ( var i = 0; i < keyValues.length; i++) {
15636
15809
  var keyValue = keyValues[i];
15637
- var value = keyValue.value(self, locals);
15638
- object[keyValue.key] = value;
15810
+ object[keyValue.key] = keyValue.value(self, locals);
15639
15811
  }
15640
15812
  return object;
15641
15813
  };
@@ -15757,7 +15929,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4) {
15757
15929
  }
15758
15930
  return pathVal;
15759
15931
  };
15760
- };
15932
+ }
15761
15933
 
15762
15934
  function getterFn(path, csp) {
15763
15935
  if (getterFnCache.hasOwnProperty(path)) {
@@ -15772,7 +15944,7 @@ function getterFn(path, csp) {
15772
15944
  fn = (pathKeysLength < 6)
15773
15945
  ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4])
15774
15946
  : function(scope, locals) {
15775
- var i = 0, val
15947
+ var i = 0, val;
15776
15948
  do {
15777
15949
  val = cspSafeGetterFn(
15778
15950
  pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++]
@@ -15837,9 +16009,10 @@ function getterFn(path, csp) {
15837
16009
  * @param {string} expression String expression to compile.
15838
16010
  * @returns {function(context, locals)} a function which represents the compiled expression:
15839
16011
  *
15840
- * * `context`: an object against which any expressions embedded in the strings are evaluated
15841
- * against (Topically a scope object).
15842
- * * `locals`: local variables context object, useful for overriding values in `context`.
16012
+ * * `context` – `{object}` – an object against which any expressions embedded in the strings
16013
+ * are evaluated against (tipically a scope object).
16014
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values in
16015
+ * `context`.
15843
16016
  *
15844
16017
  * The return function also has an `assign` property, if the expression is assignable, which
15845
16018
  * allows one to set values to expressions.
@@ -15875,8 +16048,8 @@ function $ParseProvider() {
15875
16048
  * interface for interacting with an object that represents the result of an action that is
15876
16049
  * performed asynchronously, and may or may not be finished at any given point in time.
15877
16050
  *
15878
- * From the perspective of dealing with error handling, deferred and promise apis are to
15879
- * asynchronous programing what `try`, `catch` and `throw` keywords are to synchronous programing.
16051
+ * From the perspective of dealing with error handling, deferred and promise APIs are to
16052
+ * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
15880
16053
  *
15881
16054
  * <pre>
15882
16055
  * // for the purpose of this example let's assume that variables `$q` and `scope` are
@@ -15910,7 +16083,7 @@ function $ParseProvider() {
15910
16083
  *
15911
16084
  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
15912
16085
  * comes in the way of
15913
- * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
16086
+ * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
15914
16087
  *
15915
16088
  * Additionally the promise api allows for composition that is very hard to do with the
15916
16089
  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
@@ -15922,7 +16095,7 @@ function $ParseProvider() {
15922
16095
  *
15923
16096
  * A new instance of deferred is constructed by calling `$q.defer()`.
15924
16097
  *
15925
- * The purpose of the deferred object is to expose the associated Promise instance as well as apis
16098
+ * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
15926
16099
  * that can be used for signaling the successful or unsuccessful completion of the task.
15927
16100
  *
15928
16101
  * **Methods**
@@ -15965,7 +16138,7 @@ function $ParseProvider() {
15965
16138
  * return result + 1;
15966
16139
  * });
15967
16140
  *
15968
- * // promiseB will be resolved immediately after promiseA is resolved and it's value will be
16141
+ * // promiseB will be resolved immediately after promiseA is resolved and its value will be
15969
16142
  * // the result of promiseA incremented by 1
15970
16143
  * </pre>
15971
16144
  *
@@ -15984,8 +16157,32 @@ function $ParseProvider() {
15984
16157
  * models and avoiding unnecessary browser repaints, which would result in flickering UI.
15985
16158
  * - $q promises are recognized by the templating engine in angular, which means that in templates
15986
16159
  * you can treat promises attached to a scope as if they were the resulting values.
15987
- * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains
16160
+ * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
15988
16161
  * all the important functionality needed for common async tasks.
16162
+ *
16163
+ * # Testing
16164
+ *
16165
+ * <pre>
16166
+ * it('should simulate promise', inject(function($q, $rootScope) {
16167
+ * var deferred = $q.defer();
16168
+ * var promise = deferred.promise;
16169
+ * var resolvedValue;
16170
+ *
16171
+ * promise.then(function(value) { resolvedValue = value; });
16172
+ * expect(resolvedValue).toBeUndefined();
16173
+ *
16174
+ * // Simulate resolving of promise
16175
+ * deferred.resolve(123);
16176
+ * // Note that the 'then' function does not get called synchronously.
16177
+ * // This is because we want the promise API to always be async, whether or not
16178
+ * // it got called synchronously or asynchronously.
16179
+ * expect(resolvedValue).toBeUndefined();
16180
+ *
16181
+ * // Propagate promise resolution to 'then' functions using $apply().
16182
+ * $rootScope.$apply();
16183
+ * expect(resolvedValue).toEqual(123);
16184
+ * });
16185
+ * </pre>
15989
16186
  */
15990
16187
  function $QProvider() {
15991
16188
 
@@ -16151,14 +16348,11 @@ function qFactory(nextTick, exceptionHandler) {
16151
16348
  * @methodOf ng.$q
16152
16349
  * @description
16153
16350
  * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
16154
- * This is useful when you are dealing with on object that might or might not be a promise, or if
16351
+ * This is useful when you are dealing with an object that might or might not be a promise, or if
16155
16352
  * the promise comes from a source that can't be trusted.
16156
16353
  *
16157
16354
  * @param {*} value Value or a promise
16158
- * @returns {Promise} Returns a single promise that will be resolved with an array of values,
16159
- * each value coresponding to the promise at the same index in the `promises` array. If any of
16160
- * the promises is resolved with a rejection, this resulting promise will be resolved with the
16161
- * same rejection.
16355
+ * @returns {Promise} Returns a promise of the passed value or promise
16162
16356
  */
16163
16357
  var when = function(value, callback, errback) {
16164
16358
  var result = defer(),
@@ -16218,7 +16412,7 @@ function qFactory(nextTick, exceptionHandler) {
16218
16412
  *
16219
16413
  * @param {Array.<Promise>} promises An array of promises.
16220
16414
  * @returns {Promise} Returns a single promise that will be resolved with an array of values,
16221
- * each value coresponding to the promise at the same index in the `promises` array. If any of
16415
+ * each value corresponding to the promise at the same index in the `promises` array. If any of
16222
16416
  * the promises is resolved with a rejection, this resulting promise will be resolved with the
16223
16417
  * same rejection.
16224
16418
  */
@@ -16272,8 +16466,13 @@ function $RouteProvider(){
16272
16466
  *
16273
16467
  * @param {string} path Route path (matched against `$location.path`). If `$location.path`
16274
16468
  * contains redundant trailing slash or is missing one, the route will still match and the
16275
- * `$location.path` will be updated to add or drop the trailing slash to exacly match the
16469
+ * `$location.path` will be updated to add or drop the trailing slash to exactly match the
16276
16470
  * route definition.
16471
+ *
16472
+ * `path` can contain named groups starting with a colon (`:name`). All characters up to the
16473
+ * next slash are matched and stored in `$routeParams` under the given `name` when the route
16474
+ * matches.
16475
+ *
16277
16476
  * @param {Object} route Mapping information to be assigned to `$route.current` on route
16278
16477
  * match.
16279
16478
  *
@@ -16510,8 +16709,9 @@ function $RouteProvider(){
16510
16709
  * {@link ng.directive:ngView ngView} listens for the directive
16511
16710
  * to instantiate the controller and render the view.
16512
16711
  *
16712
+ * @param {Object} angularEvent Synthetic event object.
16513
16713
  * @param {Route} current Current route information.
16514
- * @param {Route} previous Previous route information.
16714
+ * @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
16515
16715
  */
16516
16716
 
16517
16717
  /**
@@ -16538,8 +16738,7 @@ function $RouteProvider(){
16538
16738
  * instance of the Controller.
16539
16739
  */
16540
16740
 
16541
- var matcher = switchRouteMatcher,
16542
- forceReload = false,
16741
+ var forceReload = false,
16543
16742
  $route = {
16544
16743
  routes: routes,
16545
16744
 
@@ -16567,21 +16766,36 @@ function $RouteProvider(){
16567
16766
 
16568
16767
  /////////////////////////////////////////////////////
16569
16768
 
16769
+ /**
16770
+ * @param on {string} current url
16771
+ * @param when {string} route when template to match the url against
16772
+ * @return {?Object}
16773
+ */
16570
16774
  function switchRouteMatcher(on, when) {
16571
16775
  // TODO(i): this code is convoluted and inefficient, we should construct the route matching
16572
16776
  // regex only once and then reuse it
16573
- var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
16777
+
16778
+ // Escape regexp special characters.
16779
+ when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
16780
+ var regex = '',
16574
16781
  params = [],
16575
16782
  dst = {};
16576
- forEach(when.split(/\W/), function(param) {
16577
- if (param) {
16578
- var paramRegExp = new RegExp(":" + param + "([\\W])");
16579
- if (regex.match(paramRegExp)) {
16580
- regex = regex.replace(paramRegExp, "([^\\/]*)$1");
16581
- params.push(param);
16582
- }
16583
- }
16584
- });
16783
+
16784
+ var re = /:(\w+)/g,
16785
+ paramMatch,
16786
+ lastMatchedIndex = 0;
16787
+
16788
+ while ((paramMatch = re.exec(when)) !== null) {
16789
+ // Find each :param in `when` and replace it with a capturing group.
16790
+ // Append all other sections of when unchanged.
16791
+ regex += when.slice(lastMatchedIndex, paramMatch.index);
16792
+ regex += '([^\\/]*)';
16793
+ params.push(paramMatch[1]);
16794
+ lastMatchedIndex = re.lastIndex;
16795
+ }
16796
+ // Append trailing path part.
16797
+ regex += when.substr(lastMatchedIndex);
16798
+
16585
16799
  var match = on.match(new RegExp(regex));
16586
16800
  if (match) {
16587
16801
  forEach(params, function(name, index) {
@@ -16595,7 +16809,7 @@ function $RouteProvider(){
16595
16809
  var next = parseRoute(),
16596
16810
  last = $route.current;
16597
16811
 
16598
- if (next && last && next.$route === last.$route
16812
+ if (next && last && next.$$route === last.$$route
16599
16813
  && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
16600
16814
  last.params = next.params;
16601
16815
  copy(last.params, $routeParams);
@@ -16670,11 +16884,11 @@ function $RouteProvider(){
16670
16884
  // Match a route
16671
16885
  var params, match;
16672
16886
  forEach(routes, function(route, path) {
16673
- if (!match && (params = matcher($location.path(), path))) {
16887
+ if (!match && (params = switchRouteMatcher($location.path(), path))) {
16674
16888
  match = inherit(route, {
16675
16889
  params: extend({}, $location.search(), params),
16676
16890
  pathParams: params});
16677
- match.$route = route;
16891
+ match.$$route = route;
16678
16892
  }
16679
16893
  });
16680
16894
  // No route matched; fallback to "otherwise" route
@@ -16734,22 +16948,22 @@ function $RouteParamsProvider() {
16734
16948
  /**
16735
16949
  * DESIGN NOTES
16736
16950
  *
16737
- * The design decisions behind the scope ware heavily favored for speed and memory consumption.
16951
+ * The design decisions behind the scope are heavily favored for speed and memory consumption.
16738
16952
  *
16739
16953
  * The typical use of scope is to watch the expressions, which most of the time return the same
16740
16954
  * value as last time so we optimize the operation.
16741
16955
  *
16742
- * Closures construction is expensive from speed as well as memory:
16743
- * - no closures, instead ups prototypical inheritance for API
16956
+ * Closures construction is expensive in terms of speed as well as memory:
16957
+ * - No closures, instead use prototypical inheritance for API
16744
16958
  * - Internal state needs to be stored on scope directly, which means that private state is
16745
16959
  * exposed as $$____ properties
16746
16960
  *
16747
16961
  * Loop operations are optimized by using while(count--) { ... }
16748
16962
  * - this means that in order to keep the same order of execution as addition we have to add
16749
- * items to the array at the begging (shift) instead of at the end (push)
16963
+ * items to the array at the beginning (shift) instead of at the end (push)
16750
16964
  *
16751
16965
  * Child scopes are created and removed often
16752
- * - Using array would be slow since inserts in meddle are expensive so we use linked list
16966
+ * - Using an array would be slow since inserts in middle are expensive so we use linked list
16753
16967
  *
16754
16968
  * There are few watches then a lot of observers. This is why you don't want the observer to be
16755
16969
  * implemented in the same way as watch. Watch requires return of initialization function which
@@ -16771,7 +16985,7 @@ function $RouteParamsProvider() {
16771
16985
  * @methodOf ng.$rootScopeProvider
16772
16986
  * @description
16773
16987
  *
16774
- * Sets the number of digest iteration the scope should attempt to execute before giving up and
16988
+ * Sets the number of digest iterations the scope should attempt to execute before giving up and
16775
16989
  * assuming that the model is unstable.
16776
16990
  *
16777
16991
  * The current default is 10 iterations.
@@ -16822,7 +17036,7 @@ function $RootScopeProvider(){
16822
17036
  expect(scope.greeting).toEqual(undefined);
16823
17037
 
16824
17038
  scope.$watch('name', function() {
16825
- this.greeting = this.salutation + ' ' + this.name + '!';
17039
+ scope.greeting = scope.salutation + ' ' + scope.name + '!';
16826
17040
  }); // initialize the watch
16827
17041
 
16828
17042
  expect(scope.greeting).toEqual(undefined);
@@ -16865,8 +17079,10 @@ function $RootScopeProvider(){
16865
17079
  this.$$nextSibling = this.$$prevSibling =
16866
17080
  this.$$childHead = this.$$childTail = null;
16867
17081
  this['this'] = this.$root = this;
17082
+ this.$$destroyed = false;
16868
17083
  this.$$asyncQueue = [];
16869
17084
  this.$$listeners = {};
17085
+ this.$$isolateBindings = {};
16870
17086
  }
16871
17087
 
16872
17088
  /**
@@ -16983,7 +17199,7 @@ function $RootScopeProvider(){
16983
17199
  scope.counter = 0;
16984
17200
 
16985
17201
  expect(scope.counter).toEqual(0);
16986
- scope.$watch('name', function(newValue, oldValue) { counter = counter + 1; });
17202
+ scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; });
16987
17203
  expect(scope.counter).toEqual(0);
16988
17204
 
16989
17205
  scope.$digest();
@@ -17049,7 +17265,7 @@ function $RootScopeProvider(){
17049
17265
  * @function
17050
17266
  *
17051
17267
  * @description
17052
- * Process all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
17268
+ * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children.
17053
17269
  * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
17054
17270
  * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are
17055
17271
  * firing. This means that it is possible to get into an infinite loop. This function will throw
@@ -17076,7 +17292,7 @@ function $RootScopeProvider(){
17076
17292
 
17077
17293
  expect(scope.counter).toEqual(0);
17078
17294
  scope.$watch('name', function(newValue, oldValue) {
17079
- counter = counter + 1;
17295
+ scope.counter = scope.counter + 1;
17080
17296
  });
17081
17297
  expect(scope.counter).toEqual(0);
17082
17298
 
@@ -17198,10 +17414,12 @@ function $RootScopeProvider(){
17198
17414
  * perform any necessary cleanup.
17199
17415
  */
17200
17416
  $destroy: function() {
17201
- if ($rootScope == this) return; // we can't remove the root node;
17417
+ // we can't destroy the root scope or a scope that has been already destroyed
17418
+ if ($rootScope == this || this.$$destroyed) return;
17202
17419
  var parent = this.$parent;
17203
17420
 
17204
17421
  this.$broadcast('$destroy');
17422
+ this.$$destroyed = true;
17205
17423
 
17206
17424
  if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
17207
17425
  if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
@@ -17346,10 +17564,6 @@ function $RootScopeProvider(){
17346
17564
  * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
17347
17565
  * event life cycle.
17348
17566
  *
17349
- * @param {string} name Event name to listen on.
17350
- * @param {function(event)} listener Function to call when the event is emitted.
17351
- * @returns {function()} Returns a deregistration function for this listener.
17352
- *
17353
17567
  * The event listener function format is: `function(event, args...)`. The `event` object
17354
17568
  * passed into the listener has the following attributes:
17355
17569
  *
@@ -17360,6 +17574,10 @@ function $RootScopeProvider(){
17360
17574
  * propagation (available only for events that were `$emit`-ed).
17361
17575
  * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
17362
17576
  * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
17577
+ *
17578
+ * @param {string} name Event name to listen on.
17579
+ * @param {function(event, args...)} listener Function to call when the event is emitted.
17580
+ * @returns {function()} Returns a deregistration function for this listener.
17363
17581
  */
17364
17582
  $on: function(name, listener) {
17365
17583
  var namedListeners = this.$$listeners[name];
@@ -17389,7 +17607,7 @@ function $RootScopeProvider(){
17389
17607
  * Afterwards, the event traverses upwards toward the root scope and calls all registered
17390
17608
  * listeners along the way. The event will stop propagating if one of the listeners cancels it.
17391
17609
  *
17392
- * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17610
+ * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17393
17611
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17394
17612
  *
17395
17613
  * @param {string} name Event name to emit.
@@ -17458,7 +17676,7 @@ function $RootScopeProvider(){
17458
17676
  * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17459
17677
  * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17460
17678
  *
17461
- * @param {string} name Event name to emit.
17679
+ * @param {string} name Event name to broadcast.
17462
17680
  * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
17463
17681
  * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
17464
17682
  */
@@ -17537,7 +17755,7 @@ function $RootScopeProvider(){
17537
17755
 
17538
17756
  /**
17539
17757
  * function used as an initial value for watchers.
17540
- * because it's uniqueue we can easily tell it apart from other values
17758
+ * because it's unique we can easily tell it apart from other values
17541
17759
  */
17542
17760
  function initWatchVal() {}
17543
17761
  }];
@@ -17604,10 +17822,23 @@ function $SnifferProvider() {
17604
17822
  * @example
17605
17823
  <doc:example>
17606
17824
  <doc:source>
17607
- <input ng-init="$window = $service('$window'); greeting='Hello World!'" type="text" ng-model="greeting" />
17608
- <button ng-click="$window.alert(greeting)">ALERT</button>
17825
+ <script>
17826
+ function Ctrl($scope, $window) {
17827
+ $scope.$window = $window;
17828
+ $scope.greeting = 'Hello, World!';
17829
+ }
17830
+ </script>
17831
+ <div ng-controller="Ctrl">
17832
+ <input type="text" ng-model="greeting" />
17833
+ <button ng-click="$window.alert(greeting)">ALERT</button>
17834
+ </div>
17609
17835
  </doc:source>
17610
17836
  <doc:scenario>
17837
+ it('should display the greeting in the input box', function() {
17838
+ input('greeting').enter('Hello, E2E Tests');
17839
+ // If we click the button it will block the test runner
17840
+ // element(':button').click();
17841
+ });
17611
17842
  </doc:scenario>
17612
17843
  </doc:example>
17613
17844
  */
@@ -17751,7 +17982,7 @@ function $HttpProvider() {
17751
17982
  /**
17752
17983
  * @ngdoc function
17753
17984
  * @name ng.$http
17754
- * @requires $httpBacked
17985
+ * @requires $httpBackend
17755
17986
  * @requires $browser
17756
17987
  * @requires $cacheFactory
17757
17988
  * @requires $rootScope
@@ -17760,7 +17991,7 @@ function $HttpProvider() {
17760
17991
  *
17761
17992
  * @description
17762
17993
  * The `$http` service is a core Angular service that facilitates communication with the remote
17763
- * HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest
17994
+ * HTTP servers via the browser's {@link https://developer.mozilla.org/en/xmlhttprequest
17764
17995
  * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}.
17765
17996
  *
17766
17997
  * For unit testing applications that use `$http` service, see
@@ -17770,13 +18001,13 @@ function $HttpProvider() {
17770
18001
  * $resource} service.
17771
18002
  *
17772
18003
  * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
17773
- * the $q service. While for simple usage patters this doesn't matter much, for advanced usage,
17774
- * it is important to familiarize yourself with these apis and guarantees they provide.
18004
+ * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
18005
+ * it is important to familiarize yourself with these APIs and the guarantees they provide.
17775
18006
  *
17776
18007
  *
17777
18008
  * # General usage
17778
18009
  * The `$http` service is a function which takes a single argument — a configuration object —
17779
- * that is used to generate an http request and returns a {@link ng.$q promise}
18010
+ * that is used to generate an HTTP request and returns a {@link ng.$q promise}
17780
18011
  * with two $http specific methods: `success` and `error`.
17781
18012
  *
17782
18013
  * <pre>
@@ -17787,22 +18018,25 @@ function $HttpProvider() {
17787
18018
  * }).
17788
18019
  * error(function(data, status, headers, config) {
17789
18020
  * // called asynchronously if an error occurs
17790
- * // or server returns response with status
17791
- * // code outside of the <200, 400) range
18021
+ * // or server returns response with an error status.
17792
18022
  * });
17793
18023
  * </pre>
17794
18024
  *
17795
- * Since the returned value of calling the $http function is a Promise object, you can also use
18025
+ * Since the returned value of calling the $http function is a `promise`, you can also use
17796
18026
  * the `then` method to register callbacks, and these callbacks will receive a single argument –
17797
- * an object representing the response. See the api signature and type info below for more
18027
+ * an object representing the response. See the API signature and type info below for more
17798
18028
  * details.
17799
18029
  *
18030
+ * A response status code between 200 and 299 is considered a success status and
18031
+ * will result in the success callback being called. Note that if the response is a redirect,
18032
+ * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
18033
+ * called for such responses.
17800
18034
  *
17801
18035
  * # Shortcut methods
17802
18036
  *
17803
- * Since all invocation of the $http service require definition of the http method and url and
17804
- * POST and PUT requests require response body/data to be provided as well, shortcut methods
17805
- * were created to simplify using the api:
18037
+ * Since all invocations of the $http service require passing in an HTTP method and URL, and
18038
+ * POST/PUT requests require request data to be provided as well, shortcut methods
18039
+ * were created:
17806
18040
  *
17807
18041
  * <pre>
17808
18042
  * $http.get('/someUrl').success(successCallback);
@@ -17821,25 +18055,25 @@ function $HttpProvider() {
17821
18055
  *
17822
18056
  * # Setting HTTP Headers
17823
18057
  *
17824
- * The $http service will automatically add certain http headers to all requests. These defaults
18058
+ * The $http service will automatically add certain HTTP headers to all requests. These defaults
17825
18059
  * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
17826
18060
  * object, which currently contains this default configuration:
17827
18061
  *
17828
18062
  * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
17829
18063
  * - `Accept: application/json, text/plain, * / *`
17830
18064
  * - `X-Requested-With: XMLHttpRequest`
17831
- * - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
18065
+ * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
17832
18066
  * - `Content-Type: application/json`
17833
- * - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
18067
+ * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
17834
18068
  * - `Content-Type: application/json`
17835
18069
  *
17836
- * To add or overwrite these defaults, simply add or remove a property from this configuration
18070
+ * To add or overwrite these defaults, simply add or remove a property from these configuration
17837
18071
  * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
17838
- * with name equal to the lower-cased http method name, e.g.
18072
+ * with the lowercased HTTP method name as the key, e.g.
17839
18073
  * `$httpProvider.defaults.headers.get['My-Header']='value'`.
17840
18074
  *
17841
- * Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar
17842
- * fassion as described above.
18075
+ * Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
18076
+ * fashion.
17843
18077
  *
17844
18078
  *
17845
18079
  * # Transforming Requests and Responses
@@ -17849,32 +18083,36 @@ function $HttpProvider() {
17849
18083
  *
17850
18084
  * Request transformations:
17851
18085
  *
17852
- * - if the `data` property of the request config object contains an object, serialize it into
18086
+ * - If the `data` property of the request configuration object contains an object, serialize it into
17853
18087
  * JSON format.
17854
18088
  *
17855
18089
  * Response transformations:
17856
18090
  *
17857
- * - if XSRF prefix is detected, strip it (see Security Considerations section below)
17858
- * - if json response is detected, deserialize it using a JSON parser
18091
+ * - If XSRF prefix is detected, strip it (see Security Considerations section below).
18092
+ * - If JSON response is detected, deserialize it using a JSON parser.
18093
+ *
18094
+ * To globally augment or override the default transforms, modify the `$httpProvider.defaults.transformRequest` and
18095
+ * `$httpProvider.defaults.transformResponse` properties. These properties are by default an
18096
+ * array of transform functions, which allows you to `push` or `unshift` a new transformation function into the
18097
+ * transformation chain. You can also decide to completely override any default transformations by assigning your
18098
+ * transformation functions to these properties directly without the array wrapper.
17859
18099
  *
17860
- * To override these transformation locally, specify transform functions as `transformRequest`
17861
- * and/or `transformResponse` properties of the config object. To globally override the default
17862
- * transforms, override the `$httpProvider.defaults.transformRequest` and
17863
- * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`.
18100
+ * Similarly, to locally override the request/response transforms, augment the `transformRequest` and/or
18101
+ * `transformResponse` properties of the configuration object passed into `$http`.
17864
18102
  *
17865
18103
  *
17866
18104
  * # Caching
17867
18105
  *
17868
- * To enable caching set the configuration property `cache` to `true`. When the cache is
18106
+ * To enable caching, set the configuration property `cache` to `true`. When the cache is
17869
18107
  * enabled, `$http` stores the response from the server in local cache. Next time the
17870
18108
  * response is served from the cache without sending a request to the server.
17871
18109
  *
17872
18110
  * Note that even if the response is served from cache, delivery of the data is asynchronous in
17873
18111
  * the same way that real requests are.
17874
18112
  *
17875
- * If there are multiple GET requests for the same url that should be cached using the same
18113
+ * If there are multiple GET requests for the same URL that should be cached using the same
17876
18114
  * cache, but the cache is not populated yet, only one request to the server will be made and
17877
- * the remaining requests will be fulfilled using the response for the first request.
18115
+ * the remaining requests will be fulfilled using the response from the first request.
17878
18116
  *
17879
18117
  *
17880
18118
  * # Response interceptors
@@ -17926,7 +18164,7 @@ function $HttpProvider() {
17926
18164
  * When designing web applications, consider security threats from:
17927
18165
  *
17928
18166
  * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
17929
- * JSON Vulnerability}
18167
+ * JSON vulnerability}
17930
18168
  * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF}
17931
18169
  *
17932
18170
  * Both server and the client must cooperate in order to eliminate these threats. Angular comes
@@ -17936,8 +18174,8 @@ function $HttpProvider() {
17936
18174
  * ## JSON Vulnerability Protection
17937
18175
  *
17938
18176
  * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
17939
- * JSON Vulnerability} allows third party web-site to turn your JSON resource URL into
17940
- * {@link http://en.wikipedia.org/wiki/JSON#JSONP JSONP} request under some conditions. To
18177
+ * JSON vulnerability} allows third party website to turn your JSON resource URL into
18178
+ * {@link http://en.wikipedia.org/wiki/JSONP JSONP} request under some conditions. To
17941
18179
  * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
17942
18180
  * Angular will automatically strip the prefix before processing it as JSON.
17943
18181
  *
@@ -17958,19 +18196,19 @@ function $HttpProvider() {
17958
18196
  * ## Cross Site Request Forgery (XSRF) Protection
17959
18197
  *
17960
18198
  * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
17961
- * an unauthorized site can gain your user's private data. Angular provides following mechanism
18199
+ * an unauthorized site can gain your user's private data. Angular provides a mechanism
17962
18200
  * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
17963
18201
  * called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
17964
18202
  * runs on your domain could read the cookie, your server can be assured that the XHR came from
17965
18203
  * JavaScript running on your domain.
17966
18204
  *
17967
18205
  * To take advantage of this, your server needs to set a token in a JavaScript readable session
17968
- * cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
18206
+ * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
17969
18207
  * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
17970
- * that only JavaScript running on your domain could have read the token. The token must be
17971
- * unique for each user and must be verifiable by the server (to prevent the JavaScript making
18208
+ * that only JavaScript running on your domain could have sent the request. The token must be
18209
+ * unique for each user and must be verifiable by the server (to prevent the JavaScript from making
17972
18210
  * up its own tokens). We recommend that the token is a digest of your site's authentication
17973
- * cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
18211
+ * cookie with a {@link https://en.wikipedia.org/wiki/Salt_(cryptography) salt} for added security.
17974
18212
  *
17975
18213
  *
17976
18214
  * @param {object} config Object describing the request to be made and how it should be
@@ -18148,7 +18386,7 @@ function $HttpProvider() {
18148
18386
  * @methodOf ng.$http
18149
18387
  *
18150
18388
  * @description
18151
- * Shortcut method to perform `GET` request
18389
+ * Shortcut method to perform `GET` request.
18152
18390
  *
18153
18391
  * @param {string} url Relative or absolute URL specifying the destination of the request
18154
18392
  * @param {Object=} config Optional configuration object
@@ -18161,7 +18399,7 @@ function $HttpProvider() {
18161
18399
  * @methodOf ng.$http
18162
18400
  *
18163
18401
  * @description
18164
- * Shortcut method to perform `DELETE` request
18402
+ * Shortcut method to perform `DELETE` request.
18165
18403
  *
18166
18404
  * @param {string} url Relative or absolute URL specifying the destination of the request
18167
18405
  * @param {Object=} config Optional configuration object
@@ -18174,7 +18412,7 @@ function $HttpProvider() {
18174
18412
  * @methodOf ng.$http
18175
18413
  *
18176
18414
  * @description
18177
- * Shortcut method to perform `HEAD` request
18415
+ * Shortcut method to perform `HEAD` request.
18178
18416
  *
18179
18417
  * @param {string} url Relative or absolute URL specifying the destination of the request
18180
18418
  * @param {Object=} config Optional configuration object
@@ -18187,7 +18425,7 @@ function $HttpProvider() {
18187
18425
  * @methodOf ng.$http
18188
18426
  *
18189
18427
  * @description
18190
- * Shortcut method to perform `JSONP` request
18428
+ * Shortcut method to perform `JSONP` request.
18191
18429
  *
18192
18430
  * @param {string} url Relative or absolute URL specifying the destination of the request.
18193
18431
  * Should contain `JSON_CALLBACK` string.
@@ -18202,7 +18440,7 @@ function $HttpProvider() {
18202
18440
  * @methodOf ng.$http
18203
18441
  *
18204
18442
  * @description
18205
- * Shortcut method to perform `POST` request
18443
+ * Shortcut method to perform `POST` request.
18206
18444
  *
18207
18445
  * @param {string} url Relative or absolute URL specifying the destination of the request
18208
18446
  * @param {*} data Request content
@@ -18216,7 +18454,7 @@ function $HttpProvider() {
18216
18454
  * @methodOf ng.$http
18217
18455
  *
18218
18456
  * @description
18219
- * Shortcut method to perform `PUT` request
18457
+ * Shortcut method to perform `PUT` request.
18220
18458
  *
18221
18459
  * @param {string} url Relative or absolute URL specifying the destination of the request
18222
18460
  * @param {*} data Request content
@@ -18268,7 +18506,7 @@ function $HttpProvider() {
18268
18506
 
18269
18507
 
18270
18508
  /**
18271
- * Makes the request
18509
+ * Makes the request.
18272
18510
  *
18273
18511
  * !!! ACCESSES CLOSURE VARS:
18274
18512
  * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
@@ -18378,6 +18616,7 @@ function $HttpProvider() {
18378
18616
 
18379
18617
  }];
18380
18618
  }
18619
+
18381
18620
  var XHR = window.XMLHttpRequest || function() {
18382
18621
  try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
18383
18622
  try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
@@ -18445,8 +18684,30 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
18445
18684
  // always async
18446
18685
  xhr.onreadystatechange = function() {
18447
18686
  if (xhr.readyState == 4) {
18448
- completeRequest(
18449
- callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
18687
+ var responseHeaders = xhr.getAllResponseHeaders();
18688
+
18689
+ // TODO(vojta): remove once Firefox 21 gets released.
18690
+ // begin: workaround to overcome Firefox CORS http response headers bug
18691
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=608735
18692
+ // Firefox already patched in nightly. Should land in Firefox 21.
18693
+
18694
+ // CORS "simple response headers" http://www.w3.org/TR/cors/
18695
+ var value,
18696
+ simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
18697
+ "Expires", "Last-Modified", "Pragma"];
18698
+ if (!responseHeaders) {
18699
+ responseHeaders = "";
18700
+ forEach(simpleHeaders, function (header) {
18701
+ var value = xhr.getResponseHeader(header);
18702
+ if (value) {
18703
+ responseHeaders += header + ": " + value + "\n";
18704
+ }
18705
+ });
18706
+ }
18707
+ // end of the workaround.
18708
+
18709
+ completeRequest(callback, status || xhr.status, xhr.responseText,
18710
+ responseHeaders);
18450
18711
  }
18451
18712
  };
18452
18713
 
@@ -18592,17 +18853,17 @@ function $TimeoutProvider() {
18592
18853
  * block and delegates any exceptions to
18593
18854
  * {@link ng.$exceptionHandler $exceptionHandler} service.
18594
18855
  *
18595
- * The return value of registering a timeout function is a promise which will be resolved when
18856
+ * The return value of registering a timeout function is a promise, which will be resolved when
18596
18857
  * the timeout is reached and the timeout function is executed.
18597
18858
  *
18598
- * To cancel a the timeout request, call `$timeout.cancel(promise)`.
18859
+ * To cancel a timeout request, call `$timeout.cancel(promise)`.
18599
18860
  *
18600
18861
  * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
18601
18862
  * synchronously flush the queue of deferred functions.
18602
18863
  *
18603
- * @param {function()} fn A function, who's execution should be delayed.
18864
+ * @param {function()} fn A function, whose execution should be delayed.
18604
18865
  * @param {number=} [delay=0] Delay in milliseconds.
18605
- * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise
18866
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18606
18867
  * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18607
18868
  * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
18608
18869
  * promise will be resolved with is the return value of the `fn` function.
@@ -18642,7 +18903,7 @@ function $TimeoutProvider() {
18642
18903
  * @methodOf ng.$timeout
18643
18904
  *
18644
18905
  * @description
18645
- * Cancels a task associated with the `promise`. As a result of this the promise will be
18906
+ * Cancels a task associated with the `promise`. As a result of this, the promise will be
18646
18907
  * resolved with a rejection.
18647
18908
  *
18648
18909
  * @param {Promise=} promise Promise returned by the `$timeout` function.
@@ -18668,7 +18929,7 @@ function $TimeoutProvider() {
18668
18929
  *
18669
18930
  * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To
18670
18931
  * achieve this a filter definition consists of a factory function which is annotated with dependencies and is
18671
- * responsible for creating a the filter function.
18932
+ * responsible for creating a filter function.
18672
18933
  *
18673
18934
  * <pre>
18674
18935
  * // Filter registration
@@ -18730,7 +18991,7 @@ function $TimeoutProvider() {
18730
18991
  *
18731
18992
  * The general syntax in templates is as follows:
18732
18993
  *
18733
- * {{ expression | [ filter_name ] }}
18994
+ * {{ expression [| filter_name[:parameter_value] ... ] }}
18734
18995
  *
18735
18996
  * @param {String} name Name of the filter function to retrieve
18736
18997
  * @return {Function} the filter function
@@ -18806,22 +19067,22 @@ function $FilterProvider($provide) {
18806
19067
 
18807
19068
  Search: <input ng-model="searchText">
18808
19069
  <table id="searchTextResults">
18809
- <tr><th>Name</th><th>Phone</th><tr>
19070
+ <tr><th>Name</th><th>Phone</th></tr>
18810
19071
  <tr ng-repeat="friend in friends | filter:searchText">
18811
19072
  <td>{{friend.name}}</td>
18812
19073
  <td>{{friend.phone}}</td>
18813
- <tr>
19074
+ </tr>
18814
19075
  </table>
18815
19076
  <hr>
18816
19077
  Any: <input ng-model="search.$"> <br>
18817
19078
  Name only <input ng-model="search.name"><br>
18818
- Phone only <input ng-model="search.phone"å><br>
19079
+ Phone only <input ng-model="search.phone"><br>
18819
19080
  <table id="searchObjResults">
18820
- <tr><th>Name</th><th>Phone</th><tr>
19081
+ <tr><th>Name</th><th>Phone</th></tr>
18821
19082
  <tr ng-repeat="friend in friends | filter:search">
18822
19083
  <td>{{friend.name}}</td>
18823
19084
  <td>{{friend.phone}}</td>
18824
- <tr>
19085
+ </tr>
18825
19086
  </table>
18826
19087
  </doc:source>
18827
19088
  <doc:scenario>
@@ -18845,7 +19106,7 @@ function $FilterProvider($provide) {
18845
19106
  */
18846
19107
  function filterFilter() {
18847
19108
  return function(array, expression) {
18848
- if (!(array instanceof Array)) return array;
19109
+ if (!isArray(array)) return array;
18849
19110
  var predicates = [];
18850
19111
  predicates.check = function(value) {
18851
19112
  for (var j = 0; j < predicates.length; j++) {
@@ -19094,7 +19355,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
19094
19355
  fraction += '0';
19095
19356
  }
19096
19357
 
19097
- if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize);
19358
+ if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
19098
19359
  }
19099
19360
 
19100
19361
  parts.push(isNegative ? pattern.negPre : pattern.posPre);
@@ -19118,6 +19379,7 @@ function padNumber(num, digits, trim) {
19118
19379
 
19119
19380
 
19120
19381
  function dateGetter(name, size, offset, trim) {
19382
+ offset = offset || 0;
19121
19383
  return function(date) {
19122
19384
  var value = date['get' + name]();
19123
19385
  if (offset > 0 || value > -offset)
@@ -19137,8 +19399,13 @@ function dateStrGetter(name, shortForm) {
19137
19399
  }
19138
19400
 
19139
19401
  function timeZoneGetter(date) {
19140
- var offset = date.getTimezoneOffset();
19141
- return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
19402
+ var zone = -1 * date.getTimezoneOffset();
19403
+ var paddedZone = (zone >= 0) ? "+" : "";
19404
+
19405
+ paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
19406
+ padNumber(Math.abs(zone % 60), 2);
19407
+
19408
+ return paddedZone;
19142
19409
  }
19143
19410
 
19144
19411
  function ampmGetter(date, formats) {
@@ -19202,7 +19469,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
19202
19469
  * * `'ss'`: Second in minute, padded (00-59)
19203
19470
  * * `'s'`: Second in minute (0-59)
19204
19471
  * * `'a'`: am/pm marker
19205
- * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200)
19472
+ * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19206
19473
  *
19207
19474
  * `format` string can also be one of the following predefined
19208
19475
  * {@link guide/i18n localizable formats}:
@@ -19223,8 +19490,9 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
19223
19490
  * (e.g. `"h o''clock"`).
19224
19491
  *
19225
19492
  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19226
- * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
19227
- * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
19493
+ * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
19494
+ * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19495
+ * specified in the string input, the time is considered to be in the local timezone.
19228
19496
  * @param {string=} format Formatting rules (see Description). If not specified,
19229
19497
  * `mediumDate` is used.
19230
19498
  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
@@ -19244,7 +19512,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
19244
19512
  expect(binding("1288323623006 | date:'medium'")).
19245
19513
  toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19246
19514
  expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
19247
- toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
19515
+ toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19248
19516
  expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
19249
19517
  toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19250
19518
  });
@@ -19513,12 +19781,12 @@ function limitToFilter(){
19513
19781
  (<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
19514
19782
  <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
19515
19783
  <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
19516
- <tr>
19784
+ </tr>
19517
19785
  <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19518
19786
  <td>{{friend.name}}</td>
19519
19787
  <td>{{friend.phone}}</td>
19520
19788
  <td>{{friend.age}}</td>
19521
- <tr>
19789
+ </tr>
19522
19790
  </table>
19523
19791
  </div>
19524
19792
  </doc:source>
@@ -19550,7 +19818,7 @@ function limitToFilter(){
19550
19818
  orderByFilter.$inject = ['$parse'];
19551
19819
  function orderByFilter($parse){
19552
19820
  return function(array, sortPredicate, reverseOrder) {
19553
- if (!(array instanceof Array)) return array;
19821
+ if (!isArray(array)) return array;
19554
19822
  if (!sortPredicate) return array;
19555
19823
  sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
19556
19824
  sortPredicate = map(sortPredicate, function(predicate){
@@ -19618,15 +19886,25 @@ function ngDirective(directive) {
19618
19886
  *
19619
19887
  * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
19620
19888
  * without changing the location or causing page reloads, e.g.:
19621
- * <a href="" ng-click="model.$save()">Save</a>
19889
+ * `<a href="" ng-click="model.$save()">Save</a>`
19622
19890
  */
19623
19891
  var htmlAnchorDirective = valueFn({
19624
19892
  restrict: 'E',
19625
19893
  compile: function(element, attr) {
19626
- // turn <a href ng-click="..">link</a> into a link in IE
19627
- // but only if it doesn't have name attribute, in which case it's an anchor
19628
- if (!attr.href) {
19629
- attr.$set('href', '');
19894
+
19895
+ if (msie <= 8) {
19896
+
19897
+ // turn <a href ng-click="..">link</a> into a stylable link in IE
19898
+ // but only if it doesn't have name attribute, in which case it's an anchor
19899
+ if (!attr.href && !attr.name) {
19900
+ attr.$set('href', '');
19901
+ }
19902
+
19903
+ // add a comment node to anchors to workaround IE bug that causes element content to be reset
19904
+ // to new attribute content if attribute is updated with value containing @ and element also
19905
+ // contains value with @
19906
+ // see issue #1949
19907
+ element.append(document.createComment('IE fix'));
19630
19908
  }
19631
19909
 
19632
19910
  return function(scope, element) {
@@ -19634,7 +19912,6 @@ var htmlAnchorDirective = valueFn({
19634
19912
  // if we have no href url, then don't navigate anywhere.
19635
19913
  if (!element.attr('href')) {
19636
19914
  event.preventDefault();
19637
- return false; // Needed for opera
19638
19915
  }
19639
19916
  });
19640
19917
  }
@@ -19707,7 +19984,7 @@ var htmlAnchorDirective = valueFn({
19707
19984
  it('should execute ng-click but not reload when no href but name specified', function() {
19708
19985
  element('#link-5').click();
19709
19986
  expect(input('value').val()).toEqual('5');
19710
- expect(element('#link-5').attr('href')).toBe('');
19987
+ expect(element('#link-5').attr('href')).toBe(undefined);
19711
19988
  });
19712
19989
 
19713
19990
  it('should only change url when only ng-href', function() {
@@ -19950,8 +20227,9 @@ forEach(['src', 'href'], function(attrName) {
19950
20227
 
19951
20228
  // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
19952
20229
  // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
19953
- // to set the property as well to achieve the desired effect
19954
- if (msie) element.prop(attrName, value);
20230
+ // to set the property as well to achieve the desired effect.
20231
+ // we use attr[attrName] value since $set can sanitize the url.
20232
+ if (msie) element.prop(attrName, attr[attrName]);
19955
20233
  });
19956
20234
  }
19957
20235
  };
@@ -19971,13 +20249,13 @@ var nullFormCtrl = {
19971
20249
  *
19972
20250
  * @property {boolean} $pristine True if user has not interacted with the form yet.
19973
20251
  * @property {boolean} $dirty True if user has already interacted with the form.
19974
- * @property {boolean} $valid True if all of the containg forms and controls are valid.
20252
+ * @property {boolean} $valid True if all of the containing forms and controls are valid.
19975
20253
  * @property {boolean} $invalid True if at least one containing control or form is invalid.
19976
20254
  *
19977
20255
  * @property {Object} $error Is an object hash, containing references to all invalid controls or
19978
20256
  * forms, where:
19979
20257
  *
19980
- * - keys are validation tokens (error names) — such as `REQUIRED`, `URL` or `EMAIL`),
20258
+ * - keys are validation tokens (error names) — such as `required`, `url` or `email`),
19981
20259
  * - values are arrays of controls or forms that are invalid with given error.
19982
20260
  *
19983
20261
  * @description
@@ -20090,7 +20368,7 @@ function FormController(element, attrs) {
20090
20368
  * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20091
20369
  * sub-group of controls needs to be determined.
20092
20370
  *
20093
- * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20371
+ * @param {string=} name|ngForm Name of the form. If specified, the form controller will be published into
20094
20372
  * related scope, under this name.
20095
20373
  *
20096
20374
  */
@@ -20163,12 +20441,12 @@ function FormController(element, attrs) {
20163
20441
  </script>
20164
20442
  <form name="myForm" ng-controller="Ctrl">
20165
20443
  userType: <input name="input" ng-model="userType" required>
20166
- <span class="error" ng-show="myForm.input.$error.REQUIRED">Required!</span><br>
20444
+ <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20167
20445
  <tt>userType = {{userType}}</tt><br>
20168
20446
  <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
20169
20447
  <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
20170
20448
  <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
20171
- <tt>myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}</tt><br>
20449
+ <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
20172
20450
  </form>
20173
20451
  </doc:source>
20174
20452
  <doc:scenario>
@@ -20330,8 +20608,8 @@ var inputType = {
20330
20608
  *
20331
20609
  * @param {string} ngModel Assignable angular expression to data-bind to.
20332
20610
  * @param {string=} name Property name of the form under which the control is published.
20333
- * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
20334
- * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
20611
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20612
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20335
20613
  * @param {string=} required Sets `required` validation error key if the value is not entered.
20336
20614
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20337
20615
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -20643,6 +20921,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20643
20921
  } else {
20644
20922
  var timeout;
20645
20923
 
20924
+ var deferListener = function() {
20925
+ if (!timeout) {
20926
+ timeout = $browser.defer(function() {
20927
+ listener();
20928
+ timeout = null;
20929
+ });
20930
+ }
20931
+ };
20932
+
20646
20933
  element.bind('keydown', function(event) {
20647
20934
  var key = event.keyCode;
20648
20935
 
@@ -20650,16 +20937,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20650
20937
  // command modifiers arrows
20651
20938
  if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
20652
20939
 
20653
- if (!timeout) {
20654
- timeout = $browser.defer(function() {
20655
- listener();
20656
- timeout = null;
20657
- });
20658
- }
20940
+ deferListener();
20659
20941
  });
20660
20942
 
20661
20943
  // if user paste into input using mouse, we need "change" event to catch it
20662
20944
  element.bind('change', listener);
20945
+
20946
+ // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
20947
+ if ($sniffer.hasEvent('paste')) {
20948
+ element.bind('paste cut', deferListener);
20949
+ }
20663
20950
  }
20664
20951
 
20665
20952
 
@@ -20958,7 +21245,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
20958
21245
  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
20959
21246
  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
20960
21247
  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
20961
- <tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br>
21248
+ <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
20962
21249
  <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
20963
21250
  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
20964
21251
  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
@@ -21221,7 +21508,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
21221
21508
  * For example {@link ng.directive:input input} or
21222
21509
  * {@link ng.directive:select select} directives call it.
21223
21510
  *
21224
- * It internally calls all `formatters` and if resulted value is valid, updates the model and
21511
+ * It internally calls all `parsers` and if resulted value is valid, updates the model and
21225
21512
  * calls all registered change listeners.
21226
21513
  *
21227
21514
  * @param {string} value Value from the view.
@@ -21527,7 +21814,7 @@ var ngValueDirective = function() {
21527
21814
  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
21528
21815
  * `{{ expression }}` which is similar but less verbose.
21529
21816
  *
21530
- * Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when
21817
+ * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
21531
21818
  * it's desirable to put bindings into template that is momentarily displayed by the browser in its
21532
21819
  * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
21533
21820
  * bindings invisible to the user while the page is loading.
@@ -21656,6 +21943,7 @@ var ngBindHtmlUnsafeDirective = [function() {
21656
21943
  function classDirective(name, selector) {
21657
21944
  name = 'ngClass' + name;
21658
21945
  return ngDirective(function(scope, element, attr) {
21946
+ var oldVal = undefined;
21659
21947
 
21660
21948
  scope.$watch(attr[name], ngClassWatchAction, true);
21661
21949
 
@@ -21667,9 +21955,9 @@ function classDirective(name, selector) {
21667
21955
 
21668
21956
  if (name !== 'ngClass') {
21669
21957
  scope.$watch('$index', function($index, old$index) {
21670
- var mod = $index % 2;
21671
- if (mod !== old$index % 2) {
21672
- if (mod == selector) {
21958
+ var mod = $index & 1;
21959
+ if (mod !== old$index & 1) {
21960
+ if (mod === selector) {
21673
21961
  addClass(scope.$eval(attr[name]));
21674
21962
  } else {
21675
21963
  removeClass(scope.$eval(attr[name]));
@@ -21679,13 +21967,14 @@ function classDirective(name, selector) {
21679
21967
  }
21680
21968
 
21681
21969
 
21682
- function ngClassWatchAction(newVal, oldVal) {
21970
+ function ngClassWatchAction(newVal) {
21683
21971
  if (selector === true || scope.$index % 2 === selector) {
21684
- if (oldVal && (newVal !== oldVal)) {
21972
+ if (oldVal && !equals(newVal,oldVal)) {
21685
21973
  removeClass(oldVal);
21686
21974
  }
21687
21975
  addClass(newVal);
21688
21976
  }
21977
+ oldVal = copy(newVal);
21689
21978
  }
21690
21979
 
21691
21980
 
@@ -21718,7 +22007,7 @@ function classDirective(name, selector) {
21718
22007
  *
21719
22008
  * The directive won't add duplicate classes if a particular class was already set.
21720
22009
  *
21721
- * When the expression changes, the previously added classes are removed and only then the classes
22010
+ * When the expression changes, the previously added classes are removed and only then the
21722
22011
  * new classes are added.
21723
22012
  *
21724
22013
  * @element ANY
@@ -21811,7 +22100,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
21811
22100
  * @name ng.directive:ngClassEven
21812
22101
  *
21813
22102
  * @description
21814
- * The `ngClassOdd` and `ngClassEven` works exactly as
22103
+ * The `ngClassOdd` and `ngClassEven` directives work exactly as
21815
22104
  * {@link ng.directive:ngClass ngClass}, except it works in
21816
22105
  * conjunction with `ngRepeat` and takes affect only on odd (even) rows.
21817
22106
  *
@@ -21869,7 +22158,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
21869
22158
  * `angular.min.js` files. Following is the css rule:
21870
22159
  *
21871
22160
  * <pre>
21872
- * [ng\:cloak], [ng-cloak], .ng-cloak {
22161
+ * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
21873
22162
  * display: none;
21874
22163
  * }
21875
22164
  * </pre>
@@ -21928,8 +22217,7 @@ var ngCloakDirective = ngDirective({
21928
22217
  * * Controller — The `ngController` directive specifies a Controller class; the class has
21929
22218
  * methods that typically express the business logic behind the application.
21930
22219
  *
21931
- * Note that an alternative way to define controllers is via the `{@link ng.$route}`
21932
- * service.
22220
+ * Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
21933
22221
  *
21934
22222
  * @element ANY
21935
22223
  * @scope
@@ -22020,16 +22308,32 @@ var ngControllerDirective = [function() {
22020
22308
  * @name ng.directive:ngCsp
22021
22309
  * @priority 1000
22022
22310
  *
22311
+ * @element html
22023
22312
  * @description
22024
22313
  * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
22025
- * This directive should be used on the root element of the application (typically the `<html>`
22026
- * element or other element with the {@link ng.directive:ngApp ngApp}
22027
- * directive).
22028
- *
22029
- * If enabled the performance of template expression evaluator will suffer slightly, so don't enable
22030
- * this mode unless you need it.
22031
- *
22032
- * @element html
22314
+ *
22315
+ * This is necessary when developing things like Google Chrome Extensions.
22316
+ *
22317
+ * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
22318
+ * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
22319
+ * any of these restrictions.
22320
+ *
22321
+ * AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
22322
+ * it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
22323
+ * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
22324
+ * be raised.
22325
+ *
22326
+ * In order to use this feature put `ngCsp` directive on the root element of the application.
22327
+ *
22328
+ * @example
22329
+ * This example shows how to apply the `ngCsp` directive to the `html` tag.
22330
+ <pre>
22331
+ <!doctype html>
22332
+ <html ng-app ng-csp>
22333
+ ...
22334
+ ...
22335
+ </html>
22336
+ </pre>
22033
22337
  */
22034
22338
 
22035
22339
  var ngCspDirective = ['$sniffer', function($sniffer) {
@@ -22654,7 +22958,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
22654
22958
  if (!isNaN(value)) {
22655
22959
  //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
22656
22960
  //check it against pluralization rules in $locale service
22657
- if (!whens[value]) value = $locale.pluralCat(value - offset);
22961
+ if (!(value in whens)) value = $locale.pluralCat(value - offset);
22658
22962
  return whensExpFns[value](scope, element, true);
22659
22963
  } else {
22660
22964
  return '';
@@ -22758,14 +23062,17 @@ var ngRepeatDirective = ngDirective({
22758
23062
  scope.$watch(function ngRepeatWatch(scope){
22759
23063
  var index, length,
22760
23064
  collection = scope.$eval(rhs),
22761
- collectionLength = size(collection, true),
22762
- childScope,
23065
+ cursor = iterStartElement, // current position of the node
22763
23066
  // Same as lastOrder but it has the current state. It will become the
22764
23067
  // lastOrder on the next iteration.
22765
23068
  nextOrder = new HashQueueMap(),
23069
+ arrayBound,
23070
+ childScope,
22766
23071
  key, value, // key/value of iteration
22767
- array, last, // last object information {scope, element, index}
22768
- cursor = iterStartElement; // current position of the node
23072
+ array,
23073
+ last; // last object information {scope, element, index}
23074
+
23075
+
22769
23076
 
22770
23077
  if (!isArray(collection)) {
22771
23078
  // if object, extract keys, sort them and use to determine order of iteration over obj props
@@ -22780,6 +23087,8 @@ var ngRepeatDirective = ngDirective({
22780
23087
  array = collection || [];
22781
23088
  }
22782
23089
 
23090
+ arrayBound = array.length-1;
23091
+
22783
23092
  // we are not using forEach for perf reasons (trying to avoid #call)
22784
23093
  for (index = 0, length = array.length; index < length; index++) {
22785
23094
  key = (collection === array) ? index : array[index];
@@ -22815,7 +23124,7 @@ var ngRepeatDirective = ngDirective({
22815
23124
  childScope.$index = index;
22816
23125
 
22817
23126
  childScope.$first = (index === 0);
22818
- childScope.$last = (index === (collectionLength - 1));
23127
+ childScope.$last = (index === arrayBound);
22819
23128
  childScope.$middle = !(childScope.$first || childScope.$last);
22820
23129
 
22821
23130
  if (!last) {
@@ -22982,11 +23291,13 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
22982
23291
  * @description
22983
23292
  * Conditionally change the DOM structure.
22984
23293
  *
22985
- * @usageContent
22986
- * <ANY ng-switch-when="matchValue1">...</ANY>
23294
+ * @usage
23295
+ * <ANY ng-switch="expression">
23296
+ * <ANY ng-switch-when="matchValue1">...</ANY>
22987
23297
  * <ANY ng-switch-when="matchValue2">...</ANY>
22988
23298
  * ...
22989
23299
  * <ANY ng-switch-default>...</ANY>
23300
+ * </ANY>
22990
23301
  *
22991
23302
  * @scope
22992
23303
  * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
@@ -23036,52 +23347,54 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
23036
23347
  var NG_SWITCH = 'ng-switch';
23037
23348
  var ngSwitchDirective = valueFn({
23038
23349
  restrict: 'EA',
23039
- compile: function(element, attr) {
23350
+ require: 'ngSwitch',
23351
+ // asks for $scope to fool the BC controller module
23352
+ controller: ['$scope', function ngSwitchController() {
23353
+ this.cases = {};
23354
+ }],
23355
+ link: function(scope, element, attr, ctrl) {
23040
23356
  var watchExpr = attr.ngSwitch || attr.on,
23041
- cases = {};
23042
-
23043
- element.data(NG_SWITCH, cases);
23044
- return function(scope, element){
23045
- var selectedTransclude,
23046
- selectedElement,
23047
- selectedScope;
23048
-
23049
- scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
23050
- if (selectedElement) {
23051
- selectedScope.$destroy();
23052
- selectedElement.remove();
23053
- selectedElement = selectedScope = null;
23054
- }
23055
- if ((selectedTransclude = cases['!' + value] || cases['?'])) {
23056
- scope.$eval(attr.change);
23057
- selectedScope = scope.$new();
23058
- selectedTransclude(selectedScope, function(caseElement) {
23059
- selectedElement = caseElement;
23060
- element.append(caseElement);
23061
- });
23062
- }
23063
- });
23064
- };
23357
+ selectedTransclude,
23358
+ selectedElement,
23359
+ selectedScope;
23360
+
23361
+ scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
23362
+ if (selectedElement) {
23363
+ selectedScope.$destroy();
23364
+ selectedElement.remove();
23365
+ selectedElement = selectedScope = null;
23366
+ }
23367
+ if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
23368
+ scope.$eval(attr.change);
23369
+ selectedScope = scope.$new();
23370
+ selectedTransclude(selectedScope, function(caseElement) {
23371
+ selectedElement = caseElement;
23372
+ element.append(caseElement);
23373
+ });
23374
+ }
23375
+ });
23065
23376
  }
23066
23377
  });
23067
23378
 
23068
23379
  var ngSwitchWhenDirective = ngDirective({
23069
23380
  transclude: 'element',
23070
23381
  priority: 500,
23382
+ require: '^ngSwitch',
23071
23383
  compile: function(element, attrs, transclude) {
23072
- var cases = element.inheritedData(NG_SWITCH);
23073
- assertArg(cases);
23074
- cases['!' + attrs.ngSwitchWhen] = transclude;
23384
+ return function(scope, element, attr, ctrl) {
23385
+ ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
23386
+ };
23075
23387
  }
23076
23388
  });
23077
23389
 
23078
23390
  var ngSwitchDefaultDirective = ngDirective({
23079
23391
  transclude: 'element',
23080
23392
  priority: 500,
23393
+ require: '^ngSwitch',
23081
23394
  compile: function(element, attrs, transclude) {
23082
- var cases = element.inheritedData(NG_SWITCH);
23083
- assertArg(cases);
23084
- cases['?'] = transclude;
23395
+ return function(scope, element, attr, ctrl) {
23396
+ ctrl.cases['?'] = transclude;
23397
+ };
23085
23398
  }
23086
23399
  });
23087
23400
 
@@ -23170,7 +23483,7 @@ var ngTranscludeDirective = ngDirective({
23170
23483
  <hr />
23171
23484
 
23172
23485
  <pre>$location.path() = {{$location.path()}}</pre>
23173
- <pre>$route.current.template = {{$route.current.template}}</pre>
23486
+ <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
23174
23487
  <pre>$route.current.params = {{$route.current.params}}</pre>
23175
23488
  <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
23176
23489
  <pre>$routeParams = {{$routeParams}}</pre>
@@ -23289,7 +23602,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
23289
23602
  if (current.controller) {
23290
23603
  locals.$scope = lastScope;
23291
23604
  controller = $controller(current.controller, locals);
23292
- element.contents().data('$ngControllerController', controller);
23605
+ element.children().data('$ngControllerController', controller);
23293
23606
  }
23294
23607
 
23295
23608
  link(lastScope);
@@ -23378,7 +23691,8 @@ var scriptDirective = ['$templateCache', function($templateCache) {
23378
23691
  * `select` model to be bound to a non-string value. This is because an option element can currently
23379
23692
  * be bound to string values only.
23380
23693
  *
23381
- * @param {string} name assignable expression to data-bind to.
23694
+ * @param {string} ngModel Assignable angular expression to data-bind to.
23695
+ * @param {string=} name Property name of the form under which the control is published.
23382
23696
  * @param {string=} required The control is considered valid only if value is entered.
23383
23697
  * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23384
23698
  * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -23473,7 +23787,7 @@ var scriptDirective = ['$templateCache', function($templateCache) {
23473
23787
 
23474
23788
  var ngOptionsDirective = valueFn({ terminal: true });
23475
23789
  var selectDirective = ['$compile', '$parse', function($compile, $parse) {
23476
- //00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
23790
+ //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
23477
23791
  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+(.*)$/,
23478
23792
  nullModelCtrl = {$setViewValue: noop};
23479
23793
 
@@ -23616,7 +23930,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
23616
23930
  var lastView;
23617
23931
  ctrl.$render = function() {
23618
23932
  var items = new HashMap(ctrl.$viewValue);
23619
- forEach(selectElement.children(), function(option) {
23933
+ forEach(selectElement.find('option'), function(option) {
23620
23934
  option.selected = isDefined(items.get(option.value));
23621
23935
  });
23622
23936
  };
@@ -23633,7 +23947,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
23633
23947
  selectElement.bind('change', function() {
23634
23948
  scope.$apply(function() {
23635
23949
  var array = [];
23636
- forEach(selectElement.children(), function(option) {
23950
+ forEach(selectElement.find('option'), function(option) {
23637
23951
  if (option.selected) {
23638
23952
  array.push(option.value);
23639
23953
  }
@@ -23745,10 +24059,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
23745
24059
 
23746
24060
  if (multiple) {
23747
24061
  selectedSet = new HashMap(modelValue);
23748
- } else if (modelValue === null || nullOption) {
23749
- // if we are not multiselect, and we are null then we have to add the nullOption
23750
- optionGroups[''].push({selected:modelValue === null, id:'', label:''});
23751
- selectedSet = true;
23752
24062
  }
23753
24063
 
23754
24064
  // We now build up the list of options we need (we merge later)
@@ -23773,9 +24083,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
23773
24083
  selected: selected // determine if we should be selected
23774
24084
  });
23775
24085
  }
23776
- if (!multiple && !selectedSet) {
23777
- // nothing was selected, we have to insert the undefined item
23778
- optionGroups[''].unshift({id:'?', label:'', selected:true});
24086
+ if (!multiple) {
24087
+ if (nullOption || modelValue === null) {
24088
+ // insert null option if we have a placeholder, or the model is null
24089
+ optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
24090
+ } else if (!selectedSet) {
24091
+ // option could not be found, we have to insert the undefined item
24092
+ optionGroups[''].unshift({id:'?', label:'', selected:true});
24093
+ }
23779
24094
  }
23780
24095
 
23781
24096
  // Now we need to update the list of DOM nodes to match the optionGroups we computed above
@@ -23819,7 +24134,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
23819
24134
  if (existingOption.id !== option.id) {
23820
24135
  lastElement.val(existingOption.id = option.id);
23821
24136
  }
23822
- if (existingOption.element.selected !== option.selected) {
24137
+ // lastElement.prop('selected') provided by jQuery has side-effects
24138
+ if (lastElement[0].selected !== option.selected) {
23823
24139
  lastElement.prop('selected', (existingOption.selected = option.selected));
23824
24140
  }
23825
24141
  } else {
@@ -24911,160 +25227,6 @@ angular.scenario.ObjectModel.Step.prototype.setErrorStatus = function(status, er
24911
25227
  this.line = line;
24912
25228
  };
24913
25229
 
24914
- /**
24915
- * The representation of define blocks. Don't used directly, instead use
24916
- * define() in your tests.
24917
- *
24918
- * @param {string} descName Name of the block
24919
- * @param {Object} parent describe or undefined if the root.
24920
- */
24921
- angular.scenario.Describe = function(descName, parent) {
24922
- this.only = parent && parent.only;
24923
- this.beforeEachFns = [];
24924
- this.afterEachFns = [];
24925
- this.its = [];
24926
- this.children = [];
24927
- this.name = descName;
24928
- this.parent = parent;
24929
- this.id = angular.scenario.Describe.id++;
24930
-
24931
- /**
24932
- * Calls all before functions.
24933
- */
24934
- var beforeEachFns = this.beforeEachFns;
24935
- this.setupBefore = function() {
24936
- if (parent) parent.setupBefore.call(this);
24937
- angular.forEach(beforeEachFns, function(fn) { fn.call(this); }, this);
24938
- };
24939
-
24940
- /**
24941
- * Calls all after functions.
24942
- */
24943
- var afterEachFns = this.afterEachFns;
24944
- this.setupAfter = function() {
24945
- angular.forEach(afterEachFns, function(fn) { fn.call(this); }, this);
24946
- if (parent) parent.setupAfter.call(this);
24947
- };
24948
- };
24949
-
24950
- // Shared Unique ID generator for every describe block
24951
- angular.scenario.Describe.id = 0;
24952
-
24953
- // Shared Unique ID generator for every it (spec)
24954
- angular.scenario.Describe.specId = 0;
24955
-
24956
- /**
24957
- * Defines a block to execute before each it or nested describe.
24958
- *
24959
- * @param {function()} body Body of the block.
24960
- */
24961
- angular.scenario.Describe.prototype.beforeEach = function(body) {
24962
- this.beforeEachFns.push(body);
24963
- };
24964
-
24965
- /**
24966
- * Defines a block to execute after each it or nested describe.
24967
- *
24968
- * @param {function()} body Body of the block.
24969
- */
24970
- angular.scenario.Describe.prototype.afterEach = function(body) {
24971
- this.afterEachFns.push(body);
24972
- };
24973
-
24974
- /**
24975
- * Creates a new describe block that's a child of this one.
24976
- *
24977
- * @param {string} name Name of the block. Appended to the parent block's name.
24978
- * @param {function()} body Body of the block.
24979
- */
24980
- angular.scenario.Describe.prototype.describe = function(name, body) {
24981
- var child = new angular.scenario.Describe(name, this);
24982
- this.children.push(child);
24983
- body.call(child);
24984
- };
24985
-
24986
- /**
24987
- * Same as describe() but makes ddescribe blocks the only to run.
24988
- *
24989
- * @param {string} name Name of the test.
24990
- * @param {function()} body Body of the block.
24991
- */
24992
- angular.scenario.Describe.prototype.ddescribe = function(name, body) {
24993
- var child = new angular.scenario.Describe(name, this);
24994
- child.only = true;
24995
- this.children.push(child);
24996
- body.call(child);
24997
- };
24998
-
24999
- /**
25000
- * Use to disable a describe block.
25001
- */
25002
- angular.scenario.Describe.prototype.xdescribe = angular.noop;
25003
-
25004
- /**
25005
- * Defines a test.
25006
- *
25007
- * @param {string} name Name of the test.
25008
- * @param {function()} vody Body of the block.
25009
- */
25010
- angular.scenario.Describe.prototype.it = function(name, body) {
25011
- this.its.push({
25012
- id: angular.scenario.Describe.specId++,
25013
- definition: this,
25014
- only: this.only,
25015
- name: name,
25016
- before: this.setupBefore,
25017
- body: body,
25018
- after: this.setupAfter
25019
- });
25020
- };
25021
-
25022
- /**
25023
- * Same as it() but makes iit tests the only test to run.
25024
- *
25025
- * @param {string} name Name of the test.
25026
- * @param {function()} body Body of the block.
25027
- */
25028
- angular.scenario.Describe.prototype.iit = function(name, body) {
25029
- this.it.apply(this, arguments);
25030
- this.its[this.its.length-1].only = true;
25031
- };
25032
-
25033
- /**
25034
- * Use to disable a test block.
25035
- */
25036
- angular.scenario.Describe.prototype.xit = angular.noop;
25037
-
25038
- /**
25039
- * Gets an array of functions representing all the tests (recursively).
25040
- * that can be executed with SpecRunner's.
25041
- *
25042
- * @return {Array<Object>} Array of it blocks {
25043
- * definition : Object // parent Describe
25044
- * only: boolean
25045
- * name: string
25046
- * before: Function
25047
- * body: Function
25048
- * after: Function
25049
- * }
25050
- */
25051
- angular.scenario.Describe.prototype.getSpecs = function() {
25052
- var specs = arguments[0] || [];
25053
- angular.forEach(this.children, function(child) {
25054
- child.getSpecs(specs);
25055
- });
25056
- angular.forEach(this.its, function(it) {
25057
- specs.push(it);
25058
- });
25059
- var only = [];
25060
- angular.forEach(specs, function(it) {
25061
- if (it.only) {
25062
- only.push(it);
25063
- }
25064
- });
25065
- return (only.length && only) || specs;
25066
- };
25067
-
25068
25230
  /**
25069
25231
  * Runner for scenarios
25070
25232
  *
@@ -25635,13 +25797,13 @@ angular.scenario.dsl('binding', function() {
25635
25797
  */
25636
25798
  angular.scenario.dsl('input', function() {
25637
25799
  var chain = {};
25638
- var supportInputEvent = 'oninput' in document.createElement('div');
25800
+ var supportInputEvent = 'oninput' in document.createElement('div') && msie != 9;
25639
25801
 
25640
25802
  chain.enter = function(value, event) {
25641
25803
  return this.addFutureAction("input '" + this.name + "' enter '" + value + "'", function($window, $document, done) {
25642
25804
  var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
25643
25805
  input.val(value);
25644
- input.trigger(event || supportInputEvent && 'input' || 'change');
25806
+ input.trigger(event || (supportInputEvent ? 'input' : 'change'));
25645
25807
  done();
25646
25808
  });
25647
25809
  };
@@ -26108,7 +26270,7 @@ angular.scenario.output('xml', function(context, runner, model) {
26108
26270
  if (step.error) {
26109
26271
  var error = $('<error></error>');
26110
26272
  stepContext.append(error);
26111
- error.text(formatException(stepContext.error));
26273
+ error.text(formatException(step.error));
26112
26274
  }
26113
26275
  });
26114
26276
  });
@@ -26121,6 +26283,7 @@ angular.scenario.output('xml', function(context, runner, model) {
26121
26283
  angular.scenario.output('object', function(context, runner, model) {
26122
26284
  runner.$window.$result = model.value;
26123
26285
  });
26286
+
26124
26287
  bindJQuery();
26125
26288
  publishExternalAPI(angular);
26126
26289
 
@@ -26144,4 +26307,4 @@ if (config.autotest) {
26144
26307
  })(window, document);
26145
26308
 
26146
26309
  angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";\n\n[ng\\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],\n.ng-cloak, .x-ng-cloak {\n display: none;\n}\n\nng\\:form {\n display: block;\n}\n</style>');
26147
- angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";\n/* CSS Document */\n\n/** Structure */\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n font-size: 14px;\n}\n\n#system-error {\n font-size: 1.5em;\n text-align: center;\n}\n\n#json, #xml {\n display: none;\n}\n\n#header {\n position: fixed;\n width: 100%;\n}\n\n#specs {\n padding-top: 50px;\n}\n\n#header .angular {\n font-family: Courier New, monospace;\n font-weight: bold;\n}\n\n#header h1 {\n font-weight: normal;\n float: left;\n font-size: 30px;\n line-height: 30px;\n margin: 0;\n padding: 10px 10px;\n height: 30px;\n}\n\n#application h2,\n#specs h2 {\n margin: 0;\n padding: 0.5em;\n font-size: 1.1em;\n}\n\n#status-legend {\n margin-top: 10px;\n margin-right: 10px;\n}\n\n#header,\n#application,\n.test-info,\n.test-actions li {\n overflow: hidden;\n}\n\n#application {\n margin: 10px;\n}\n\n#application iframe {\n width: 100%;\n height: 758px;\n}\n\n#application .popout {\n float: right;\n}\n\n#application iframe {\n border: none;\n}\n\n.tests li,\n.test-actions li,\n.test-it li,\n.test-it ol,\n.status-display {\n list-style-type: none;\n}\n\n.tests,\n.test-it ol,\n.status-display {\n margin: 0;\n padding: 0;\n}\n\n.test-info {\n margin-left: 1em;\n margin-top: 0.5em;\n border-radius: 8px 0 0 8px;\n -webkit-border-radius: 8px 0 0 8px;\n -moz-border-radius: 8px 0 0 8px;\n cursor: pointer;\n}\n\n.test-info:hover .test-name {\n text-decoration: underline;\n}\n\n.test-info .closed:before {\n content: \'\\25b8\\00A0\';\n}\n\n.test-info .open:before {\n content: \'\\25be\\00A0\';\n font-weight: bold;\n}\n\n.test-it ol {\n margin-left: 2.5em;\n}\n\n.status-display,\n.status-display li {\n float: right;\n}\n\n.status-display li {\n padding: 5px 10px;\n}\n\n.timer-result,\n.test-title {\n display: inline-block;\n margin: 0;\n padding: 4px;\n}\n\n.test-actions .test-title,\n.test-actions .test-result {\n display: table-cell;\n padding-left: 0.5em;\n padding-right: 0.5em;\n}\n\n.test-actions {\n display: table;\n}\n\n.test-actions li {\n display: table-row;\n}\n\n.timer-result {\n width: 4em;\n padding: 0 10px;\n text-align: right;\n font-family: monospace;\n}\n\n.test-it pre,\n.test-actions pre {\n clear: left;\n color: black;\n margin-left: 6em;\n}\n\n.test-describe {\n padding-bottom: 0.5em;\n}\n\n.test-describe .test-describe {\n margin: 5px 5px 10px 2em;\n}\n\n.test-actions .status-pending .test-title:before {\n content: \'\\00bb\\00A0\';\n}\n\n.scrollpane {\n max-height: 20em;\n overflow: auto;\n}\n\n/** Colors */\n\n#header {\n background-color: #F2C200;\n}\n\n#specs h2 {\n border-top: 2px solid #BABAD1;\n}\n\n#specs h2,\n#application h2 {\n background-color: #efefef;\n}\n\n#application {\n border: 1px solid #BABAD1;\n}\n\n.test-describe .test-describe {\n border-left: 1px solid #BABAD1;\n border-right: 1px solid #BABAD1;\n border-bottom: 1px solid #BABAD1;\n}\n\n.status-display {\n border: 1px solid #777;\n}\n\n.status-display .status-pending,\n.status-pending .test-info {\n background-color: #F9EEBC;\n}\n\n.status-display .status-success,\n.status-success .test-info {\n background-color: #B1D7A1;\n}\n\n.status-display .status-failure,\n.status-failure .test-info {\n background-color: #FF8286;\n}\n\n.status-display .status-error,\n.status-error .test-info {\n background-color: black;\n color: white;\n}\n\n.test-actions .status-success .test-title {\n color: #30B30A;\n}\n\n.test-actions .status-failure .test-title {\n color: #DF0000;\n}\n\n.test-actions .status-error .test-title {\n color: black;\n}\n\n.test-actions .timer-result {\n color: #888;\n}\n</style>');
26310
+ angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";\n/* CSS Document */\n\n/** Structure */\nbody {\n font-family: Arial, sans-serif;\n margin: 0;\n font-size: 14px;\n}\n\n#system-error {\n font-size: 1.5em;\n text-align: center;\n}\n\n#json, #xml {\n display: none;\n}\n\n#header {\n position: fixed;\n width: 100%;\n}\n\n#specs {\n padding-top: 50px;\n}\n\n#header .angular {\n font-family: Courier New, monospace;\n font-weight: bold;\n}\n\n#header h1 {\n font-weight: normal;\n float: left;\n font-size: 30px;\n line-height: 30px;\n margin: 0;\n padding: 10px 10px;\n height: 30px;\n}\n\n#application h2,\n#specs h2 {\n margin: 0;\n padding: 0.5em;\n font-size: 1.1em;\n}\n\n#status-legend {\n margin-top: 10px;\n margin-right: 10px;\n}\n\n#header,\n#application,\n.test-info,\n.test-actions li {\n overflow: hidden;\n}\n\n#application {\n margin: 10px;\n}\n\n#application iframe {\n width: 100%;\n height: 758px;\n}\n\n#application .popout {\n float: right;\n}\n\n#application iframe {\n border: none;\n}\n\n.tests li,\n.test-actions li,\n.test-it li,\n.test-it ol,\n.status-display {\n list-style-type: none;\n}\n\n.tests,\n.test-it ol,\n.status-display {\n margin: 0;\n padding: 0;\n}\n\n.test-info {\n margin-left: 1em;\n margin-top: 0.5em;\n border-radius: 8px 0 0 8px;\n -webkit-border-radius: 8px 0 0 8px;\n -moz-border-radius: 8px 0 0 8px;\n cursor: pointer;\n}\n\n.test-info:hover .test-name {\n text-decoration: underline;\n}\n\n.test-info .closed:before {\n content: \'\\25b8\\00A0\';\n}\n\n.test-info .open:before {\n content: \'\\25be\\00A0\';\n font-weight: bold;\n}\n\n.test-it ol {\n margin-left: 2.5em;\n}\n\n.status-display,\n.status-display li {\n float: right;\n}\n\n.status-display li {\n padding: 5px 10px;\n}\n\n.timer-result,\n.test-title {\n display: inline-block;\n margin: 0;\n padding: 4px;\n}\n\n.test-actions .test-title,\n.test-actions .test-result {\n display: table-cell;\n padding-left: 0.5em;\n padding-right: 0.5em;\n}\n\n.test-actions {\n display: table;\n}\n\n.test-actions li {\n display: table-row;\n}\n\n.timer-result {\n width: 4em;\n padding: 0 10px;\n text-align: right;\n font-family: monospace;\n}\n\n.test-it pre,\n.test-actions pre {\n clear: left;\n color: black;\n margin-left: 6em;\n}\n\n.test-describe {\n padding-bottom: 0.5em;\n}\n\n.test-describe .test-describe {\n margin: 5px 5px 10px 2em;\n}\n\n.test-actions .status-pending .test-title:before {\n content: \'\\00bb\\00A0\';\n}\n\n.scrollpane {\n max-height: 20em;\n overflow: auto;\n}\n\n/** Colors */\n\n#header {\n background-color: #F2C200;\n}\n\n#specs h2 {\n border-top: 2px solid #BABAD1;\n}\n\n#specs h2,\n#application h2 {\n background-color: #efefef;\n}\n\n#application {\n border: 1px solid #BABAD1;\n}\n\n.test-describe .test-describe {\n border-left: 1px solid #BABAD1;\n border-right: 1px solid #BABAD1;\n border-bottom: 1px solid #BABAD1;\n}\n\n.status-display {\n border: 1px solid #777;\n}\n\n.status-display .status-pending,\n.status-pending .test-info {\n background-color: #F9EEBC;\n}\n\n.status-display .status-success,\n.status-success .test-info {\n background-color: #B1D7A1;\n}\n\n.status-display .status-failure,\n.status-failure .test-info {\n background-color: #FF8286;\n}\n\n.status-display .status-error,\n.status-error .test-info {\n background-color: black;\n color: white;\n}\n\n.test-actions .status-success .test-title {\n color: #30B30A;\n}\n\n.test-actions .status-failure .test-title {\n color: #DF0000;\n}\n\n.test-actions .status-error .test-title {\n color: black;\n}\n\n.test-actions .timer-result {\n color: #888;\n}\n</style>');