angular-gem 1.1.5 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/lib/angular-gem/version.rb +1 -1
  2. data/lib/tasks/copy.rake +22 -11
  3. data/lib/tasks/tag.rake +2 -11
  4. data/vendor/assets/javascripts/1.2.0/angular-animate.js +1226 -0
  5. data/vendor/assets/javascripts/1.2.0/angular-animate.min.js +21 -0
  6. data/vendor/assets/javascripts/1.2.0/angular-animate.min.js.map +8 -0
  7. data/vendor/assets/javascripts/{angular-cookies-unstable.js → 1.2.0/angular-cookies.js} +21 -4
  8. data/vendor/assets/javascripts/1.2.0/angular-cookies.min.js +8 -0
  9. data/vendor/assets/javascripts/1.2.0/angular-cookies.min.js.map +8 -0
  10. data/vendor/assets/javascripts/1.2.0/angular-csp.css +24 -0
  11. data/vendor/assets/javascripts/{angular-loader-unstable.js → 1.2.0/angular-loader.js} +48 -29
  12. data/vendor/assets/javascripts/1.2.0/angular-loader.min.js +8 -0
  13. data/vendor/assets/javascripts/1.2.0/angular-loader.min.js.map +8 -0
  14. data/vendor/assets/javascripts/{angular-mocks-unstable.js → 1.2.0/angular-mocks.js} +460 -216
  15. data/vendor/assets/javascripts/{angular-resource-unstable.js → 1.2.0/angular-resource.js} +189 -148
  16. data/vendor/assets/javascripts/1.2.0/angular-resource.min.js +12 -0
  17. data/vendor/assets/javascripts/1.2.0/angular-resource.min.js.map +8 -0
  18. data/vendor/assets/javascripts/1.2.0/angular-route.js +880 -0
  19. data/vendor/assets/javascripts/1.2.0/angular-route.min.js +14 -0
  20. data/vendor/assets/javascripts/1.2.0/angular-route.min.js.map +8 -0
  21. data/vendor/assets/javascripts/{angular-sanitize-unstable.js → 1.2.0/angular-sanitize.js} +142 -123
  22. data/vendor/assets/javascripts/1.2.0/angular-sanitize.min.js +14 -0
  23. data/vendor/assets/javascripts/1.2.0/angular-sanitize.min.js.map +8 -0
  24. data/vendor/assets/javascripts/{angular-scenario-unstable.js → 1.2.0/angular-scenario.js} +15978 -12405
  25. data/vendor/assets/javascripts/{angular-mobile-unstable.js → 1.2.0/angular-touch.js} +214 -111
  26. data/vendor/assets/javascripts/1.2.0/angular-touch.min.js +13 -0
  27. data/vendor/assets/javascripts/1.2.0/angular-touch.min.js.map +8 -0
  28. data/vendor/assets/javascripts/{angular-unstable.js → 1.2.0/angular.js} +10167 -7012
  29. data/vendor/assets/javascripts/1.2.0/angular.min.js +200 -0
  30. data/vendor/assets/javascripts/1.2.0/angular.min.js.map +8 -0
  31. data/vendor/assets/javascripts/1.2.0/errors.json +1 -0
  32. data/vendor/assets/javascripts/1.2.0/version.json +1 -0
  33. data/vendor/assets/javascripts/1.2.0/version.txt +1 -0
  34. data/vendor/assets/javascripts/angular-animate.js +1226 -0
  35. data/vendor/assets/javascripts/angular-animate.min.js +21 -0
  36. data/vendor/assets/javascripts/angular-cookies.js +21 -4
  37. data/vendor/assets/javascripts/angular-cookies.min.js +8 -0
  38. data/vendor/assets/javascripts/angular-loader.js +63 -17
  39. data/vendor/assets/javascripts/angular-loader.min.js +8 -0
  40. data/vendor/assets/javascripts/angular-mocks.js +553 -211
  41. data/vendor/assets/javascripts/angular-resource.js +268 -147
  42. data/vendor/assets/javascripts/angular-resource.min.js +12 -0
  43. data/vendor/assets/javascripts/angular-route.js +880 -0
  44. data/vendor/assets/javascripts/angular-route.min.js +14 -0
  45. data/vendor/assets/javascripts/angular-sanitize.js +165 -125
  46. data/vendor/assets/javascripts/angular-sanitize.min.js +14 -0
  47. data/vendor/assets/javascripts/angular-scenario.js +16615 -10889
  48. data/vendor/assets/javascripts/angular-touch.js +563 -0
  49. data/vendor/assets/javascripts/angular-touch.min.js +13 -0
  50. data/vendor/assets/javascripts/angular.js +9717 -4533
  51. data/vendor/assets/javascripts/angular.min.js +200 -0
  52. metadata +44 -21
  53. data/test/dummy/log/test.log +0 -2
  54. data/test/dummy/tmp/cache/assets/D65/250/sprockets%2F54a960d46bb0b354e8bd46fa03f5e0e4 +0 -0
  55. data/test/dummy/tmp/cache/assets/D6A/FB0/sprockets%2F92721e9941b77adcfdfba3d060622de2 +0 -0
  56. data/test/dummy/tmp/cache/assets/E07/040/sprockets%2Ff55b8ce9d0f28ce36b768a1c7aeb2ef3 +0 -0
  57. data/test/tmp/app/assets/javascripts/application.js +0 -5
