angular-gem 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. data/README.md +9 -2
  2. data/Rakefile +86 -19
  3. data/lib/angular-gem/version.rb +1 -1
  4. data/lib/generators/angular/resource_helpers.rb +13 -13
  5. data/test/dummy/log/test.log +1 -3
  6. data/test/dummy/tmp/cache/assets/{DF8/FD0/sprockets%2F87b01decd5c7fd3c6f174b8cba0d810f → D65/250/sprockets%2F54a960d46bb0b354e8bd46fa03f5e0e4} +0 -0
  7. data/test/dummy/tmp/cache/assets/{D73/C00/sprockets%2Fa3620dc4bbd0562669b546e9c58eede4 → D6A/FB0/sprockets%2F92721e9941b77adcfdfba3d060622de2} +0 -0
  8. data/test/dummy/tmp/cache/assets/E07/040/sprockets%2Ff55b8ce9d0f28ce36b768a1c7aeb2ef3 +0 -0
  9. data/test/test_helper.rb +3 -0
  10. data/vendor/assets/javascripts/1.0.5/angular-1.0.5.js +14733 -0
  11. data/vendor/assets/javascripts/1.0.5/angular-resource-1.0.5.js +445 -0
  12. data/vendor/assets/javascripts/1.0.5/angular-sanitize-1.0.5.js +535 -0
  13. data/{test/dummy/tmp/cache/assets/CF7/120/sprockets%2Fd6adf41113c67a57c4a1330845ba0d08 → vendor/assets/javascripts/1.1.2/angular-1.1.2.js} +0 -0
  14. data/vendor/assets/javascripts/1.1.2/angular-resource-1.1.2.js +472 -0
  15. data/vendor/assets/javascripts/1.1.2/angular-sanitize-1.1.2.js +556 -0
  16. data/{test/dummy/tmp/cache/assets/CD5/C60/sprockets%2F4348b099c4aadda10262e757261e0f81 → vendor/assets/javascripts/1.1.3/angular-1.1.3.js} +0 -0
  17. data/vendor/assets/javascripts/1.1.3/angular-resource-1.1.3.js +507 -0
  18. data/vendor/assets/javascripts/1.1.3/angular-sanitize-1.1.3.js +556 -0
  19. data/vendor/assets/javascripts/angular-resource-unstable.js +507 -0
  20. data/vendor/assets/javascripts/angular-resource.js +23 -50
  21. data/vendor/assets/javascripts/angular-sanitize-unstable.js +556 -0
  22. data/vendor/assets/javascripts/angular-sanitize.js +4 -25
  23. data/{test/dummy/tmp/cache/assets/CE3/300/sprockets%2Fc11999ba09d72e745355212e48bc72dd → vendor/assets/javascripts/angular-unstable.js} +0 -0
  24. data/vendor/assets/javascripts/angular.js +305 -454
  25. metadata +53 -21
  26. data/test/dummy/tmp/cache/assets/D71/060/sprockets%2Fc434a3f93ecb87049eb12e766005fede +0 -0
  27. data/test/dummy/tmp/cache/assets/D79/F40/sprockets%2F9088cfe323d1b81dd526e6a9e3ed358c +0 -0
  28. data/test/dummy/tmp/cache/assets/D9E/830/sprockets%2Fbb7eb0f50f1afed5705594e9d812b8d0 +0 -0
  29. data/test/dummy/tmp/cache/assets/DD4/250/sprockets%2Feaa70f0d0954a5edffe77c9ca1e93860 +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.1.2
2
+ * @license AngularJS v1.0.5
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -30,8 +30,7 @@
30
30
  * number, like this: `$resource('http://example.com\\:8080/api')`.
31
31
  *
32
32
  * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
33
- * `actions` methods. If any of the parameter value is a function, it will be executed every time
34
- * when a param value needs to be obtained for a request (unless the param was overriden).
33
+ * `actions` methods.
35
34
  *
36
35
  * Each key value in the parameter object is first bound to url template if present and then any
37
36
  * excess keys are appended to the url search query after the `?`.
@@ -43,40 +42,21 @@
43
42
  * the data object (useful for non-GET operations).
44
43
  *
45
44
  * @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
