angularjs-rails 1.0.4 → 1.0.5

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