@@ -0,0 +1,14 @@
1
+ /*
2
+ AngularJS v1.2.0
3
+ (c) 2010-2012 Google, Inc. http://angularjs.org
4
+ License: MIT
5
+ */
6
+ (function(t,c,B){'use strict';function w(s,r,g,a,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",compile:function(k,d,A){return function(u,k,d){function v(){l&&(l.$destroy(),l=null);m&&(h.leave(m),m=null)}function x(){var f=s.current&&s.current.locals,y=f&&f.$template;if(y){var z=u.$new();A(z,function(e){e.html(y);h.enter(e,null,m||k,function(){!c.isDefined(n)||n&&!u.$eval(n)||r()});v();var p=g(e.contents()),q=s.current;l=q.scope=z;m=e;if(q.controller){f.$scope=l;var d=a(q.controller,
7
+ f);q.controllerAs&&(l[q.controllerAs]=d);e.data("$ngControllerController",d);e.children().data("$ngControllerController",d)}p(l);l.$emit("$viewContentLoaded");l.$eval(b)})}else v()}var l,m,n=d.autoscroll,b=d.onload||"";u.$on("$routeChangeSuccess",x);x()}}}}t=c.module("ngRoute",["ng"]).provider("$route",function(){function s(a,h){return c.extend(new (c.extend(function(){},{prototype:a})),h)}function r(a,c){var k=c.caseInsensitiveMatch,d={originalPath:a,regexp:a},g=d.keys=[];a=a.replace(/([().])/g,
8
+ "\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g,function(a,c,h,d){a="?"===d?d:null;d="*"===d?d:null;g.push({name:h,optional:!!a});c=c||"";return""+(a?"":c)+"(?:"+(a?c:"")+(d&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=RegExp("^"+a+"$",k?"i":"");return d}var g={};this.when=function(a,h){g[a]=c.extend({reloadOnSearch:!0},h,a&&r(a,h));if(a){var k="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";g[k]=c.extend({redirectTo:a},r(k,h))}return this};this.otherwise=function(a){this.when(null,
9
+ a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,h,k,d,r,u,t,w){function v(){var b=x(),f=n.current;if(b&&f&&b.$$route===f.$$route&&c.equals(b.pathParams,f.pathParams)&&!b.reloadOnSearch&&!m)f.params=b.params,c.copy(f.params,k),a.$broadcast("$routeUpdate",f);else if(b||f)m=!1,a.$broadcast("$routeChangeStart",b,f),(n.current=b)&&b.redirectTo&&(c.isString(b.redirectTo)?h.path(l(b.redirectTo,b.params)).search(b.params).replace():
10
+ h.url(b.redirectTo(b.pathParams,h.path(),h.search())).replace()),d.when(b).then(function(){if(b){var a=c.extend({},b.resolve),f,e;c.forEach(a,function(b,f){a[f]=c.isString(b)?r.get(b):r.invoke(b)});c.isDefined(f=b.template)?c.isFunction(f)&&(f=f(b.params)):c.isDefined(e=b.templateUrl)&&(c.isFunction(e)&&(e=e(b.params)),e=w.getTrustedResourceUrl(e),c.isDefined(e)&&(b.loadedTemplateUrl=e,f=u.get(e,{cache:t}).then(function(b){return b.data})));c.isDefined(f)&&(a.$template=f);return d.all(a)}}).then(function(d){b==
11
+ n.current&&(b&&(b.locals=d,c.copy(b.params,k)),a.$broadcast("$routeChangeSuccess",b,f))},function(c){b==n.current&&a.$broadcast("$routeChangeError",b,f,c)})}function x(){var b,a;c.forEach(g,function(d,l){var e;if(e=!a){var p=h.path();e=d.keys;var q={};if(d.regexp)if(p=d.regexp.exec(p)){for(var g=1,k=p.length;g<k;++g){var m=e[g-1],n="string"==typeof p[g]?decodeURIComponent(p[g]):p[g];m&&n&&(q[m.name]=n)}e=q}else e=null;else e=null;e=b=e}e&&(a=s(d,{params:c.extend({},h.search(),b),pathParams:b}),a.$$route=
12
+ d)});return a||g[null]&&s(g[null],{params:{},pathParams:{}})}function l(a,d){var g=[];c.forEach((a||"").split(":"),function(a,b){if(0===b)g.push(a);else{var c=a.match(/(\w+)(.*)/),h=c[1];g.push(d[h]);g.push(c[2]||"");delete d[h]}});return g.join("")}var m=!1,n={routes:g,reload:function(){m=!0;a.$evalAsync(v)}};a.$on("$locationChangeSuccess",v);return n}]});t.provider("$routeParams",function(){this.$get=function(){return{}}});t.directive("ngView",w);w.$inject=["$route","$anchorScroll","$compile","$controller",
13
+ "$animate"]})(window,window.angular);
14
+ //# sourceMappingURL=angular-route.min.js.map
@@ -1,15 +1,26 @@
1
1
  /**
2
- * @license AngularJS v1.0.7
2
+ * @license AngularJS v1.2.0
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {
7
- 'use strict';
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ var $sanitizeMinErr = angular.$$minErr('$sanitize');
8
9
 
9
10
  /**
10
11
  * @ngdoc overview
11
12
  * @name ngSanitize
12
13
  * @description
14
+ *
15
+ * # ngSanitize
16
+ *
17
+ * The `ngSanitize` module provides functionality to sanitize HTML.
18
+ *
19
+ * {@installModule sanitize}
20
+ *
21
+ * <div doc-module-components="ngSanitize"></div>
22
+ *
23
+ * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
13
24
  */
