angularjs-rails 1.5.6 → 1.5.8

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.5.6
2
+ * @license AngularJS v1.5.8
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -113,19 +113,29 @@ angular.mock.$Browser = function() {
113
113
  * @param {number=} number of milliseconds to flush. See {@link #defer.now}
114
114
  */
115
115
  self.defer.flush = function(delay) {
116
+ var nextTime;
117
+
116
118
  if (angular.isDefined(delay)) {
117
- self.defer.now += delay;
119
+ // A delay was passed so compute the next time
120
+ nextTime = self.defer.now + delay;
118
121
  } else {
119
122
  if (self.deferredFns.length) {
120
- self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
123
+ // No delay was passed so set the next time so that it clears the deferred queue
124
+ nextTime = self.deferredFns[self.deferredFns.length - 1].time;
121
125
  } else {
126
+ // No delay passed, but there are no deferred tasks so flush - indicates an error!
122
127
  throw new Error('No deferred tasks to be flushed');
123
128
  }
124
129
  }
125
130
 
126
- while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
131
+ while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
132
+ // Increment the time and call the next deferred function
133
+ self.defer.now = self.deferredFns[0].time;
127
134
  self.deferredFns.shift().fn();
128
135
  }
136
+
137
+ // Ensure that the current time is correct
138
+ self.defer.now = nextTime;
129
139
  };
130
140
 
131
141
  self.$$baseHref = '/';
@@ -934,13 +944,10 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
934
944
  * @name angular.mock.dump
935
945
  * @description
936
946
  *
937
- * *NOTE*: this is not an injectable instance, just a globally available function.
938
- *
939
- * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
940
- * debugging.
947
+ * *NOTE*: This is not an injectable instance, just a globally available function.
941
948
  *
942
- * This method is also available on window, where it can be used to display objects on debug
943
- * console.
949
+ * Method for serializing common angular objects (scope, elements, etc..) into strings.
950
+ * It is useful for logging objects to the console when debugging.
944
951
  *
945
952
  * @param {*} object - any object to turn into string.
946
953
  * @return {string} a serialized string of the argument
@@ -2201,9 +2208,15 @@ angular.mock.$RootElementProvider = function() {
2201
2208
  angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
2202
2209
  return function(expression, locals, later, ident) {
2203
2210
  if (later && typeof later === 'object') {
2204
- var create = $delegate(expression, locals, true, ident);
2205
- angular.extend(create.instance, later);
2206
- return create();
2211
+ var instantiate = $delegate(expression, locals, true, ident);
2212
+ angular.extend(instantiate.instance, later);
2213
+
2214
+ var instance = instantiate();
2215
+ if (instance !== instantiate.instance) {
2216
+ angular.extend(instance, later);
2217
+ }
2218
+
2219
+ return instance;
2207
2220
  }
2208
2221
  return $delegate(expression, locals, later, ident);
2209
2222
  };
@@ -2275,7 +2288,7 @@ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compi
2275
2288
  * * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
2276
2289
  * `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
2277
2290
  * * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
2278
- * * [Bower](http://bower.io) e.g. `bower install angular-mocks@X.Y.Z`
2291
+ * * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
2279
2292
  * * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
2280
2293
  * `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
2281
2294
  *
@@ -2325,6 +2338,7 @@ angular.module('ngMock', ['ng']).provider({
2325
2338
  * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
2326
2339
  */
2327
2340
  angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2341
+ $provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
2328
2342
  $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
2329
2343
  }]);
2330
2344
 
@@ -2923,7 +2937,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
2923
2937
  angular.forEach(angular.callbacks, function(val, key) {
2924
2938
  delete angular.callbacks[key];
2925
2939
  });
2926
- angular.callbacks.counter = 0;
2940
+ angular.callbacks.$$counter = 0;
2927
2941
  };
2928
2942
 
2929
2943
  (window.beforeEach || window.setup)(module.$$beforeEach);
@@ -3026,7 +3040,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
3026
3040
  this.stack = e.stack + '\n' + errorForStack.stack;
3027
3041
  if (e.stackArray) this.stackArray = e.stackArray;
3028
3042
  };
3029
- ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
3043
+ ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
3030
3044
 
