This approach matches the usernames and uses the RegExp capability of remembering the last match postion. So subsequent exec()
call will match subsequent usernames. The match object returned by exec()
is an array containing the matched texts and an index
attribute pointing to the match position.
The code copies the text from the last position (initially position 0) to the matched username, then appends the "wrapped" username and starts over until there are no more username matches.
The code is commented line by line, describing its function:
function wrapUsernames(str) {
// match "usernames" ("at" followed by text, preceeded by non-word character or line-start),
// the RegExp keeps its state
var re = /(?:([^\w]|^)(@[a-z]+))/g,
// store a RegExp match object
match,
// store the text fragments
results = [],
// remember last index for substring copy
lastIndex = 0;
// match and store result, returns null if it does no longer match
while (match = re.exec(str)) {
// copy text from last match / start to username
// (only if not matched at index 0 to prevent empty string)
if (match.index != 0) {
results.push( str.substring(lastIndex, match.index) );
}
if (match[1].length >= 1) {
// it also matched the char before the username, append it
results.push(match[1]);
}
// copy matched username and wrap in a tag
results.push('<a href="...">' + match[2] + '</a>');
// update the index to start copy at the next position
lastIndex = match.index + match[0].length;
}
// append the remaining string (only if it wouldn't be an empty string)
if (lastIndex < str.length) {
results.push(str.substring(lastIndex));
}
return results;
}
This should also match usernames prefixed with other characters than space:
> wrapUsernames("(@steph) the email you requested is test@test.com for user (@test)")
< Array [ "(", "<a href="...">@steph</a>", ") the email you requested is test@test.com for user ", "(", "<a href="...">@test</a>", ")" ]
> wrapUsernames("@steph the email you requested is test@test.com for user (@test)")
< Array [ "<a href="...">@steph</a>", " the email you requested is test@test.com for user ", "(", "<a href="...">@test</a>", ")" ]
> wrapUsernames("hi, @steph the email you requested is test@test.com for user @test")
< Array [ "hi,", " ", "<a href="...">@steph</a>", " the email you requested is test@test.com for user ", " ", "<a href="...">@test</a>" ]