14
25
 
15
26
  /*
@@ -46,72 +57,80 @@
46
57
  *
47
58
  * @example
48
59
  <doc:example module="ngSanitize">
49
- <doc:source>
50
- <script>
51
- function Ctrl($scope) {
52
- $scope.snippet =
53
- '<p style="color:blue">an html\n' +
54
- '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
55
- 'snippet</p>';
56
- }
57
- </script>
58
- <div ng-controller="Ctrl">
59
- Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
60
- <table>
61
- <tr>
62
- <td>Filter</td>
63
- <td>Source</td>
64
- <td>Rendered</td>
65
- </tr>
66
- <tr id="html-filter">
67
- <td>html filter</td>
68
- <td>
69
- <pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre>
70
- </td>
71
- <td>
72
- <div ng-bind-html="snippet"></div>
73
- </td>
74
- </tr>
75
- <tr id="escaped-html">
76
- <td>no filter</td>
77
- <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
78
- <td><div ng-bind="snippet"></div></td>
79
- </tr>
80
- <tr id="html-unsafe-filter">
81
- <td>unsafe html filter</td>
82
- <td><pre>&lt;div ng-bind-html-unsafe="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
83
- <td><div ng-bind-html-unsafe="snippet"></div></td>
84
- </tr>
85
- </table>
86
- </div>
87
- </doc:source>
88
- <doc:scenario>
89
- it('should sanitize the html snippet ', function() {
90
- expect(using('#html-filter').element('div').html()).
91
- toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
92
- });
93
-
94
- it('should escape snippet without any filter', function() {
95
- expect(using('#escaped-html').element('div').html()).
96
- toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
97
- "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
98
- "snippet&lt;/p&gt;");
99
- });
100
-
101
- it('should inline raw snippet if filtered as unsafe', function() {
102
- expect(using('#html-unsafe-filter').element("div").html()).
103
- toBe("<p style=\"color:blue\">an html\n" +
104
- "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
105
- "snippet</p>");
106
- });
107
-
108
- it('should update', function() {
109
- input('snippet').enter('new <b>text</b>');
110
- expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>');
111
- expect(using('#escaped-html').element('div').html()).toBe("new &lt;b&gt;text&lt;/b&gt;");
112
- expect(using('#html-unsafe-filter').binding("snippet")).toBe('new <b>text</b>');
113
- });
114
- </doc:scenario>
60
+ <doc:source>
61
+ <script>
62
+ function Ctrl($scope, $sce) {
63
+ $scope.snippet =
64
+ '<p style="color:blue">an html\n' +
65
+ '<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
66
+ 'snippet</p>';
67
+ $scope.deliberatelyTrustDangerousSnippet = function() {
68
+ return $sce.trustAsHtml($scope.snippet);
69
+ };
70
+ }
71
+ </script>
72
+ <div ng-controller="Ctrl">
73
+ Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
74
+ <table>
75
+ <tr>
76
+ <td>Directive</td>
77
+ <td>How</td>
78
+ <td>Source</td>
79
+ <td>Rendered</td>
80
+ </tr>
81
+ <tr id="bind-html-with-sanitize">
82
+ <td>ng-bind-html</td>
83
+ <td>Automatically uses $sanitize</td>
84
+ <td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
85
+ <td><div ng-bind-html="snippet"></div></td>
86
+ </tr>
87
+ <tr id="bind-html-with-trust">
88
+ <td>ng-bind-html</td>
89
+ <td>Bypass $sanitize by explicitly trusting the dangerous value</td>
90
+ <td>
91
+ <pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
92
+ &lt;/div&gt;</pre>
93
+ </td>
94
+ <td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
95
+ </tr>
96
+ <tr id="bind-default">
97
+ <td>ng-bind</td>
98
+ <td>Automatically escapes</td>
99
+ <td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
100
+ <td><div ng-bind="snippet"></div></td>
101
+ </tr>
102
+ </table>
103
+ </div>
104
+ </doc:source>
105
+ <doc:scenario>
106
+ it('should sanitize the html snippet by default', function() {
107
+ expect(using('#bind-html-with-sanitize').element('div').html()).
108
+ toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
109
+ });
110
+
111
+ it('should inline raw snippet if bound to a trusted value', function() {
112
+ expect(using('#bind-html-with-trust').element("div").html()).
113
+ toBe("<p style=\"color:blue\">an html\n" +
114
+ "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
115
+ "snippet</p>");
116
+ });
117
+
118
+ it('should escape snippet without any filter', function() {
119
+ expect(using('#bind-default').element('div').html()).
120
+ toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
121
+ "&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
122
+ "snippet&lt;/p&gt;");
123
+ });
124
+
125
+ it('should update', function() {
126
+ input('snippet').enter('new <b onclick="alert(1)">text</b>');
127
+ expect(using('#bind-html-with-sanitize').element('div').html()).toBe('new <b>text</b>');
128
+ expect(using('#bind-html-with-trust').element('div').html()).toBe(
129
+ 'new <b onclick="alert(1)">text</b>');
130
+ expect(using('#bind-default').element('div').html()).toBe(
131
+ "new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
132
+ });
133
+ </doc:scenario>
115
134
  </doc:example>
116
135
  */
