Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Add custom attributes to linky #12558

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 43 additions & 6 deletions src/ngSanitize/filter/linky.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
*
* @param {string} text Input text.
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
* @param {object|function(url)} [attributes] Add custom attributes to the link element.
*
* Can be one of:
*
* - `object`: A map of attributes
* - `function`: Takes the url as a parameter and returns a map of attributes
*
* If the map of attributes contains a value for `target`, it overrides the value of
* the target parameter.
*
* @returns {string} Html-linkified text.
*
* @usage
Expand All @@ -32,7 +42,7 @@
'mailto:us@somewhere.org,\n'+
'another@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.';
$scope.snippetWithTarget = 'http://angularjs.org/';
$scope.snippetWithSingleURL = 'http://angularjs.org/';
}]);
</script>
<div ng-controller="ExampleController">
Expand All @@ -55,10 +65,19 @@
<tr id="linky-target">
<td>linky target</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
<pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithSingleURL | linky:'_blank'"></div>
</td>
</tr>
<tr id="linky-custom-attributes">
<td>linky custom attributes</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel:'nofollow'}"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
<div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel:'nofollow'}"></div>
</td>
</tr>
<tr id="escaped-html">
Expand Down Expand Up @@ -95,10 +114,17 @@

it('should work with the target property', function() {
expect(element(by.id('linky-target')).
element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
});

it('should optionally add custom attributes', function() {
expect(element(by.id('linky-custom-attributes')).
element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');
});
</file>
</example>
*/
Expand All @@ -107,7 +133,7 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
MAILTO_REGEXP = /^mailto:/i;

return function(text, target) {
return function(text, target, attributes) {
if (!text) return text;
var match;
var raw = text;
Expand Down Expand Up @@ -137,8 +163,19 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
}

function addLink(url, text) {
var key;
html.push('<a ');
if (angular.isDefined(target)) {
if (angular.isFunction(attributes)) {
attributes = attributes(url);
}
if (angular.isObject(attributes)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this code duplication be prevented using something like this?

var key;
var computedAttributes = {};
if (angular.isFunction(attributes)) {
  attributes = attributes(url);
}
if (angular.isObject(attributes)) {
  computedAttributes = attributes;
  for (key in computedAttributes) {
    html.push(key + '="' + computedAttributes[key] + '" ');
  }
}

This would also check if the return value from the attributes function is an object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's a good idea. At first I didn't want to reassign the attributes variable, but the extra check for the return value of attributes as a function, is a compelling case for doing it like this.

for (key in attributes) {
html.push(key + '="' + attributes[key] + '" ');
}
} else {
attributes = {};
}
if (angular.isDefined(target) && !('target' in attributes)) {
html.push('target="',
target,
'" ');
Expand Down
21 changes: 21 additions & 0 deletions test/ngSanitize/filter/linkySpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,25 @@ describe('linky', function() {
toBeOneOf('<a target="someNamedIFrame" href="http://example.com">http://example.com</a>',
'<a href="http://example.com" target="someNamedIFrame">http://example.com</a>');
});

it('should optionally add custom attributes', function() {
expect(linky("http://example.com", "_self", {rel: "nofollow"})).
toEqual('<a rel="nofollow" target="_self" href="http://example.com">http://example.com</a>');
});

it('should override target parameter with custom attributes', function() {
expect(linky("http://example.com", "_self", {target: "_blank"})).
toEqual('<a target="_blank" href="http://example.com">http://example.com</a>');
});

it('should optionally add custom attributes from function', function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should also be an exra test that ensures the url is actually passed to the attr function

expect(linky("http://example.com", "_self", function(url) {return {"class": "blue"};})).
toEqual('<a class="blue" target="_self" href="http://example.com">http://example.com</a>');
});

it('should pass url as parameter to custom attribute function', function() {
var linkParameters = jasmine.createSpy('linkParameters').andReturn({"class": "blue"});
linky("http://example.com", "_self", linkParameters);
expect(linkParameters).toHaveBeenCalledWith('http://example.com');
});
});