3031
3045
  window.inject = angular.mock.inject = function() {
3032
3046
  var blockFns = Array.prototype.slice.call(arguments, 0);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.6
2
+ * @license AngularJS v1.5.8
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.6
2
+ * @license AngularJS v1.5.8
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -61,7 +61,21 @@ function shallowClearAndCopy(src, dst) {
61
61
  *
62
62
  * <div doc-module-components="ngResource"></div>
63
63
  *
64
- * See {@link ngResource.$resource `$resource`} for usage.
64
+ * See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
65
+ */
66
+
67
+ /**
68
+ * @ngdoc provider
69
+ * @name $resourceProvider
70
+ *
71
+ * @description
72
+ *
73
+ * Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
74
+ * service.
75
+ *
76
+ * ## Dependencies
77
+ * Requires the {@link ngResource } module to be installed.
78
+ *
65
79
  */
66
80
 
67
81
  /**
@@ -105,8 +119,9 @@ function shallowClearAndCopy(src, dst) {
105
119
  * can escape it with `/\.`.
106
120
  *
107
121
  * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
108
- * `actions` methods. If a parameter value is a function, it will be executed every time
109
- * when a param value needs to be obtained for a request (unless the param was overridden).
122
+ * `actions` methods. If a parameter value is a function, it will be called every time
123
+ * a param value needs to be obtained for a request (unless the param was overridden). The function
124
+ * will be passed the current data value as an argument.
110
125
  *
111
126
  * Each key value in the parameter object is first bound to url template if present and then any
112
127
  * excess keys are appended to the url search query after the `?`.
@@ -114,10 +129,13 @@ function shallowClearAndCopy(src, dst) {
114
129
  * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
115
130
  * URL `/path/greet?salutation=Hello`.
116
131
  *
117
- * If the parameter value is prefixed with `@` then the value for that parameter will be extracted
118
- * from the corresponding property on the `data` object (provided when calling an action method).
132
+ * If the parameter value is prefixed with `@`, then the value for that parameter will be
133
+ * extracted from the corresponding property on the `data` object (provided when calling a
134
+ * "non-GET" action method).
119
135
  * For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
120
136
  * `someParam` will be `data.someProp`.
137
+ * Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
138
+ * method that does not accept a request body)
121
139
  *
122
140
  * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
123
141
  * the default set of resource actions. The declaration should be created in the format of {@link
@@ -134,8 +152,9 @@ function shallowClearAndCopy(src, dst) {
134
152
  * - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
135
153
  * `DELETE`, `JSONP`, etc).
136
154
  * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
137
- * the parameter value is a function, it will be executed every time when a param value needs to
138
- * be obtained for a request (unless the param was overridden).
155
+ * the parameter value is a function, it will be called every time when a param value needs to
156
+ * be obtained for a request (unless the param was overridden). The function will be passed the
157
+ * current data value as an argument.
139
158
  * - **`url`** – {string} – action specific `url` override. The url templating is supported just
140
159
  * like for the resource-level urls.
141
160
  * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
@@ -256,6 +275,14 @@ function shallowClearAndCopy(src, dst) {
256
275
  * - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
257
276
  * collection, calling this method will abort the request.
258
277
  *
278
+ * The Resource instances have these additional methods:
279
+ *
280
+ * - `toJSON`: It returns a simple object without any of the extra properties added as part of
281
+ * the Resource API. This object can be serialized through {@link angular.toJson} safely
282
+ * without attaching Angular-specific fields. Notice that `JSON.stringify` (and
283
+ * `angular.toJson`) automatically use this method when serializing a Resource instance
284
+ * (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
285
+ *
259
286
  * @example
260
287
  *
261
288
  * # Credit card resource
@@ -407,10 +434,78 @@ angular.module('ngResource', ['ng']).
407
434
  var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
408
435
  var provider = this;
409
436
 
437
+ /**
438
+ * @ngdoc property
439
+ * @name $resourceProvider#defaults
440
+ * @description
441
+ * Object containing default options used when creating `$resource` instances.
442
+ *
443
+ * The default values satisfy a wide range of usecases, but you may choose to overwrite any of
444
+ * them to further customize your instances. The available properties are:
445
+ *
446
+ * - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any
447
+ * calculated URL will be stripped.<br />
448
+ * (Defaults to true.)
449
+ * - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be
450
+ * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return
451
+ * value. For more details, see {@link ngResource.$resource}. This can be overwritten per
452
+ * resource class or action.<br />
453
+ * (Defaults to false.)
454
+ * - **actions** - `{Object.<Object>}` - A hash with default actions declarations. Actions are
455
+ * high-level methods corresponding to RESTful actions/methods on resources. An action may
456
+ * specify what HTTP method to use, what URL to hit, if the return value will be a single
457
+ * object or a collection (array) of objects etc. For more details, see
458
+ * {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource
459
+ * class.<br />
460
+ * The default actions are:
461
+ * ```js
462
+ * {
463
+ * get: {method: 'GET'},
464
+ * save: {method: 'POST'},
465
+ * query: {method: 'GET', isArray: true},
466
+ * remove: {method: 'DELETE'},
467
+ * delete: {method: 'DELETE'}
468
+ * }
469
+ * ```
470
+ *
471
+ * #### Example
472
+ *
473
+ * For example, you can specify a new `update` action that uses the `PUT` HTTP verb:
474
+ *
475
+ * ```js
476
+ * angular.
477
+ * module('myApp').
478
+ * config(['resourceProvider', function ($resourceProvider) {
479
+ * $resourceProvider.defaults.actions.update = {
480
+ * method: 'PUT'
481
+ * };
482
+ * });
483
+ * ```
484
+ *
485
+ * Or you can even overwrite the whole `actions` list and specify your own:
486
+ *
487
+ * ```js
488
+ * angular.
489
+ * module('myApp').
490
+ * config(['resourceProvider', function ($resourceProvider) {
491
+ * $resourceProvider.defaults.actions = {
492
+ * create: {method: 'POST'}
493
+ * get: {method: 'GET'},
494
+ * getAll: {method: 'GET', isArray:true},
495
+ * update: {method: 'PUT'},
496
+ * delete: {method: 'DELETE'}
497
+ * };
498
+ * });
499
+ * ```
500
+ *
501
+ */
410
502
  this.defaults = {
411
503
  // Strip slashes by default
412
504
  stripTrailingSlashes: true,
413
505
 
506
+ // Make non-instance requests cancellable (via `$cancelRequest()`)
507
+ cancellable: false,
508
+
414
509
  // Default actions configuration
415
510
  actions: {
416
511
  'get': {method: 'GET'},
@@ -556,7 +651,7 @@ angular.module('ngResource', ['ng']).
556
651
  var ids = {};
557
652
  actionParams = extend({}, paramDefaults, actionParams);
558
653
  forEach(actionParams, function(value, key) {
559
- if (isFunction(value)) { value = value(); }
654
+ if (isFunction(value)) { value = value(data); }
560
655
  ids[key] = value && value.charAt && value.charAt(0) == '@' ?
561
656
  lookupDottedPath(data, value.substr(1)) : value;
562
657
  });
@@ -1,10 +1,44 @@
1
1
  /**
2
- * @license AngularJS v1.5.6
2
+ * @license AngularJS v1.5.8
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular) {'use strict';
7
7
 
8
+ /* global shallowCopy: true */
9
+
10
+ /**
11
+ * Creates a shallow copy of an object, an array or a primitive.
12
+ *
13
+ * Assumes that there are no proto properties for objects.
14
+ */
15
+ function shallowCopy(src, dst) {
16
+ if (isArray(src)) {
17
+ dst = dst || [];
18
+
19
+ for (var i = 0, ii = src.length; i < ii; i++) {
20
+ dst[i] = src[i];
21
+ }
22
+ } else if (isObject(src)) {
23
+ dst = dst || {};
24
+
25
+ for (var key in src) {
26
+ if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
27
+ dst[key] = src[key];
28
+ }
29
+ }
30
+ }
31
+
32
+ return dst || src;
33
+ }
34
+
35
+ /* global shallowCopy: false */
36
+
37
+ // There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
38
+ // They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
39
+ var isArray;
40
+ var isObject;
41
+
8
42
  /**
9
43
  * @ngdoc module
10
44
  * @name ngRoute
@@ -40,6 +74,9 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
40
74
  * Requires the {@link ngRoute `ngRoute`} module to be installed.
41
75
  */
42
76
  function $RouteProvider() {
77
+ isArray = angular.isArray;
78
+ isObject = angular.isObject;
79
+
43
80
  function inherit(parent, extra) {
44
81
  return angular.extend(Object.create(parent), extra);
45
82
  }
@@ -159,7 +196,7 @@ function $RouteProvider() {
159
196
  */
160
197
  this.when = function(path, route) {
161
198
  //copy original route object to preserve params inherited from proto chain
162
- var routeCopy = angular.copy(route);
199
+ var routeCopy = shallowCopy(route);
163
200
  if (angular.isUndefined(routeCopy.reloadOnSearch)) {
164
201
  routeCopy.reloadOnSearch = true;
165
202
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.5.6
2
+ * @license AngularJS v1.5.8
3
3
  * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -17,6 +17,14 @@
17
17
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
18
18
 
19
19
  var $sanitizeMinErr = angular.$$minErr('$sanitize');
20
+ var bind;
21
+ var extend;
22
+ var forEach;
23
+ var isDefined;
24
+ var lowercase;
25
+ var noop;
26
+ var htmlParser;
27
+ var htmlSanitizeWriter;
20
28
 
21
29
  /**
22
30
  * @ngdoc module
@@ -149,7 +157,7 @@ function $SanitizeProvider() {
149
157
 
150
158
  this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
151
159
  if (svgEnabled) {
152
- angular.extend(validElements, svgElements);
160
+ extend(validElements, svgElements);
153
161
  }
154
162
  return function(html) {
155
163
  var buf = [];
@@ -192,334 +200,344 @@ function $SanitizeProvider() {
192
200
  * without an argument or self for chaining otherwise.
193
201
  */
194
202
  this.enableSvg = function(enableSvg) {
195
- if (angular.isDefined(enableSvg)) {
203
+ if (isDefined(enableSvg)) {
196
204
  svgEnabled = enableSvg;
197
205
  return this;
198
206
  } else {
199
207
  return svgEnabled;
200
208
  }
201
209
  };
202
- }
203
-
204
- function sanitizeText(chars) {
205
- var buf = [];
206
- var writer = htmlSanitizeWriter(buf, angular.noop);
207
- writer.chars(chars);
208
- return buf.join('');
209
- }
210
-
211
-
212
- // Regular Expressions for parsing tags and attributes
213
- var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
214
- // Match everything outside of normal chars and " (quote character)
215
- NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
216
-
217
-
218
- // Good source of info about elements and attributes
219
- // http://dev.w3.org/html5/spec/Overview.html#semantics
220
- // http://simon.html5.org/html-elements
221
-
222
- // Safe Void Elements - HTML5
223
- // http://dev.w3.org/html5/spec/Overview.html#void-elements
224
- var voidElements = toMap("area,br,col,hr,img,wbr");
225
-
226
- // Elements that you can, intentionally, leave open (and which close themselves)
227
- // http://dev.w3.org/html5/spec/Overview.html#optional-tags
228
- var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
229
- optionalEndTagInlineElements = toMap("rp,rt"),
230
- optionalEndTagElements = angular.extend({},
231
- optionalEndTagInlineElements,
232
- optionalEndTagBlockElements);
233
-
234
- // Safe Block Elements - HTML5
235
- var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," +
236
- "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
237
- "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
238
-
239
- // Inline Elements - HTML5
240
- var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
241
- "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
242
- "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
243
-
244
- // SVG Elements
245
- // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
246
- // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
247
- // They can potentially allow for arbitrary javascript to be executed. See #11290
248
- var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
249
- "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
250
- "radialGradient,rect,stop,svg,switch,text,title,tspan");
251
-
252
- // Blocked Elements (will be stripped)
253
- var blockedElements = toMap("script,style");
254
-
255
- var validElements = angular.extend({},
256
- voidElements,
257
- blockElements,
258
- inlineElements,
259
- optionalEndTagElements);
260
-
261
- //Attributes that have href and hence need to be sanitized
262
- var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
263
-
264
- var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
265
- 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
266
- 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
267
- 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
268
- 'valign,value,vspace,width');
269
-
270
- // SVG attributes (without "id" and "name" attributes)
271
- // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
272
- var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
273
- 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
274
- 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
275
- 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
276
- 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
277
- 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
278
- 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
279
- 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
280
- 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
281
- 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
282
- 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
283
- 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
284
- 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
285
- 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
286
- 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
287
-
288
- var validAttrs = angular.extend({},
289
- uriAttrs,
290
- svgAttrs,
291
- htmlAttrs);
292
-
293
- function toMap(str, lowercaseKeys) {
294
- var obj = {}, items = str.split(','), i;
295
- for (i = 0; i < items.length; i++) {
296
- obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
297
- }
298
- return obj;
299
- }
300
210
 
301
- var inertBodyElement;
302
- (function(window) {
303
- var doc;
304
- if (window.document && window.document.implementation) {
305
- doc = window.document.implementation.createHTMLDocument("inert");
306
- } else {
307
- throw $sanitizeMinErr('noinert', "Can't create an inert html document");
308
- }
309
- var docElement = doc.documentElement || doc.getDocumentElement();
310
- var bodyElements = docElement.getElementsByTagName('body');
311
-
312
- // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
313
- if (bodyElements.length === 1) {
314
- inertBodyElement = bodyElements[0];
315
- } else {
316
- var html = doc.createElement('html');
317
- inertBodyElement = doc.createElement('body');
318
- html.appendChild(inertBodyElement);
319
- doc.appendChild(html);
211
+ //////////////////////////////////////////////////////////////////////////////////////////////////
212
+ // Private stuff
213
+ //////////////////////////////////////////////////////////////////////////////////////////////////
214
+
215
+ bind = angular.bind;
216
+ extend = angular.extend;
217
+ forEach = angular.forEach;
218
+ isDefined = angular.isDefined;
219
+ lowercase = angular.lowercase;
220
+ noop = angular.noop;
221
+
222
+ htmlParser = htmlParserImpl;
223
+ htmlSanitizeWriter = htmlSanitizeWriterImpl;
224
+
225
+ // Regular Expressions for parsing tags and attributes
226
+ var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
227
+ // Match everything outside of normal chars and " (quote character)
228
+ NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
229
+
230
+
231
+ // Good source of info about elements and attributes
232
+ // http://dev.w3.org/html5/spec/Overview.html#semantics
233
+ // http://simon.html5.org/html-elements
234
+
235
+ // Safe Void Elements - HTML5
236
+ // http://dev.w3.org/html5/spec/Overview.html#void-elements
237
+ var voidElements = toMap("area,br,col,hr,img,wbr");
238
+
239
+ // Elements that you can, intentionally, leave open (and which close themselves)
240
+ // http://dev.w3.org/html5/spec/Overview.html#optional-tags
241
+ var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
242
+ optionalEndTagInlineElements = toMap("rp,rt"),
243
+ optionalEndTagElements = extend({},
244
+ optionalEndTagInlineElements,
245
+ optionalEndTagBlockElements);
246
+
247
+ // Safe Block Elements - HTML5
248
+ var blockElements = extend({}, optionalEndTagBlockElements, toMap("address,article," +
249
+ "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
250
+ "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
251
+
252
+ // Inline Elements - HTML5
253
+ var inlineElements = extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
254
+ "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
255
+ "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
256
+
257
+ // SVG Elements
258
+ // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
259
+ // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
260
+ // They can potentially allow for arbitrary javascript to be executed. See #11290
261
+ var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
262
+ "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
263
+ "radialGradient,rect,stop,svg,switch,text,title,tspan");
264
+
265
+ // Blocked Elements (will be stripped)
266
+ var blockedElements = toMap("script,style");
267
+
268
+ var validElements = extend({},
269
+ voidElements,
270
+ blockElements,
271
+ inlineElements,
272
+ optionalEndTagElements);
273
+
274
+ //Attributes that have href and hence need to be sanitized
275
+ var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
276
+
277
+ var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
278
+ 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
279
+ 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
280
+ 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
281
+ 'valign,value,vspace,width');
282
+
283
+ // SVG attributes (without "id" and "name" attributes)
284
+ // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
285
+ var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
286
+ 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
287
+ 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
288
+ 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
289
+ 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
290
+ 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
291
+ 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
292
+ 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
293
+ 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
294
+ 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
295
+ 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
296
+ 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
297
+ 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
298
+ 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
299
+ 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
300
+
301
+ var validAttrs = extend({},
302
+ uriAttrs,
303
+ svgAttrs,
304
+ htmlAttrs);
305
+
306
+ function toMap(str, lowercaseKeys) {
307
+ var obj = {}, items = str.split(','), i;
308
+ for (i = 0; i < items.length; i++) {
309
+ obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
310
+ }
311
+ return obj;
320
312
  }
321
- })(window);
322
313
 
323
- /**
324
- * @example
325
- * htmlParser(htmlString, {
326
- * start: function(tag, attrs) {},
327
- * end: function(tag) {},
328
- * chars: function(text) {},
329
- * comment: function(text) {}
330
- * });
331
- *
332
- * @param {string} html string
333
- * @param {object} handler
334
- */
335
- function htmlParser(html, handler) {
336
- if (html === null || html === undefined) {
337
- html = '';
338
- } else if (typeof html !== 'string') {
339
- html = '' + html;
340
- }
341
- inertBodyElement.innerHTML = html;
314
+ var inertBodyElement;
315
+ (function(window) {
316
+ var doc;
317
+ if (window.document && window.document.implementation) {
318
+ doc = window.document.implementation.createHTMLDocument("inert");
319
+ } else {
320
+ throw $sanitizeMinErr('noinert', "Can't create an inert html document");
321
+ }
322
+ var docElement = doc.documentElement || doc.getDocumentElement();
323
+ var bodyElements = docElement.getElementsByTagName('body');
342
324
 
343
- //mXSS protection
344
- var mXSSAttempts = 5;
345
- do {
346
- if (mXSSAttempts === 0) {
347
- throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
325
+ // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
326
+ if (bodyElements.length === 1) {
327
+ inertBodyElement = bodyElements[0];
328
+ } else {
329
+ var html = doc.createElement('html');
330
+ inertBodyElement = doc.createElement('body');
331
+ html.appendChild(inertBodyElement);
332
+ doc.appendChild(html);
348
333
  }
349
- mXSSAttempts--;
334
+ })(window);
350
335
 
351
- // strip custom-namespaced attributes on IE<=11
352
- if (window.document.documentMode) {
353
- stripCustomNsAttrs(inertBodyElement);
336
+ /**
337
+ * @example
338
+ * htmlParser(htmlString, {
339
+ * start: function(tag, attrs) {},
340
+ * end: function(tag) {},
341
+ * chars: function(text) {},
342
+ * comment: function(text) {}
343
+ * });
344
+ *
345
+ * @param {string} html string
346
+ * @param {object} handler
347
+ */
348
+ function htmlParserImpl(html, handler) {
349
+ if (html === null || html === undefined) {
350
+ html = '';
351
+ } else if (typeof html !== 'string') {
352
+ html = '' + html;
354
353
  }
355
- html = inertBodyElement.innerHTML; //trigger mXSS
356
354
  inertBodyElement.innerHTML = html;
357
- } while (html !== inertBodyElement.innerHTML);
358
-
359
- var node = inertBodyElement.firstChild;
360
- while (node) {
361
- switch (node.nodeType) {
362
- case 1: // ELEMENT_NODE
363
- handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
364
- break;
365
- case 3: // TEXT NODE
366
- handler.chars(node.textContent);
367
- break;
368
- }
369
355
 
370
- var nextNode;
371
- if (!(nextNode = node.firstChild)) {
372
- if (node.nodeType == 1) {
373
- handler.end(node.nodeName.toLowerCase());
356
+ //mXSS protection
357
+ var mXSSAttempts = 5;
358
+ do {
359
+ if (mXSSAttempts === 0) {
360
+ throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
374
361
  }
375
- nextNode = node.nextSibling;
376
- if (!nextNode) {
377
- while (nextNode == null) {
378
- node = node.parentNode;
379
- if (node === inertBodyElement) break;
380
- nextNode = node.nextSibling;
362
+ mXSSAttempts--;
363
+
364
+ // strip custom-namespaced attributes on IE<=11
365
+ if (window.document.documentMode) {
366
+ stripCustomNsAttrs(inertBodyElement);
367
+ }
368
+ html = inertBodyElement.innerHTML; //trigger mXSS
369
+ inertBodyElement.innerHTML = html;
370
+ } while (html !== inertBodyElement.innerHTML);
371
+
372
+ var node = inertBodyElement.firstChild;
373
+ while (node) {
374
+ switch (node.nodeType) {
375
+ case 1: // ELEMENT_NODE
376
+ handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
377
+ break;
378
+ case 3: // TEXT NODE
379
+ handler.chars(node.textContent);
380
+ break;
381
+ }
382
+
383
+ var nextNode;
384
+ if (!(nextNode = node.firstChild)) {
385
+ if (node.nodeType == 1) {
386
+ handler.end(node.nodeName.toLowerCase());
387
+ }
388
+ nextNode = node.nextSibling;
389
+ if (!nextNode) {
390
+ while (nextNode == null) {
391
+ node = node.parentNode;
392
+ if (node === inertBodyElement) break;
393
+ nextNode = node.nextSibling;
381
394
  if (node.nodeType == 1) {
382
- handler.end(node.nodeName.toLowerCase());
395
+ handler.end(node.nodeName.toLowerCase());
396
+ }
383
397
  }
384
398
  }
385
399
  }
400
+ node = nextNode;
386
401
  }
387
- node = nextNode;
388
- }
389
402
 
390
- while (node = inertBodyElement.firstChild) {
391
- inertBodyElement.removeChild(node);
403
+ while (node = inertBodyElement.firstChild) {
404
+ inertBodyElement.removeChild(node);
405
+ }
392
406
  }
393
- }
394
407
 
395
- function attrToMap(attrs) {
396
- var map = {};
397
- for (var i = 0, ii = attrs.length; i < ii; i++) {
398
- var attr = attrs[i];
399
- map[attr.name] = attr.value;
408
+ function attrToMap(attrs) {
409
+ var map = {};
410
+ for (var i = 0, ii = attrs.length; i < ii; i++) {
411
+ var attr = attrs[i];
412
+ map[attr.name] = attr.value;
413
+ }
414
+ return map;
400
415
  }
401
- return map;
402
- }
403
416
 
404
417
 
405
- /**
406
- * Escapes all potentially dangerous characters, so that the
407
- * resulting string can be safely inserted into attribute or
408
- * element text.
409
- * @param value
410
- * @returns {string} escaped text
411
- */
412
- function encodeEntities(value) {
413
- return value.
414
- replace(/&/g, '&amp;').
415
- replace(SURROGATE_PAIR_REGEXP, function(value) {
416
- var hi = value.charCodeAt(0);
417
- var low = value.charCodeAt(1);
418
- return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
419
- }).
420
- replace(NON_ALPHANUMERIC_REGEXP, function(value) {
421
- return '&#' + value.charCodeAt(0) + ';';
422
- }).
423
- replace(/</g, '&lt;').
424
- replace(/>/g, '&gt;');
425
- }
418
+ /**
419
+ * Escapes all potentially dangerous characters, so that the
420
+ * resulting string can be safely inserted into attribute or
421
+ * element text.
422
+ * @param value
423
+ * @returns {string} escaped text
424
+ */
425
+ function encodeEntities(value) {
426
+ return value.
427
+ replace(/&/g, '&amp;').
428
+ replace(SURROGATE_PAIR_REGEXP, function(value) {
429
+ var hi = value.charCodeAt(0);
430
+ var low = value.charCodeAt(1);
431
+ return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
432
+ }).
433
+ replace(NON_ALPHANUMERIC_REGEXP, function(value) {
434
+ return '&#' + value.charCodeAt(0) + ';';
435
+ }).
436
+ replace(/</g, '&lt;').
437
+ replace(/>/g, '&gt;');
438
+ }
426
439
 
427
- /**
428
- * create an HTML/XML writer which writes to buffer
429
- * @param {Array} buf use buf.join('') to get out sanitized html string
430
- * @returns {object} in the form of {
431
- * start: function(tag, attrs) {},
432
- * end: function(tag) {},
433
- * chars: function(text) {},
434
- * comment: function(text) {}
435
- * }
436
- */
437
- function htmlSanitizeWriter(buf, uriValidator) {
438
- var ignoreCurrentElement = false;
439
- var out = angular.bind(buf, buf.push);
440
- return {
441
- start: function(tag, attrs) {
442
- tag = angular.lowercase(tag);
443
- if (!ignoreCurrentElement && blockedElements[tag]) {
444
- ignoreCurrentElement = tag;
445
- }
446
- if (!ignoreCurrentElement && validElements[tag] === true) {
447
- out('<');
448
- out(tag);
449
- angular.forEach(attrs, function(value, key) {
450
- var lkey=angular.lowercase(key);
451
- var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
452
- if (validAttrs[lkey] === true &&
453
- (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
454
- out(' ');
455
- out(key);
456
- out('="');
457
- out(encodeEntities(value));
458
- out('"');
459
- }
460
- });
461
- out('>');
462
- }
463
- },
464
- end: function(tag) {
465
- tag = angular.lowercase(tag);
466
- if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
467
- out('</');
468
- out(tag);
469
- out('>');
470
- }
471
- if (tag == ignoreCurrentElement) {
472
- ignoreCurrentElement = false;
473
- }
474
- },
475
- chars: function(chars) {
476
- if (!ignoreCurrentElement) {
477
- out(encodeEntities(chars));
440
+ /**
441
+ * create an HTML/XML writer which writes to buffer
442
+ * @param {Array} buf use buf.join('') to get out sanitized html string
443
+ * @returns {object} in the form of {
444
+ * start: function(tag, attrs) {},
445
+ * end: function(tag) {},
446
+ * chars: function(text) {},
447
+ * comment: function(text) {}
448
+ * }
449
+ */
450
+ function htmlSanitizeWriterImpl(buf, uriValidator) {
451
+ var ignoreCurrentElement = false;
452
+ var out = bind(buf, buf.push);
453
+ return {
454
+ start: function(tag, attrs) {
455
+ tag = lowercase(tag);
456
+ if (!ignoreCurrentElement && blockedElements[tag]) {
457
+ ignoreCurrentElement = tag;
458
+ }
459
+ if (!ignoreCurrentElement && validElements[tag] === true) {
460
+ out('<');
461
+ out(tag);
462
+ forEach(attrs, function(value, key) {
463
+ var lkey = lowercase(key);
464
+ var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
465
+ if (validAttrs[lkey] === true &&
466
+ (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
467
+ out(' ');
468
+ out(key);
469
+ out('="');
470
+ out(encodeEntities(value));
471
+ out('"');
472
+ }
473
+ });
474
+ out('>');
475
+ }
476
+ },
477
+ end: function(tag) {
478
+ tag = lowercase(tag);
479
+ if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
480
+ out('</');
481
+ out(tag);
482
+ out('>');
483
+ }
484
+ if (tag == ignoreCurrentElement) {
485
+ ignoreCurrentElement = false;
486
+ }
487
+ },
488
+ chars: function(chars) {
489
+ if (!ignoreCurrentElement) {
490
+ out(encodeEntities(chars));
491
+ }
478
492
  }
479
- }
480
- };
481
- }
493
+ };
494
+ }
482
495
 
483
496
 
484
- /**
485
- * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
486
- * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
487
- * to allow any of these custom attributes. This method strips them all.
488
- *
489
- * @param node Root element to process
490
- */
491
- function stripCustomNsAttrs(node) {
492
- if (node.nodeType === window.Node.ELEMENT_NODE) {
493
- var attrs = node.attributes;
494
- for (var i = 0, l = attrs.length; i < l; i++) {
495
- var attrNode = attrs[i];
496
- var attrName = attrNode.name.toLowerCase();
497
- if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
498
- node.removeAttributeNode(attrNode);
499
- i--;
500
- l--;
497
+ /**
498
+ * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
499
+ * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
500
+ * to allow any of these custom attributes. This method strips them all.
501
+ *
502
+ * @param node Root element to process
503
+ */
504
+ function stripCustomNsAttrs(node) {
505
+ if (node.nodeType === window.Node.ELEMENT_NODE) {
506
+ var attrs = node.attributes;
507
+ for (var i = 0, l = attrs.length; i < l; i++) {
508
+ var attrNode = attrs[i];
509
+ var attrName = attrNode.name.toLowerCase();
510
+ if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
511
+ node.removeAttributeNode(attrNode);
512
+ i--;
513
+ l--;
514
+ }
501
515
  }
502
516
  }
503
- }
504
517
 
505
- var nextNode = node.firstChild;
506
- if (nextNode) {
507
- stripCustomNsAttrs(nextNode);
508
- }
518
+ var nextNode = node.firstChild;
519
+ if (nextNode) {
520
+ stripCustomNsAttrs(nextNode);
521
+ }
509
522
 
510
- nextNode = node.nextSibling;
511
- if (nextNode) {
512
- stripCustomNsAttrs(nextNode);
523
+ nextNode = node.nextSibling;
524
+ if (nextNode) {
525
+ stripCustomNsAttrs(nextNode);
526
+ }
513
527
  }
514
528
  }
515
529
 
530
+ function sanitizeText(chars) {
531
+ var buf = [];
532
+ var writer = htmlSanitizeWriter(buf, noop);
533
+ writer.chars(chars);
534
+ return buf.join('');
535
+ }
516
536
 
517
537
 
518
538
  // define ngSanitize module and register $sanitize service
519
539
  angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
520
540
 
521
- /* global sanitizeText: false */
522
-
523
541
  /**
524
542
  * @ngdoc filter
525
543
  * @name linky
@@ -653,12 +671,20 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
653
671
  MAILTO_REGEXP = /^mailto:/i;
654
672
 
655
673
  var linkyMinErr = angular.$$minErr('linky');
674
+ var isDefined = angular.isDefined;
675
+ var isFunction = angular.isFunction;
676
+ var isObject = angular.isObject;
656
677
  var isString = angular.isString;
657
678
 
658
679
  return function(text, target, attributes) {
659
680
  if (text == null || text === '') return text;
660
681
  if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
661
682
 
683
+ var attributesFn =
684
+ isFunction(attributes) ? attributes :
685
+ isObject(attributes) ? function getAttributesObject() {return attributes;} :
686
+ function getEmptyAttributesObject() {return {};};
687
+
662
688
  var match;
663
689
  var raw = text;
664
690
  var html = [];
@@ -687,19 +713,14 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
687
713
  }
688
714
 
689
715
  function addLink(url, text) {
690
- var key;
716
+ var key, linkAttributes = attributesFn(url);
691
717
  html.push('<a ');
692
- if (angular.isFunction(attributes)) {
693
- attributes = attributes(url);
694
- }
695
- if (angular.isObject(attributes)) {
696
- for (key in attributes) {
697
- html.push(key + '="' + attributes[key] + '" ');
698
- }
699
- } else {
700
- attributes = {};
718
+
719
+ for (key in linkAttributes) {
720
+ html.push(key + '="' + linkAttributes[key] + '" ');
701
721
  }
702
- if (angular.isDefined(target) && !('target' in attributes)) {
722
+
723
+ if (isDefined(target) && !('target' in linkAttributes)) {
703
724
  html.push('target="',
704
725
  target,
705
726
  '" ');