117
136
  var $sanitize = function(html) {
@@ -122,15 +141,18 @@ var $sanitize = function(html) {
122
141
 
123
142
 
124
143
  // Regular Expressions for parsing tags and attributes
125
- var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
144
+ var START_TAG_REGEXP =
145
+ /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
126
146
  END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/,
127
147
  ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
128
148
  BEGIN_TAG_REGEXP = /^</,
129
149
  BEGING_END_TAGE_REGEXP = /^<\s*\//,
130
150
  COMMENT_REGEXP = /<!--(.*?)-->/g,
151
+ DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
131
152
  CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
132
- URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/,
133
- NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
153
+ URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/i,
154
+ // Match everything outside of normal chars and " (quote character)
155
+ NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
134
156
 
135
157
 
136
158
  // Good source of info about elements and attributes
@@ -145,23 +167,29 @@ var voidElements = makeMap("area,br,col,hr,img,wbr");
145
167
  // http://dev.w3.org/html5/spec/Overview.html#optional-tags
146
168
  var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
147
169
  optionalEndTagInlineElements = makeMap("rp,rt"),
148
- optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements);
170
+ optionalEndTagElements = angular.extend({},
171
+ optionalEndTagInlineElements,
172
+ optionalEndTagBlockElements);
149
173
 
150
174
  // Safe Block Elements - HTML5
151
- var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," +
152
- "blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," +
153
- "header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
175
+ var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
176
+ "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
177
+ "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
154
178
 
155
179
  // Inline Elements - HTML5
156
- var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," +
157
- "big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," +
158
- "span,strike,strong,sub,sup,time,tt,u,var"));
180
+ var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
181
+ "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
182
+ "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
159
183
 
