angularjs-rails 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.0.4
2
+ * @license AngularJS v1.0.5
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -55,8 +55,7 @@ if ('i' !== 'I'.toLowerCase()) {
55
55
  function fromCharCode(code) {return String.fromCharCode(code);}
56
56
 
57
57
 
58
- var Error = window.Error,
59
- /** holds major version number for IE or NaN for real browsers */
58
+ var /** holds major version number for IE or NaN for real browsers */
60
59
  msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
61
60
  jqLite, // delay binding since jQuery could be loaded after us.
62
61
  jQuery, // delay binding
@@ -97,6 +96,30 @@ var Error = window.Error,
97
96
  * @param {Object=} context Object to become context (`this`) for the iterator function.
98
97
  * @returns {Object|Array} Reference to `obj`.
99
98
  */
99
+
100
+
101
+ /**
102
+ * @private
103
+ * @param {*} obj
104
+ * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
105
+ */
106
+ function isArrayLike(obj) {
107
+ if (!obj || (typeof obj.length !== 'number')) return false;
108
+
109
+ // We have on object which has length property. Should we treat it as array?
110
+ if (typeof obj.hasOwnProperty != 'function' &&
111
+ typeof obj.constructor != 'function') {
112
+ // This is here for IE8: it is a bogus object treat it as array;
113
+ return true;
114
+ } else {
115
+ return obj instanceof JQLite || // JQLite
116
+ (jQuery && obj instanceof jQuery) || // jQuery
117
+ toString.call(obj) !== '[object Object]' || // some browser native object
118
+ typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
119
+ }
120
+ }
121
+
122
+
100
123
  function forEach(obj, iterator, context) {
101
124
  var key;
102
125
  if (obj) {
@@ -108,7 +131,7 @@ function forEach(obj, iterator, context) {
108
131
  }
109
132
  } else if (obj.forEach && obj.forEach !== forEach) {
110
133
  obj.forEach(iterator, context);
111
- } else if (isObject(obj) && isNumber(obj.length)) {
134
+ } else if (isArrayLike(obj)) {
112
135
  for (key = 0; key < obj.length; key++)
113
136
  iterator.call(context, obj[key], key);
114
137
  } else {
@@ -153,7 +176,7 @@ function reverseParams(iteratorFn) {
153
176
  /**
154
177
  * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
155
178
  * characters such as '012ABC'. The reason why we are not using simply a number counter is that
156
- * the number string gets longer over time, and it can also overflow, where as the the nextId
179
+ * the number string gets longer over time, and it can also overflow, where as the nextId
157
180
  * will grow much slower, it is a string, and it will never overflow.
158
181
  *
159
182
  * @returns an unique alpha-numeric string
@@ -549,9 +572,7 @@ function copy(source, destination){
549
572
  } else {
550
573
  if (source === destination) throw Error("Can't copy equivalent objects or arrays");
551
574
  if (isArray(source)) {
552
- while(destination.length) {
553
- destination.pop();
554
- }
575
+ destination.length = 0;
555
576
  for ( var i = 0; i < source.length; i++) {
556
577
  destination.push(copy(source[i]));
557
578
  }
@@ -762,9 +783,18 @@ function startingTag(element) {
762
783
  // are not allowed to have children. So we just ignore it.
763
784
  element.html('');
764
785
  } catch(e) {}
765
- return jqLite('<div>').append(element).html().
766
- match(/^(<[^>]+>)/)[1].
767
- replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
786
+ // As Per DOM Standards
787
+ var TEXT_NODE = 3;
788
+ var elemHtml = jqLite('<div>').append(element).html();
789
+ try {
790
+ return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
791
+ elemHtml.
792
+ match(/^(<[^>]+>)/)[1].
793
+ replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
794
+ } catch(e) {
795
+ return lowercase(elemHtml);
796
+ }
797
+
768
798
  }
769
799
 
770
800
 
@@ -848,7 +878,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
848
878
  * Use this directive to auto-bootstrap on application. Only
849
879
  * one directive can be used per HTML document. The directive
850
880
  * designates the root of the application and is typically placed
851
- * ot the root of the page.
881
+ * at the root of the page.
852
882
  *
853
883
  * In the example below if the `ngApp` directive would not be placed
854
884
  * on the `html` element then the document would not be compiled
@@ -1249,11 +1279,11 @@ function setupModuleLoader(window) {
1249
1279
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
1250
1280
  */
1251
1281
  var version = {
1252
- full: '1.0.4', // all of these placeholder strings will be replaced by rake's
1282
+ full: '1.0.5', // all of these placeholder strings will be replaced by rake's
1253
1283
  major: 1, // compile task
1254
1284
  minor: 0,
1255
- dot: 4,
1256
- codeName: 'bewildering-hair'
1285
+ dot: 5,
1286
+ codeName: 'flatulent-propulsion'
1257
1287
  };
1258
1288
 
1259
1289
 
@@ -2430,7 +2460,7 @@ function annotate(fn) {
2430
2460
  * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
2431
2461
  * are supported.
2432
2462
  *
2433
- * # The `$injector` property
2463
+ * # The `$inject` property
2434
2464
  *
2435
2465
  * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
2436
2466
  * services to be injected into the function.
@@ -3144,7 +3174,7 @@ function Browser(window, document, $log, $sniffer) {
3144
3174
  */
3145
3175
  self.baseHref = function() {
3146
3176
  var href = baseElement.attr('href');
3147
- return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
3177
+ return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
3148
3178
  };
3149
3179
 
3150
3180
  //////////////////////////////////////////////////////////////
@@ -3594,7 +3624,8 @@ function $CompileProvider($provide) {
3594
3624
  Suffix = 'Directive',
3595
3625
  COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
3596
3626
  CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
3597
- MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
3627
+ MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
3628
+ urlSanitizationWhitelist = /^\s*(https?|ftp|mailto):/;
3598
3629
 
3599
3630
 
3600
3631
  /**
@@ -3648,11 +3679,41 @@ function $CompileProvider($provide) {
3648
3679
  };
3649
3680
 
3650
3681
 
3682
+ /**
3683
+ * @ngdoc function
3684
+ * @name ng.$compileProvider#urlSanitizationWhitelist
3685
+ * @methodOf ng.$compileProvider
3686
+ * @function
3687
+ *
3688
+ * @description
3689
+ * Retrieves or overrides the default regular expression that is used for whitelisting of safe
3690
+ * urls during a[href] sanitization.
3691
+ *
3692
+ * The sanitization is a security measure aimed at prevent XSS attacks via html links.
3693
+ *
3694
+ * Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
3695
+ * absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
3696
+ * expression. If a match is found the original url is written into the dom. Otherwise the
3697
+ * absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
3698
+ *
3699
+ * @param {RegExp=} regexp New regexp to whitelist urls with.
3700
+ * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
3701
+ * chaining otherwise.
3702
+ */
3703
+ this.urlSanitizationWhitelist = function(regexp) {
3704
+ if (isDefined(regexp)) {
3705
+ urlSanitizationWhitelist = regexp;
3706
+ return this;
3707
+ }
3708
+ return urlSanitizationWhitelist;
3709
+ };
3710
+
3711
+
3651
3712
  this.$get = [
3652
3713
  '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
3653
- '$controller', '$rootScope',
3714
+ '$controller', '$rootScope', '$document',
3654
3715
  function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
3655
- $controller, $rootScope) {
3716
+ $controller, $rootScope, $document) {
3656
3717
 
3657
3718
  var Attributes = function(element, attr) {
3658
3719
  this.$$element = element;
@@ -3674,7 +3735,8 @@ function $CompileProvider($provide) {
3674
3735
  */
3675
3736
  $set: function(key, value, writeAttr, attrName) {
3676
3737
  var booleanKey = getBooleanAttrName(this.$$element[0], key),
3677
- $$observers = this.$$observers;
3738
+ $$observers = this.$$observers,
3739
+ normalizedVal;
3678
3740
 
3679
3741
  if (booleanKey) {
3680
3742
  this.$$element.prop(key, value);
@@ -3693,6 +3755,19 @@ function $CompileProvider($provide) {
3693
3755
  }
3694
3756
  }
3695
3757
 
3758
+
3759
+ // sanitize a[href] values
3760
+ if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
3761
+ urlSanitizationNode.setAttribute('href', value);
3762
+
3763
+ // href property always returns normalized absolute url, so we can match against that
3764
+ normalizedVal = urlSanitizationNode.href;
3765
+ if (!normalizedVal.match(urlSanitizationWhitelist)) {
3766
+ this[key] = value = 'unsafe:' + normalizedVal;
3767
+ }
3768
+ }
3769
+
3770
+
3696
3771
  if (writeAttr !== false) {
3697
3772
  if (value === null || value === undefined) {
3698
3773
  this.$$element.removeAttr(attrName);
@@ -3736,7 +3811,8 @@ function $CompileProvider($provide) {
3736
3811
  }
3737
3812
  };
3738
3813
 
3739
- var startSymbol = $interpolate.startSymbol(),
3814
+ var urlSanitizationNode = $document[0].createElement('a'),
3815
+ startSymbol = $interpolate.startSymbol(),
3740
3816
  endSymbol = $interpolate.endSymbol(),
3741
3817
  denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
3742
3818
  ? identity
@@ -3769,7 +3845,14 @@ function $CompileProvider($provide) {
3769
3845
  var $linkNode = cloneConnectFn
3770
3846
  ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
3771
3847
  : $compileNodes;
3772
- $linkNode.data('$scope', scope);
3848
+
3849
+ // Attach scope only to non-text nodes.
3850
+ for(var i = 0, ii = $linkNode.length; i<ii; i++) {
3851
+ var node = $linkNode[i];
3852
+ if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
3853
+ $linkNode.eq(i).data('$scope', scope);
3854
+ }
3855
+ }
3773
3856
  safeAddClass($linkNode, 'ng-scope');
3774
3857
  if (cloneConnectFn) cloneConnectFn($linkNode, scope);
3775
3858
  if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
@@ -3859,6 +3942,7 @@ function $CompileProvider($provide) {
3859
3942
  (function(transcludeFn) {
3860
3943
  return function(cloneFn) {
3861
3944
  var transcludeScope = scope.$new();
3945
+ transcludeScope.$$transcluded = true;
3862
3946
 
3863
3947
  return transcludeFn(transcludeScope, cloneFn).
3864
3948
  bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
@@ -4164,6 +4248,8 @@ function $CompileProvider($provide) {
4164
4248
  lastValue,
4165
4249
  parentGet, parentSet;
4166
4250
 
4251
+ scope.$$isolateBindings[scopeName] = mode + attrName;
4252
+
4167
4253
  switch (mode) {
4168
4254
 
4169
4255
  case '@': {
@@ -4374,7 +4460,7 @@ function $CompileProvider($provide) {
4374
4460
  }
4375
4461
 
4376
4462
  directives.unshift(derivedSyncDirective);
4377
- afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
4463
+ afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
4378
4464
  afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
4379
4465
 
4380
4466
 
@@ -4454,10 +4540,10 @@ function $CompileProvider($provide) {
4454
4540
  function addAttrInterpolateDirective(node, directives, value, name) {
4455
4541
  var interpolateFn = $interpolate(value, true);
4456
4542
 
4457
-
4458
4543
  // no interpolation found -> ignore
4459
4544
  if (!interpolateFn) return;
4460
4545
 
4546
+
4461
4547
  directives.push({
4462
4548
  priority: 100,
4463
4549
  compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
@@ -6451,9 +6537,10 @@ function getterFn(path, csp) {
6451
6537
  * @param {string} expression String expression to compile.
6452
6538
  * @returns {function(context, locals)} a function which represents the compiled expression:
6453
6539
  *
6454
- * * `context`: an object against which any expressions embedded in the strings are evaluated
6455
- * against (Topically a scope object).
6456
- * * `locals`: local variables context object, useful for overriding values in `context`.
6540
+ * * `context` – `{object}` – an object against which any expressions embedded in the strings
6541
+ * are evaluated against (tipically a scope object).
6542
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values in
6543
+ * `context`.
6457
6544
  *
6458
6545
  * The return function also has an `assign` property, if the expression is assignable, which
6459
6546
  * allows one to set values to expressions.
@@ -6489,7 +6576,7 @@ function $ParseProvider() {
6489
6576
  * interface for interacting with an object that represents the result of an action that is
6490
6577
  * performed asynchronously, and may or may not be finished at any given point in time.
6491
6578
  *
6492
- * From the perspective of dealing with error handling, deferred and promise apis are to
6579
+ * From the perspective of dealing with error handling, deferred and promise APIs are to
6493
6580
  * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
6494
6581
  *
6495
6582
  * <pre>
@@ -6524,7 +6611,7 @@ function $ParseProvider() {
6524
6611
  *
6525
6612
  * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
6526
6613
  * comes in the way of
6527
- * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
6614
+ * [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
6528
6615
  *
6529
6616
  * Additionally the promise api allows for composition that is very hard to do with the
6530
6617
  * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
@@ -6536,7 +6623,7 @@ function $ParseProvider() {
6536
6623
  *
6537
6624
  * A new instance of deferred is constructed by calling `$q.defer()`.
6538
6625
  *
6539
- * The purpose of the deferred object is to expose the associated Promise instance as well as apis
6626
+ * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
6540
6627
  * that can be used for signaling the successful or unsuccessful completion of the task.
6541
6628
  *
6542
6629
  * **Methods**
@@ -6579,7 +6666,7 @@ function $ParseProvider() {
6579
6666
  * return result + 1;
6580
6667
  * });
6581
6668
  *
6582
- * // promiseB will be resolved immediately after promiseA is resolved and it's value will be
6669
+ * // promiseB will be resolved immediately after promiseA is resolved and its value will be
6583
6670
  * // the result of promiseA incremented by 1
6584
6671
  * </pre>
6585
6672
  *
@@ -6604,7 +6691,7 @@ function $ParseProvider() {
6604
6691
  * # Testing
6605
6692
  *
6606
6693
  * <pre>
6607
- * it('should simulate promise', inject(function($q, $rootSCope) {
6694
+ * it('should simulate promise', inject(function($q, $rootScope) {
6608
6695
  * var deferred = $q.defer();
6609
6696
  * var promise = deferred.promise;
6610
6697
  * var resolvedValue;
@@ -6613,7 +6700,7 @@ function $ParseProvider() {
6613
6700
  * expect(resolvedValue).toBeUndefined();
6614
6701
  *
6615
6702
  * // Simulate resolving of promise
6616
- * defered.resolve(123);
6703
+ * deferred.resolve(123);
6617
6704
  * // Note that the 'then' function does not get called synchronously.
6618
6705
  * // This is because we want the promise API to always be async, whether or not
6619
6706
  * // it got called synchronously or asynchronously.
@@ -6789,12 +6876,12 @@ function qFactory(nextTick, exceptionHandler) {
6789
6876
  * @methodOf ng.$q
6790
6877
  * @description
6791
6878
  * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
6792
- * This is useful when you are dealing with on object that might or might not be a promise, or if
6879
+ * This is useful when you are dealing with an object that might or might not be a promise, or if
6793
6880
  * the promise comes from a source that can't be trusted.
6794
6881
  *
6795
6882
  * @param {*} value Value or a promise
6796
6883
  * @returns {Promise} Returns a single promise that will be resolved with an array of values,
6797
- * each value coresponding to the promise at the same index in the `promises` array. If any of
6884
+ * each value corresponding to the promise at the same index in the `promises` array. If any of
6798
6885
  * the promises is resolved with a rejection, this resulting promise will be resolved with the
6799
6886
  * same rejection.
6800
6887
  */
@@ -6856,7 +6943,7 @@ function qFactory(nextTick, exceptionHandler) {
6856
6943
  *
6857
6944
  * @param {Array.<Promise>} promises An array of promises.
6858
6945
  * @returns {Promise} Returns a single promise that will be resolved with an array of values,
6859
- * each value coresponding to the promise at the same index in the `promises` array. If any of
6946
+ * each value corresponding to the promise at the same index in the `promises` array. If any of
6860
6947
  * the promises is resolved with a rejection, this resulting promise will be resolved with the
6861
6948
  * same rejection.
6862
6949
  */
@@ -7525,6 +7612,7 @@ function $RootScopeProvider(){
7525
7612
  this.$$destroyed = false;
7526
7613
  this.$$asyncQueue = [];
7527
7614
  this.$$listeners = {};
7615
+ this.$$isolateBindings = {};
7528
7616
  }
7529
7617
 
7530
7618
  /**
@@ -8006,10 +8094,6 @@ function $RootScopeProvider(){
8006
8094
  * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
8007
8095
  * event life cycle.
8008
8096
  *
8009
- * @param {string} name Event name to listen on.
8010
- * @param {function(event)} listener Function to call when the event is emitted.
8011
- * @returns {function()} Returns a deregistration function for this listener.
8012
- *
8013
8097
  * The event listener function format is: `function(event, args...)`. The `event` object
8014
8098
  * passed into the listener has the following attributes:
8015
8099
  *
@@ -8020,6 +8104,10 @@ function $RootScopeProvider(){
8020
8104
  * propagation (available only for events that were `$emit`-ed).
8021
8105
  * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
8022
8106
  * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
8107
+ *
8108
+ * @param {string} name Event name to listen on.
8109
+ * @param {function(event, args...)} listener Function to call when the event is emitted.
8110
+ * @returns {function()} Returns a deregistration function for this listener.
8023
8111
  */
8024
8112
  $on: function(name, listener) {
8025
8113
  var namedListeners = this.$$listeners[name];
@@ -8197,7 +8285,7 @@ function $RootScopeProvider(){
8197
8285
 
8198
8286
  /**
8199
8287
  * function used as an initial value for watchers.
8200
- * because it's uniqueue we can easily tell it apart from other values
8288
+ * because it's unique we can easily tell it apart from other values
8201
8289
  */
8202
8290
  function initWatchVal() {}
8203
8291
  }];
@@ -9108,8 +9196,30 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
9108
9196
  // always async
9109
9197
  xhr.onreadystatechange = function() {
9110
9198
  if (xhr.readyState == 4) {
9111
- completeRequest(
9112
- callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
9199
+ var responseHeaders = xhr.getAllResponseHeaders();
9200
+
9201
+ // TODO(vojta): remove once Firefox 21 gets released.
9202
+ // begin: workaround to overcome Firefox CORS http response headers bug
9203
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=608735
9204
+ // Firefox already patched in nightly. Should land in Firefox 21.
9205
+
9206
+ // CORS "simple response headers" http://www.w3.org/TR/cors/
9207
+ var value,
9208
+ simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
9209
+ "Expires", "Last-Modified", "Pragma"];
9210
+ if (!responseHeaders) {
9211
+ responseHeaders = "";
9212
+ forEach(simpleHeaders, function (header) {
9213
+ var value = xhr.getResponseHeader(header);
9214
+ if (value) {
9215
+ responseHeaders += header + ": " + value + "\n";
9216
+ }
9217
+ });
9218
+ }
9219
+ // end of the workaround.
9220
+
9221
+ completeRequest(callback, status || xhr.status, xhr.responseText,
9222
+ responseHeaders);
9113
9223
  }
9114
9224
  };
9115
9225
 
@@ -9508,7 +9618,7 @@ function $FilterProvider($provide) {
9508
9618
  */
9509
9619
  function filterFilter() {
9510
9620
  return function(array, expression) {
9511
- if (!(array instanceof Array)) return array;
9621
+ if (!isArray(array)) return array;
9512
9622
  var predicates = [];
9513
9623
  predicates.check = function(value) {
9514
9624
  for (var j = 0; j < predicates.length; j++) {
@@ -9757,7 +9867,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
9757
9867
  fraction += '0';
9758
9868
  }
9759
9869
 
9760
- if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize);
9870
+ if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
9761
9871
  }
9762
9872
 
9763
9873
  parts.push(isNegative ? pattern.negPre : pattern.posPre);
@@ -9800,8 +9910,12 @@ function dateStrGetter(name, shortForm) {
9800
9910
  }
9801
9911
 
9802
9912
  function timeZoneGetter(date) {
9803
- var offset = date.getTimezoneOffset();
9804
- return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
9913
+ var zone = -1 * date.getTimezoneOffset();
9914
+ var paddedZone = (zone >= 0) ? "+" : "";
9915
+
9916
+ paddedZone += padNumber(zone / 60, 2) + padNumber(Math.abs(zone % 60), 2);
9917
+
9918
+ return paddedZone;
9805
9919
  }
9806
9920
 
9807
9921
  function ampmGetter(date, formats) {
@@ -9887,7 +10001,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9887
10001
  *
9888
10002
  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
9889
10003
  * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
9890
- * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
10004
+ * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
10005
+ * specified in the string input, the time is considered to be in the local timezone.
9891
10006
  * @param {string=} format Formatting rules (see Description). If not specified,
9892
10007
  * `mediumDate` is used.
9893
10008
  * @returns {string} Formatted string or the input if input is not recognized as date/millis.
@@ -9907,7 +10022,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
9907
10022
  expect(binding("1288323623006 | date:'medium'")).
9908
10023
  toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
9909
10024
  expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
9910
- toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
10025
+ toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
9911
10026
  expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
9912
10027
  toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
9913
10028
  });
@@ -10213,7 +10328,7 @@ function limitToFilter(){
10213
10328
  orderByFilter.$inject = ['$parse'];
10214
10329
  function orderByFilter($parse){
10215
10330
  return function(array, sortPredicate, reverseOrder) {
10216
- if (!(array instanceof Array)) return array;
10331
+ if (!isArray(array)) return array;
10217
10332
  if (!sortPredicate) return array;
10218
10333
  sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
10219
10334
  sortPredicate = map(sortPredicate, function(predicate){
@@ -10281,15 +10396,25 @@ function ngDirective(directive) {
10281
10396
  *
10282
10397
  * The reasoning for this change is to allow easy creation of action links with `ngClick` directive
10283
10398
  * without changing the location or causing page reloads, e.g.:
10284
- * <a href="" ng-click="model.$save()">Save</a>
10399
+ * `<a href="" ng-click="model.$save()">Save</a>`
10285
10400
  */
10286
10401
  var htmlAnchorDirective = valueFn({
10287
10402
  restrict: 'E',
10288
10403
  compile: function(element, attr) {
10289
- // turn <a href ng-click="..">link</a> into a link in IE
10290
- // but only if it doesn't have name attribute, in which case it's an anchor
10291
- if (!attr.href) {
10292
- attr.$set('href', '');
10404
+
10405
+ if (msie <= 8) {
10406
+
10407
+ // turn <a href ng-click="..">link</a> into a stylable link in IE
10408
+ // but only if it doesn't have name attribute, in which case it's an anchor
10409
+ if (!attr.href && !attr.name) {
10410
+ attr.$set('href', '');
10411
+ }
10412
+
10413
+ // add a comment node to anchors to workaround IE bug that causes element content to be reset
10414
+ // to new attribute content if attribute is updated with value containing @ and element also
10415
+ // contains value with @
10416
+ // see issue #1949
10417
+ element.append(document.createComment('IE fix'));
10293
10418
  }
10294
10419
 
10295
10420
  return function(scope, element) {
@@ -10369,7 +10494,7 @@ var htmlAnchorDirective = valueFn({
10369
10494
  it('should execute ng-click but not reload when no href but name specified', function() {
10370
10495
  element('#link-5').click();
10371
10496
  expect(input('value').val()).toEqual('5');
10372
- expect(element('#link-5').attr('href')).toBe('');
10497
+ expect(element('#link-5').attr('href')).toBe(undefined);
10373
10498
  });
10374
10499
 
10375
10500
  it('should only change url when only ng-href', function() {
@@ -10612,8 +10737,9 @@ forEach(['src', 'href'], function(attrName) {
10612
10737
 
10613
10738
  // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
10614
10739
  // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
10615
- // to set the property as well to achieve the desired effect
10616
- if (msie) element.prop(attrName, value);
10740
+ // to set the property as well to achieve the desired effect.
10741
+ // we use attr[attrName] value since $set can sanitize the url.
10742
+ if (msie) element.prop(attrName, attr[attrName]);
10617
10743
  });
10618
10744
  }
10619
10745
  };
@@ -12318,6 +12444,7 @@ var ngBindHtmlUnsafeDirective = [function() {
12318
12444
  function classDirective(name, selector) {
12319
12445
  name = 'ngClass' + name;
12320
12446
  return ngDirective(function(scope, element, attr) {
12447
+ var oldVal = undefined;
12321
12448
 
12322
12449
  scope.$watch(attr[name], ngClassWatchAction, true);
12323
12450
 
@@ -12341,13 +12468,14 @@ function classDirective(name, selector) {
12341
12468
  }
12342
12469
 
12343
12470
 
12344
- function ngClassWatchAction(newVal, oldVal) {
12471
+ function ngClassWatchAction(newVal) {
12345
12472
  if (selector === true || scope.$index % 2 === selector) {
12346
12473
  if (oldVal && (newVal !== oldVal)) {
12347
12474
  removeClass(oldVal);
12348
12475
  }
12349
12476
  addClass(newVal);
12350
12477
  }
12478
+ oldVal = newVal;
12351
12479
  }
12352
12480
 
12353
12481
 
@@ -12380,7 +12508,7 @@ function classDirective(name, selector) {
12380
12508
  *
12381
12509
  * The directive won't add duplicate classes if a particular class was already set.
12382
12510
  *
12383
- * When the expression changes, the previously added classes are removed and only then the classes
12511
+ * When the expression changes, the previously added classes are removed and only then the
12384
12512
  * new classes are added.
12385
12513
  *
12386
12514
  * @element ANY
@@ -13704,9 +13832,10 @@ var NG_SWITCH = 'ng-switch';
13704
13832
  var ngSwitchDirective = valueFn({
13705
13833
  restrict: 'EA',
13706
13834
  require: 'ngSwitch',
13707
- controller: function ngSwitchController() {
13835
+ // asks for $scope to fool the BC controller module
13836
+ controller: ['$scope', function ngSwitchController() {
13708
13837
  this.cases = {};
13709
- },
13838
+ }],
13710
13839
  link: function(scope, element, attr, ctrl) {
13711
13840
  var watchExpr = attr.ngSwitch || attr.on,
13712
13841
  selectedTransclude,
@@ -13957,7 +14086,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
13957
14086
  if (current.controller) {
13958
14087
  locals.$scope = lastScope;
13959
14088
  controller = $controller(current.controller, locals);
13960
- element.contents().data('$ngControllerController', controller);
14089
+ element.children().data('$ngControllerController', controller);
13961
14090
  }
13962
14091
 
13963
14092
  link(lastScope);