EN
JavaScript - interpret URLs and emails in text replacing them with HTML link elements
5 points
In this short article, we would liek to show how to interpret URLs and emails replacing them with links using JavaScript.

Presented solution escapes HTML special characters making produced HTML safe.
xxxxxxxxxx
1
2
<html>
3
<head>
4
<style>
5
6
.link { color: #5f5fd0; }
7
.email { color: #468b36; }
8
9
</style>
10
</head>
11
<body>
12
<div id="output"></div>
13
<script>
14
15
function RuleProcessor(expression, replacer) {
16
if (expression.sticky === false) {
17
throw new Error('Used regular expression does not have sticky flag.');
18
}
19
this.execute = function(index, text) {
20
expression.lastIndex = index;
21
return expression.exec(text);
22
};
23
this.process = function(match) {
24
return replacer.apply(null, match);
25
};
26
}
27
28
function TextProcessor(escape, rules) {
29
if (escape == null) {
30
escape = '\\';
31
} else {
32
if (escape.length !== 1) {
33
throw new Error('Escape parameter must be single character.');
34
}
35
}
36
this.processText = function(text) {
37
var index = 0;
38
var position = 0;
39
var omitting = false;
40
var result = '';
41
loop:
42
while (index < text.length) {
43
var character = text[index];
44
if (character === escape) {
45
if (position < index) {
46
omitting = true;
47
result += text.substring(position, index);
48
position = index;
49
} else {
50
if (omitting) {
51
omitting = false;
52
result += escape;
53
} else {
54
omitting = true;
55
}
56
}
57
position += 1;
58
index += 1;
59
} else {
60
for (var i = 0; i < rules.length; ++i) {
61
var rule = rules[i];
62
var match = rule.execute(index, text);
63
if (match) {
64
var entry = match[0];
65
if (omitting) {
66
omitting = false;
67
result += entry;
68
} else {
69
if (position < index) {
70
result += text.substring(position, index);
71
position = index;
72
}
73
result += rule.process(match);
74
}
75
position += entry.length;
76
index += entry.length;
77
continue loop;
78
}
79
}
80
index += 1;
81
}
82
}
83
if (position < index) {
84
result += text.substring(position, index);
85
}
86
return result;
87
};
88
}
89
90
91
92
// Helper logic:
93
94
function escapeHtml(html) {
95
var result = '';
96
var escape = '';
97
var helper = 0;
98
var index = 0;
99
for (; index < html.length; ++index) {
100
switch (html.charCodeAt(index)) {
101
case 34: escape = '"'; break; // " // "
102
case 38: escape = '&'; break; // & // &
103
case 39: escape = '''; break; // '
104
case 60: escape = '<'; break; // < // <
105
case 62: escape = '>'; break; // > // >
106
default: continue;
107
}
108
if (helper < index) {
109
result += html.substring(helper, index);
110
}
111
result += escape;
112
helper = index + 1;
113
}
114
if (helper < index) {
115
result += html.substring(helper, index);
116
}
117
return result;
118
}
119
120
// Hint: link and email patterns base on improved ANGUALR 1.5.8 patterns.
121
//
122
var ESCAPE_REGEX = /[&<>]/y;
123
var LINK_REGEX = /(?:\b[a-z][a-z\d.+-]*:\/{2,3}|\/\/(?![a-z][a-z\d.+-]*:\/{2,3})|\bwww\.)(?:[^:@\s]+(?::[^@\s]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#\s]*)?(?:\?[^#\s]*)?(?:#[^\s]*)?/y;
124
var EMAIL_REGEX = /(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+(?:\.[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*/y; // 64 + @ + 255 = 320
125
126
function interpretEscapes(match) {
127
switch (match) {
128
case '&': return '&';
129
case '<': return '<';
130
case '>': return '>';
131
default: throw new Error('Not supported esacape character.');
132
}
133
}
134
135
function interpretLink(match) {
136
var href = escapeHtml(match);
137
return '<a class="link" href="' + href + '">' + href + '</a>';
138
}
139
140
function interpretEmail(match) {
141
var href = escapeHtml(match);
142
return '<a class="email" href="mailto:' + href + '">' + href + '</a>';
143
}
144
145
var rules = [
146
new RuleProcessor(ESCAPE_REGEX, interpretEscapes),
147
new RuleProcessor(LINK_REGEX, interpretLink),
148
new RuleProcessor(EMAIL_REGEX, interpretEmail)
149
];
150
151
var processor = new TextProcessor('\\', rules); // '\\' is used as escape character for detected rules in the processed text
152
153
154
155
// Usage example:
156
157
var text = 'This is my email: john@e-mail.com, This is my website: https://john.com';
158
var html = processor.processText(text);
159
160
var output = document.querySelector('#output');
161
162
output.innerHTML = html;
163
164
</script>
165
</body>
166
</html>