160
184
 
161
185
  // Special Elements (can contain anything)
162
186
  var specialElements = makeMap("script,style");
163
187
 
164
- var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements);
188
+ var validElements = angular.extend({},
189
+ voidElements,
190
+ blockElements,
191
+ inlineElements,
192
+ optionalEndTagElements);
165
193
 
166
194
  //Attributes that have href and hence need to be sanitized
167
195
  var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap");
@@ -203,14 +231,22 @@ function htmlParser( html, handler ) {
203
231
 
204
232
  // Comment
205
233
  if ( html.indexOf("<!--") === 0 ) {
206
- index = html.indexOf("-->");
234
+ // comments containing -- are not allowed unless they terminate the comment
235
+ index = html.indexOf("--", 4);
207
236
 
208
- if ( index >= 0 ) {
237
+ if ( index >= 0 && html.lastIndexOf("-->", index) === index) {
209
238
  if (handler.comment) handler.comment( html.substring( 4, index ) );
210
239
  html = html.substring( index + 3 );
211
240
  chars = false;
212
241
  }
242
+ // DOCTYPE
243
+ } else if ( DOCTYPE_REGEXP.test(html) ) {
244
+ match = html.match( DOCTYPE_REGEXP );
213
245
 
246
+ if ( match ) {
247
+ html = html.replace( match[0] , '');
248
+ chars = false;
249
+ }
214
250
  // end tag
215
251
  } else if ( BEGING_END_TAGE_REGEXP.test(html) ) {
216
252
  match = html.match( END_TAG_REGEXP );
@@ -242,21 +278,21 @@ function htmlParser( html, handler ) {
242
278
  }
243
279
 
244
280
  } else {
245
- html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){
246
- text = text.
247
- replace(COMMENT_REGEXP, "$1").
248
- replace(CDATA_REGEXP, "$1");
281
+ html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
282
+ function(all, text){
283
+ text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
249
284
 
250
- if (handler.chars) handler.chars( decodeEntities(text) );
285
+ if (handler.chars) handler.chars( decodeEntities(text) );
251
286
 
252
- return "";
287
+ return "";
253
288
  });
254
289
 
255
290
  parseEndTag( "", stack.last() );
256
291
  }
257
292
 
258
293
  if ( html == last ) {
259
- throw "Parse Error: " + html;
294
+ throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
295
+ "of html: {0}", html);
260
296
  }
261
297
  last = html;
262
298
  }
@@ -283,13 +319,14 @@ function htmlParser( html, handler ) {
283
319
 
284
320
  var attrs = {};
285
321
 
286
- rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
287
- var value = doubleQuotedValue
288
- || singleQoutedValue
289
- || unqoutedValue
290
- || '';
322
+ rest.replace(ATTR_REGEXP,
323
+ function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
324
+ var value = doubleQuotedValue
325
+ || singleQuotedValue
326
+ || unquotedValue
327
+ || '';
291
328
 
292
- attrs[name] = decodeEntities(value);
329
+ attrs[name] = decodeEntities(value);
293
330
  });
294
331
  if (handler.start) handler.start( tagName, attrs, unary );
295
332
  }
