angularjs-rails 1.5.6 → 1.5.8

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.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
  '" ');