46
- * default set of resource actions. The declaration should be created in the format of {@link
47
- * ng.$http#Parameters $http.config}:
45
+ * default set of resource actions. The declaration should be created in the following format:
48
46
  *
49
- * {action1: {method:?, params:?, isArray:?, headers:?, ...},
50
- * action2: {method:?, params:?, isArray:?, headers:?, ...},
47
+ * {action1: {method:?, params:?, isArray:?},
48
+ * action2: {method:?, params:?, isArray:?},
51
49
  * ...}
52
50
  *
53
51
  * Where:
54
52
  *
55
- * - **`action`** – {string} – The name of action. This name becomes the name of the method on your
53
+ * - `action` – {string} – The name of action. This name becomes the name of the method on your
56
54
  * resource object.
57
- * - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
58
- * and `JSONP`.
59
- * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of the
60
- * parameter value is a function, it will be executed every time when a param value needs to be
61
- * obtained for a request (unless the param was overriden).
62
- * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, see
55
+ * - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
56
+ * and `JSONP`
57
+ * - `params` – {object=} – Optional set of pre-bound parameters for this action.
58
+ * - isArray {boolean=} If true then the returned object for this action is an array, see
63
59
  * `returns` section.
64
- * - **`transformRequest`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
65
- * transform function or an array of such functions. The transform function takes the http
66
- * request body and headers and returns its transformed (typically serialized) version.
67
- * - **`transformResponse`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
68
- * transform function or an array of such functions. The transform function takes the http
69
- * response body and headers and returns its transformed (typically deserialized) version.
70
- * - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
71
- * GET request, otherwise if a cache instance built with
72
- * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
73
- * caching.
74
- * - **`timeout`** – `{number}` – timeout in milliseconds.
75
- * - **`withCredentials`** - `{boolean}` - whether to to set the `withCredentials` flag on the
76
- * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
77
- * requests with credentials} for more information.
78
- * - **`responseType`** - `{string}` - see {@link
79
- * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
80
60
  *
81
61
  * @returns {Object} A resource "class" object with methods for the default set of resource actions
82
62
  * optionally extended with custom `actions`. The default set contains these actions:
@@ -89,9 +69,9 @@
89
69
  *
90
70
  * Calling these methods invoke an {@link ng.$http} with the specified http method,
91
71
  * destination and parameters. When the data is returned from the server then the object is an
92
- * instance of the resource class `save`, `remove` and `delete` actions are available on it as
93
- * methods with the `$` prefix. This allows you to easily perform CRUD operations (create, read,
94
- * update, delete) on server-side data like this:
72
+ * instance of the resource class. The actions `save`, `remove` and `delete` are available on it
73
+ * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
74
+ * read, update, delete) on server-side data like this:
95
75
  * <pre>
96
76
  var User = $resource('/user/:userId', {userId:'@id'});
97
77
  var user = User.get({userId:123}, function() {
@@ -158,7 +138,7 @@
158
138
  * The object returned from this function execution is a resource "class" which has "static" method
159
139
  * for each action in the definition.
160
140
  *
161
- * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and `headers`.
141
+ * Calling these methods invoke `$http` on the `url` template with the given `method` and `params`.
162
142
  * When the data is returned from the server then the object is an instance of the resource type and
163
143
  * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
164
144
  * operations (create, read, update, delete) on server-side data.
@@ -171,9 +151,9 @@
171
151
  });
172
152
  </pre>
173
153
  *
174
- * It's worth noting that the success callback for `get`, `query` and other method gets passed
175
- * in the response that came from the server as well as $http header getter function, so one
176
- * could rewrite the above example and get access to http headers as:
154
+ * It's worth noting that the success callback for `get`, `query` and other method gets passed
155
+ * in the response that came from the server as well as $http header getter function, so one
156
+ * could rewrite the above example and get access to http headers as:
177
157
  *
178
158
  <pre>
179
159
  var User = $resource('/user/:userId', {userId:'@id'});
@@ -296,7 +276,7 @@ angular.module('ngResource', ['ng']).
296
276
  this.defaults = defaults || {};
297
277
  var urlParams = this.urlParams = {};
298
278
  forEach(template.split(/\W/), function(param){
299
- if (param && template.match(new RegExp("[^\\\\]:" + param + "\\W"))) {
279
+ if (param && (new RegExp("(^|[^\\\\]):" + param + "\\W").test(template))) {
300
280
  urlParams[param] = true;
301
281
  }
302
282
  });
@@ -350,7 +330,6 @@ angular.module('ngResource', ['ng']).
350
330
  var ids = {};
351
331
  actionParams = extend({}, paramDefaults, actionParams);
352
332
  forEach(actionParams, function(value, key){
353
- if (isFunction(value)) { value = value(); }
354
333
  ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
355
334
  });
356
335
  return ids;
@@ -403,17 +382,11 @@ angular.module('ngResource', ['ng']).
403
382
  }
404
383
 
405
384
  var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
406
- var httpConfig = {};
407
-
408
- forEach(action, function(value, key) {
409
- if (key != 'params' && key != 'isArray' ) {
410
- httpConfig[key] = copy(value);
411
- }
412
- });
413
- httpConfig.data = data;
414
- httpConfig.url = route.url(extend({}, extractParams(data, action.params || {}), params))
415
-
416
- $http(httpConfig).then(function(response) {
385
+ $http({
386
+ method: action.method,
387
+ url: route.url(extend({}, extractParams(data, action.params || {}), params)),
388
+ data: data
389
+ }).then(function(response) {
417
390
  var data = response.data;
418
391
 
419
392
  if (data) {
@@ -0,0 +1,556 @@
1
+ /**
2
+ * @license AngularJS v1.1.3
3
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {
7
+ 'use strict';
8
+
9
+ /**
10
+ * @ngdoc overview
11
+ * @name ngSanitize
12
+ * @description
13
+ */
14
+
15
+ /*
16
+ * HTML Parser By Misko Hevery (misko@hevery.com)
17
+ * based on: HTML Parser By John Resig (ejohn.org)
18
+ * Original code by Erik Arvidsson, Mozilla Public License
19
+ * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
20
+ *
21
+ * // Use like so:
22
+ * htmlParser(htmlString, {
23
+ * start: function(tag, attrs, unary) {},
24
+ * end: function(tag) {},
25
+ * chars: function(text) {},
26
+ * comment: function(text) {}
27
+ * });
28
+ *
29
+ */
30
+
31
+
32
+ /**
33
+ * @ngdoc service
34
+ * @name ngSanitize.$sanitize
35
+ * @function
36
+ *
37
+ * @description
38
+ * The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
39
+ * then serialized back to properly escaped html string. This means that no unsafe input can make
40
+ * it into the returned string, however, since our parser is more strict than a typical browser
41
+ * parser, it's possible that some obscure input, which would be recognized as valid HTML by a
42
+ * browser, won't make it through the sanitizer.
43
+ *
44
+ * @param {string} html Html input.
45
+ * @returns {string} Sanitized html.
46
+ *
47
+ * @example
48
+ <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>
115
+ </doc:example>
116
+ */
117
+ var $sanitize = function(html) {
118
+ var buf = [];
119
+ htmlParser(html, htmlSanitizeWriter(buf));
120
+ return buf.join('');
121
+ };
122
+
123
+
124
+ // Regular Expressions for parsing tags and attributes
125
+ var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
126
+ END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/,
127
+ ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
128
+ BEGIN_TAG_REGEXP = /^</,
129
+ BEGING_END_TAGE_REGEXP = /^<\s*\//,
130
+ COMMENT_REGEXP = /<!--(.*?)-->/g,
131
+ CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
132
+ URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/,
133
+ NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
134
+
135
+
136
+ // Good source of info about elements and attributes
137
+ // http://dev.w3.org/html5/spec/Overview.html#semantics
138
+ // http://simon.html5.org/html-elements
139
+
140
+ // Safe Void Elements - HTML5
141
+ // http://dev.w3.org/html5/spec/Overview.html#void-elements
142
+ var voidElements = makeMap("area,br,col,hr,img,wbr");
143
+
144
+ // Elements that you can, intentionally, leave open (and which close themselves)
145
+ // http://dev.w3.org/html5/spec/Overview.html#optional-tags
146
+ var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
147
+ optionalEndTagInlineElements = makeMap("rp,rt"),
148
+ optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements);
149
+
150
+ // 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"));
154
+
155
+ // 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"));
159
+
160
+
161
+ // Special Elements (can contain anything)
162
+ var specialElements = makeMap("script,style");
163
+
164
+ var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements);
165
+
166
+ //Attributes that have href and hence need to be sanitized
167
+ var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap");
168
+ var validAttrs = angular.extend({}, uriAttrs, makeMap(
169
+ 'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
170
+ 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
171
+ 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
172
+ 'scope,scrolling,shape,span,start,summary,target,title,type,'+
173
+ 'valign,value,vspace,width'));
174
+
175
+ function makeMap(str) {
176
+ var obj = {}, items = str.split(','), i;
177
+ for (i = 0; i < items.length; i++) obj[items[i]] = true;
178
+ return obj;
179
+ }
180
+
181
+
182
+ /**
183
+ * @example
184
+ * htmlParser(htmlString, {
185
+ * start: function(tag, attrs, unary) {},
186
+ * end: function(tag) {},
187
+ * chars: function(text) {},
188
+ * comment: function(text) {}
189
+ * });
190
+ *
191
+ * @param {string} html string
192
+ * @param {object} handler
193
+ */
194
+ function htmlParser( html, handler ) {
195
+ var index, chars, match, stack = [], last = html;
196
+ stack.last = function() { return stack[ stack.length - 1 ]; };
197
+
198
+ while ( html ) {
199
+ chars = true;
200
+
201
+ // Make sure we're not in a script or style element
202
+ if ( !stack.last() || !specialElements[ stack.last() ] ) {
203
+
204
+ // Comment
205
+ if ( html.indexOf("<!--") === 0 ) {
206
+ index = html.indexOf("-->");
207
+
208
+ if ( index >= 0 ) {
209
+ if (handler.comment) handler.comment( html.substring( 4, index ) );
210
+ html = html.substring( index + 3 );
211
+ chars = false;
212
+ }
213
+
214
+ // end tag
215
+ } else if ( BEGING_END_TAGE_REGEXP.test(html) ) {
216
+ match = html.match( END_TAG_REGEXP );
217
+
218
+ if ( match ) {
219
+ html = html.substring( match[0].length );
220
+ match[0].replace( END_TAG_REGEXP, parseEndTag );
221
+ chars = false;
222
+ }
223
+
224
+ // start tag
225
+ } else if ( BEGIN_TAG_REGEXP.test(html) ) {
226
+ match = html.match( START_TAG_REGEXP );
227
+
228
+ if ( match ) {
229
+ html = html.substring( match[0].length );
230
+ match[0].replace( START_TAG_REGEXP, parseStartTag );
231
+ chars = false;
232
+ }
233
+ }
234
+
235
+ if ( chars ) {
236
+ index = html.indexOf("<");
237
+
238
+ var text = index < 0 ? html : html.substring( 0, index );
239
+ html = index < 0 ? "" : html.substring( index );
240
+
241
+ if (handler.chars) handler.chars( decodeEntities(text) );
242
+ }
243
+
244
+ } 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");
249
+
250
+ if (handler.chars) handler.chars( decodeEntities(text) );
251
+
252
+ return "";
253
+ });
254
+
255
+ parseEndTag( "", stack.last() );
256
+ }
257
+
258
+ if ( html == last ) {
259
+ throw "Parse Error: " + html;
260
+ }
261
+ last = html;
262
+ }
263
+
264
+ // Clean up any remaining tags
265
+ parseEndTag();
266
+
267
+ function parseStartTag( tag, tagName, rest, unary ) {
268
+ tagName = angular.lowercase(tagName);
269
+ if ( blockElements[ tagName ] ) {
270
+ while ( stack.last() && inlineElements[ stack.last() ] ) {
271
+ parseEndTag( "", stack.last() );
272
+ }
273
+ }
274
+
275
+ if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) {
276
+ parseEndTag( "", tagName );
277
+ }
278
+
279
+ unary = voidElements[ tagName ] || !!unary;
280
+
281
+ if ( !unary )
282
+ stack.push( tagName );
283
+
284
+ var attrs = {};
285
+
286
+ rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
287
+ var value = doubleQuotedValue
288
+ || singleQoutedValue
289
+ || unqoutedValue
290
+ || '';
291
+
292
+ attrs[name] = decodeEntities(value);
293
+ });
294
+ if (handler.start) handler.start( tagName, attrs, unary );
295
+ }
296
+
297
+ function parseEndTag( tag, tagName ) {
298
+ var pos = 0, i;
299
+ tagName = angular.lowercase(tagName);
300
+ if ( tagName )
301
+ // Find the closest opened tag of the same type
302
+ for ( pos = stack.length - 1; pos >= 0; pos-- )
303
+ if ( stack[ pos ] == tagName )
304
+ break;
305
+
306
+ if ( pos >= 0 ) {
307
+ // Close all the open elements, up the stack
308
+ for ( i = stack.length - 1; i >= pos; i-- )
309
+ if (handler.end) handler.end( stack[ i ] );
310
+
311
+ // Remove the open elements from the stack
312
+ stack.length = pos;
313
+ }
314
+ }
315
+ }
316
+
317
+ /**
318
+ * decodes all entities into regular string
319
+ * @param value
320
+ * @returns {string} A string with decoded entities.
321
+ */
322
+ var hiddenPre=document.createElement("pre");
323
+ function decodeEntities(value) {
324
+ hiddenPre.innerHTML=value.replace(/</g,"&lt;");
325
+ return hiddenPre.innerText || hiddenPre.textContent || '';
326
+ }
327
+
328
+ /**
329
+ * Escapes all potentially dangerous characters, so that the
330
+ * resulting string can be safely inserted into attribute or
331
+ * element text.
332
+ * @param value
333
+ * @returns escaped text
334
+ */
335
+ function encodeEntities(value) {
336
+ return value.
337
+ replace(/&/g, '&amp;').
338
+ replace(NON_ALPHANUMERIC_REGEXP, function(value){
339
+ return '&#' + value.charCodeAt(0) + ';';
340
+ }).
341
+ replace(/</g, '&lt;').
342
+ replace(/>/g, '&gt;');
343
+ }
344
+
345
+ /**
346
+ * create an HTML/XML writer which writes to buffer
347
+ * @param {Array} buf use buf.jain('') to get out sanitized html string
348
+ * @returns {object} in the form of {
349
+ * start: function(tag, attrs, unary) {},
350
+ * end: function(tag) {},
351
+ * chars: function(text) {},
352
+ * comment: function(text) {}
353
+ * }
354
+ */
355
+ function htmlSanitizeWriter(buf){
356
+ var ignore = false;
357
+ var out = angular.bind(buf, buf.push);
358
+ return {
359
+ start: function(tag, attrs, unary){
360
+ tag = angular.lowercase(tag);
361
+ if (!ignore && specialElements[tag]) {
362
+ ignore = tag;
363
+ }
364
+ if (!ignore && validElements[tag] == true) {
365
+ out('<');
366
+ out(tag);
367
+ angular.forEach(attrs, function(value, key){
368
+ var lkey=angular.lowercase(key);
369
+ if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
370
+ out(' ');
371
+ out(key);
372
+ out('="');
373
+ out(encodeEntities(value));
374
+ out('"');
375
+ }
376
+ });
377
+ out(unary ? '/>' : '>');
378
+ }
379
+ },
380
+ end: function(tag){
381
+ tag = angular.lowercase(tag);
382
+ if (!ignore && validElements[tag] == true) {
383
+ out('</');
384
+ out(tag);
385
+ out('>');
386
+ }
387
+ if (tag == ignore) {
388
+ ignore = false;
389
+ }
390
+ },
391
+ chars: function(chars){
392
+ if (!ignore) {
393
+ out(encodeEntities(chars));
394
+ }
395
+ }
396
+ };
397
+ }
398
+
399
+
400
+ // define ngSanitize module and register $sanitize service
401
+ angular.module('ngSanitize', []).value('$sanitize', $sanitize);
402
+
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
+ }]);
425
+ /**
426
+ * @ngdoc filter
427
+ * @name ngSanitize.filter:linky
428
+ * @function
429
+ *
430
+ * @description
431
+ * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
432
+ * plain email address links.
433
+ *
434
+ * @param {string} text Input text.
435
+ * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
436
+ * @returns {string} Html-linkified text.
437
+ *
438
+ * @usage
439
+ <span ng-bind-html="linky_expression | linky"></span>
440
+ *
441
+ * @example
442
+ <doc:example module="ngSanitize">
443
+ <doc:source>
444
+ <script>
445
+ function Ctrl($scope) {
446
+ $scope.snippet =
447
+ 'Pretty text with some links:\n'+
448
+ 'http://angularjs.org/,\n'+
449
+ 'mailto:us@somewhere.org,\n'+
450
+ 'another@somewhere.org,\n'+
451
+ 'and one more: ftp://127.0.0.1/.';
452
+ $scope.snippetWithTarget = 'http://angularjs.org/';
453
+ }
454
+ </script>
455
+ <div ng-controller="Ctrl">
456
+ Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
457
+ <table>
458
+ <tr>
459
+ <td>Filter</td>
460
+ <td>Source</td>
461
+ <td>Rendered</td>
462
+ </tr>
463
+ <tr id="linky-filter">
464
+ <td>linky filter</td>
465
+ <td>
466
+ <pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
467
+ </td>
468
+ <td>
469
+ <div ng-bind-html="snippet | linky"></div>
470
+ </td>
471
+ </tr>
472
+ <tr id="linky-target">
473
+ <td>linky target</td>
474
+ <td>
475
+ <pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
476
+ </td>
477
+ <td>
478
+ <div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
479
+ </td>
480
+ </tr>
481
+ <tr id="escaped-html">
482
+ <td>no filter</td>
483
+ <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
484
+ <td><div ng-bind="snippet"></div></td>
485
+ </tr>
486
+ </table>
487
+ </doc:source>
488
+ <doc:scenario>
489
+ it('should linkify the snippet with urls', function() {
490
+ expect(using('#linky-filter').binding('snippet | linky')).
491
+ toBe('Pretty text with some links:&#10;' +
492
+ '<a href="http://angularjs.org/">http://angularjs.org/</a>,&#10;' +
493
+ '<a href="mailto:us@somewhere.org">us@somewhere.org</a>,&#10;' +
494
+ '<a href="mailto:another@somewhere.org">another@somewhere.org</a>,&#10;' +
495
+ 'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
496
+ });
497
+
498
+ it ('should not linkify snippet without the linky filter', function() {
499
+ expect(using('#escaped-html').binding('snippet')).
500
+ toBe("Pretty text with some links:\n" +
501
+ "http://angularjs.org/,\n" +
502
+ "mailto:us@somewhere.org,\n" +
503
+ "another@somewhere.org,\n" +
504
+ "and one more: ftp://127.0.0.1/.");
505
+ });
506
+
507
+ it('should update', function() {
508
+ input('snippet').enter('new http://link.');
509
+ expect(using('#linky-filter').binding('snippet | linky')).
510
+ toBe('new <a href="http://link">http://link</a>.');
511
+ expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
512
+ });
513
+
514
+ it('should work with the target property', function() {
515
+ expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
516
+ toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
517
+ });
518
+ </doc:scenario>
519
+ </doc:example>
520
+ */
521
+ angular.module('ngSanitize').filter('linky', function() {
522
+ var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
523
+ MAILTO_REGEXP = /^mailto:/;
524
+
525
+ return function(text, target) {
526
+ if (!text) return text;
527
+ var match;
528
+ var raw = text;
529
+ var html = [];
530
+ // TODO(vojta): use $sanitize instead
531
+ var writer = htmlSanitizeWriter(html);
532
+ var url;
533
+ var i;
534
+ var properties = {};
535
+ if (angular.isDefined(target)) {
536
+ properties.target = target;
537
+ }
538
+ while ((match = raw.match(LINKY_URL_REGEXP))) {
539
+ // We can not end in these as they are sometimes found at the end of the sentence
540
+ url = match[0];
541
+ // if we did not match ftp/http/mailto then assume mailto
542
+ if (match[2] == match[3]) url = 'mailto:' + url;
543
+ i = match.index;
544
+ writer.chars(raw.substr(0, i));
545
+ properties.href = url;
546
+ writer.start('a', properties);
547
+ writer.chars(match[0].replace(MAILTO_REGEXP, ''));
548
+ writer.end('a');
549
+ raw = raw.substring(i + match[0].length);
550
+ }
551
+ writer.chars(raw);
552
+ return html.join('');
553
+ };
554
+ });
555
+
556
+ })(window, window.angular);