@@ -361,12 +398,12 @@ function htmlSanitizeWriter(buf){
361
398
  if (!ignore && specialElements[tag]) {
362
399
  ignore = tag;
363
400
  }
364
- if (!ignore && validElements[tag] == true) {
401
+ if (!ignore && validElements[tag] === true) {
365
402
  out('<');
366
403
  out(tag);
367
404
  angular.forEach(attrs, function(value, key){
368
405
  var lkey=angular.lowercase(key);
369
- if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
406
+ if (validAttrs[lkey]===true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
370
407
  out(' ');
371
408
  out(key);
372
409
  out('="');
@@ -379,7 +416,7 @@ function htmlSanitizeWriter(buf){
379
416
  },
380
417
  end: function(tag){
381
418
  tag = angular.lowercase(tag);
382
- if (!ignore && validElements[tag] == true) {
419
+ if (!ignore && validElements[tag] === true) {
383
420
  out('</');
384
421
  out(tag);
385
422
  out('>');
@@ -400,28 +437,7 @@ function htmlSanitizeWriter(buf){
400
437
  // define ngSanitize module and register $sanitize service
401
438
  angular.module('ngSanitize', []).value('$sanitize', $sanitize);
402
439
 
403
- /**
404
- * @ngdoc directive
405
- * @name ngSanitize.directive:ngBindHtml
406
- *
407
- * @description
408
- * Creates a binding that will sanitize the result of evaluating the `expression` with the
409
- * {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element.
410
- *
411
- * See {@link ngSanitize.$sanitize $sanitize} docs for examples.
412
- *
413
- * @element ANY
414
- * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
415
- */
416
- angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
417
- return function(scope, element, attr) {
418
- element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
419
- scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
420
- value = $sanitize(value);
421
- element.html(value || '');
422
- });
423
- };
424
- }]);
440
+ /* global htmlSanitizeWriter: false */
425
441
 
426
442
  /**
427
443
  * @ngdoc filter
@@ -429,10 +445,13 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
429
445
  * @function
430
446
  *
431
447
  * @description
432
- * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
433
- * plain email address links.
448
+ * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
449
+ * plain email address links.
450
+ *
451
+ * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
434
452
  *
435
453
  * @param {string} text Input text.
454
+ * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
436
455
  * @returns {string} Html-linkified text.
437
456
  *
438
457
  * @usage
@@ -449,6 +468,7 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
449
468
  'mailto:us@somewhere.org,\n'+
450
469
  'another@somewhere.org,\n'+
451
470
  'and one more: ftp://127.0.0.1/.';
471
+ $scope.snippetWithTarget = 'http://angularjs.org/';
452
472
  }
453
473
  </script>
454
474
  <div ng-controller="Ctrl">
@@ -468,6 +488,15 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
468
488
  <div ng-bind-html="snippet | linky"></div>
469
489
  </td>
470
490
  </tr>
491
+ <tr id="linky-target">
492
+ <td>linky target</td>
493
+ <td>
494
+ <pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
495
+ </td>
496
+ <td>
497
+ <div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
498
+ </td>
499
+ </tr>
471
500
  <tr id="escaped-html">
472
501
  <td>no filter</td>
473
502
  <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
@@ -500,14 +529,20 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
500
529
  toBe('new <a href="http://link">http://link</a>.');
501
530
  expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
502
531
  });
532
+
533
+ it('should work with the target property', function() {
534
+ expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
535
+ toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
536
+ });
503
537
  </doc:scenario>
504
538
  </doc:example>
505
539
  */
506
540
  angular.module('ngSanitize').filter('linky', function() {
507
- var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
541
+ var LINKY_URL_REGEXP =
542
+ /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
508
543
  MAILTO_REGEXP = /^mailto:/;
509
544
 
510
- return function(text) {
545
+ return function(text, target) {
511
546
  if (!text) return text;
512
547
  var match;
513
548
  var raw = text;
@@ -516,6 +551,10 @@ angular.module('ngSanitize').filter('linky', function() {
516
551
  var writer = htmlSanitizeWriter(html);
517
552
  var url;
518
553
  var i;
554
+ var properties = {};
555
+ if (angular.isDefined(target)) {
556
+ properties.target = target;
557
+ }
519
558
  while ((match = raw.match(LINKY_URL_REGEXP))) {
520
559
  // We can not end in these as they are sometimes found at the end of the sentence
521
560
  url = match[0];
@@ -523,7 +562,8 @@ angular.module('ngSanitize').filter('linky', function() {
523
562
  if (match[2] == match[3]) url = 'mailto:' + url;
524
563
  i = match.index;
525
564
  writer.chars(raw.substr(0, i));
526
- writer.start('a', {href:url});
565
+ properties.href = url;
566
+ writer.start('a', properties);
527
567
  writer.chars(match[0].replace(MAILTO_REGEXP, ''));
528
568
  writer.end('a');
529
569
  raw = raw.substring(i + match[0].length);