For example notes from StreetComplete often have a linked image. These links should be opened in a new tab instead of the current one.
I've been looking into this briefly but I can't find where link tags are constructed. I'm guessing there's some sort of user-input parsing library that's being used to clean any comment data, that also detects and wraps links? Maybe this is coming from the API. I think I might need to use a CSS selector and manually append a target=_blank attr for any link tags pointing away from OSM hosts (something that would move your view within the window).
Any ideas? :)
Okay, so I've gone on the assumption and written a helper function to preprocess any comments for links, adding target=_blank attribute to any which point to 'external domains'.
At the moment, I'm considering any link whose hostname does not include "osm.org" or "openstreetmap.org" as an external domain.
There are 3 things to note with this:
Here's a preview: https://www.youtube.com/watch?v=MFpImKkY9gA
Here's what I've written for it (wary of making a PR just yet):
// This is all in note_comments.js
mainEnter
.append('div')
.attr('class', 'comment-text')
.html((d) => preprocessLinkTags(d.html));
/**
* Add target="_blank" to any comment links pointing away from the site
* That is, any link with domain other than those in ignoredDomains.
*/
const preprocessLinkTags = (commentTextHtml) => {
if (typeof commentTextHtml !== 'string') return commentTextHtml;
try {
const template = document.createElement('template');
const trimmedCommentTextHtml = commentTextHtml.trim();
template.innerHTML = trimmedCommentTextHtml;
const links = Array.from(template.content.querySelectorAll('a'));
const ignoredDomains = ['openstreetmap.org', 'osm.org']; // Possibly more?
// Filter links based on their hostname not including domains in ignoredDomains
links
.filter((link) => {
const hostname = new URL(link).hostname;
// Find if any ignoredDomains are found in the hostname (e.g. "api06.dev.osm.org" will be matched)
return ignoredDomains.findIndex(d => hostname.includes(d)) === -1;
})
.forEach((link) => link.setAttribute('target', '_blank'));
return template.content.firstChild.innerHTML;
} catch (error) {
// Bail without changing if something goes wrong
return commentTextHtml;
}
};
You could use d3 to select all the a elements in the comment-text div and add the attribute to them all like so:
mainEnter
.append('div')
.attr('class', 'comment-text')
.html(d => d.html)
.selectAll('a')
.attr('rel', 'noopener')
.attr('target', '_blank');
You could use d3 to select all the
aelements in the comment-text div and add the attribute to them all like so:
Ah! I'm super unfamiliar with d3. I'll still want to filter which tags open, something like:
mainEnter
.append('div')
.attr('class', 'comment-text')
.html((d) => d.html)
.selectAll('a')
.filter(isExternalLink)
.attr('rel', 'noopener')
.attr('rel', 'nofollow')
.attr('target', '_blank');
Does this look reasonable?
To answer your question about where the links are actually generated I had a look.
Looks like the note information returned by the API is being parsed here: https://github.com/openstreetmap/iD/blob/248b29983d273aa28877c47729af936644044481/modules/services/osm.js#L328-L367
Which involves the parseComments function in the same file on L150. However, that all just seems to be parsing the json response and not doing anything with the note content. The parsed properties are all fed into the osmNote object constructor in https://github.com/openstreetmap/iD/blob/248b29983d273aa28877c47729af936644044481/modules/osm/note.js#L25-L44 which again doesn't seem to be doing anything with the contents. So I think the content must be coming in pre-linked from the API.
Ah! I'm super unfamiliar with d3. I'll still want to filter which tags open, something like:
Does this look reasonable?
Hey, yes that looks good to me! I'm not the most well versed in d3 myself, but have played with it enough to know when it can make life easier 馃槃
Hey, yes that looks good to me! I'm not the most well versed in d3 myself, but have played with it enough to know when it can make life easier 馃槃
Great, I'll give a test and see how it goes with this new format. Thanks so much for all this!
I've made a pull request (#7893) for this based on previous discussion. Solution now uses a much simpler function for determining internal links, and provided d3 functions.
Done by @JeeZeh in #7893.
Most helpful comment
I've made a pull request (#7893) for this based on previous discussion. Solution now uses a much simpler function for determining internal links, and provided d3 functions.