Background
The basics of this project have already been discussed here.
I've improved the following things:
Sᴀᴍ Onᴇᴌᴀ mentioned that I shouldn't use multiple elements with the same id. I corrected that and use classes instead. I also removed units from style.css when the value is 0. Furthermore I removed !important.
Sᴀᴍ Onᴇᴌᴀ also pointed out, I did not follow always follow the common style-guide. I tried to improve it for this follow-up question.
Mast mentioned that it is not possible to have html-tags in for example comments, without destroying the formatting.
I've solved this problem by replacing & and <:
text = text.replace(new RegExp("&", "g"), "&");
text = text.replace(new RegExp("<", "g"), "<");
FeRD noted, that inputs like System.out.println("Hi \"friend\"."); cause problems because of the escaped quotes. For this problem i used the solution provided here.
Another problem was to highlight private Account my_import_export;. This problem also was solved by changing the regex a bit.
Charlie Harding mentioned in the comments that something like serif (x), if (x) y(); or if(x) y() is not possible. This problem was also solved.
Olivier Grégoire noted, that "\"" and "\\\"" are not highlighted correctly. I solved this problem also.
Roland Illig suggested to properly parse the java-code. I did not implement this suggestion, because I will first try how far I can come by just using regex.
The code
//Add listener to highlight-button
document.getElementById('highlightButton').addEventListener('click', highlight);
//Add possibility to use tabs in inputArea
var area = document.getElementById('Input');
area.onkeydown = function(e) {
if(e.keyCode == 9){
e.preventDefault();
this.value = this.value.substring(0,this.selectionStart) + '\t' + this.value.substring(this.selectionEnd);
}
}
//===============================
//Highlighting-functionality
//===============================
var keywordsColor = '#0033cc';
var controlKeywordsColor = '#009933';
var typesKeywordsColor = '#3399ff';
var stringColor = '#ff3300';
var importColor = '#0033cc';
var commentColor = 'gray';
var text;
var keywords = ['abstract', 'assert', 'const', 'extends', 'false', 'final',
'implements', 'import', 'instanceof', 'interface', 'native', 'new', 'null', 'package',
'private', 'protected', 'public', 'return', 'static', 'strictfp', 'super', 'synchronized',
'System', 'this', 'throw', 'throws', 'transient', 'true', 'volatile'];
var controlKeywords = ['break', 'case', 'catch', 'continue', 'default', 'do', 'else',
'finally', 'for', 'goto', 'if', 'switch', 'try', 'while'];
var typesKeywords = ['boolean', 'byte', 'char', 'double', 'enum', 'float', 'int',
'long', 'short', 'String', 'void'];
var otherKeywords = [];
function highlight() {
text = document.getElementById('Input').value;
text = text.replace(new RegExp('&', 'g'), '&');
text = text.replace(new RegExp('<', 'g'), '<');
findOtherKeywords();
highlightKeywords();
highlightStrings();
highlightImports();
highlightSingleLineComments();
highlightMultiLineComments();
var arr = text.split("\n");
for(var i = 0; i < arr.length; i++) {
arr[i] = '<code>' + arr[i] + '</code>';
}
text = arr.join('\n');
addStyles();
var check = document.getElementById('checkbox2');
if(check.checked) {
text = '<div style = \'background: LightGray; font-family: monospace;\'>' + text + '</div>';
}
document.getElementById('Output').value = text;
document.getElementById('outputArea').innerHTML = text;
}
function findOtherKeywords() {
var arr = text.split('\n');
for(var i = 0; i < arr.length; i++) {
if(arr[i].startsWith('import')) {
arr[i] = arr[i].substring(7, arr[i].indexOf(';'));
var array = arr[i].split('\.');
for(var j = 0; j < array.length; j++) {
typesKeywords.push(array[j]);
}
}
}
}
function highlightKeywords() {
var i;
for (i = 0; i < typesKeywords.length; i++) {
var x = new RegExp(`\\b${typesKeywords[i]}\\b`, 'g');
var y = '<span class=\'typesKeywordsClass\'>' + typesKeywords[i] + '</span>';
text = text.replace(x, y);
}
for (i = 0; i < keywords.length; i++) {
var x = new RegExp(`\\b${keywords[i]}\\b`, 'g');
var y = '<span class=\'keywordsClass\'>' + keywords[i] + '</span>';
text = text.replace(x, y);
}
for (i = 0; i < controlKeywords.length; i++) {
var x = new RegExp(`\\b${controlKeywords[i]}\\b`, 'g');
var y = '<span class=\'controlKeywordsClass\'>' + controlKeywords[i] + '</span>';
text = text.replace(x, y);
}
var x = new RegExp(`\\b(class) `, 'g');
var y = '<span class=\'keywordsClass\'>' + 'class ' + '</span>';
text = text.replace(x, y);
}
function highlightStrings() {
text = text.replace(/"([^"\\]*(\\.[^"\\]*)*)"/,
'<span class="str" style="color:' + stringColor + '; font-weight:bold;">'
+ '"$1"' + '</span>');
}
function highlightImports() {
text = text.replace(/^import(.*?);/g,
'<span class="str" style="color:' + importColor + '; font-weight:bold;">'
+ 'import$1;' + '</span>');
}
function highlightSingleLineComments() {
text = text.replace(/\/\/(.*)/g,
'<span class="comment" style="color:' + commentColor + '; font-weight:bold;">'
+ '//$1' + '</span>');
}
function highlightMultiLineComments() {
text = text.replace(/[ ]*\/\*([\s\S]*?)\*\//g, (...match) => {
let groups = match.pop();
var str = groups + '';
var arr = str.split('\n');
var i = 0;
var j = 0;
var test = false;
for(i = 0; i < arr.length; i++) {
if(arr[i].includes('*/')) {
var arr2 = arr[i].split("*/");
arr2[0] = '<span class="comment" style="color:' + commentColor + '; font-weight:bold;">'
+ arr2[0] + '</span>';
arr[i] = arr2.join('<span class="comment" style="color:' + commentColor + '; font-weight:bold;">'
+ '*/' + '</span>')
break;
}
if(arr[i].includes('/*') || test) {
if(!test) {
j = i;
}
arr[i] = '<span class="comment" style="color:' + commentColor + '; font-weight:bold;">'
+ arr[i] + '</span>';
test = true;
}
}
var result = arr.slice(j, i + 1).join('\n');
return result;
});
}
function addStyles() {
var style = '<!-- Style begins here (copy to head) -->\n'
+ '<style> .keywordsClass {color:' + keywordsColor + ';font-weight:bold;}</style>'
+ '<style> .controlKeywordsClass {color:' + controlKeywordsColor + ';font-weight:bold;}</style>'
+ '<style> .typesKeywordsClass {color:' + typesKeywordsColor + ';font-weight:bold;}</style>'
+ '<style>code .comment span {color:' + commentColor + ';}</style>'
+ '<style>code .str span {color:' + stringColor + ';}</style>';
if(document.getElementById('checkbox1').checked) {
style = style
+ '<style> pre{counter-reset: line;}</style>'
+ '<style> code{counter-increment: line;}</style>'
+ '<style> code::before {content: counter(line); display: inline-block; width: 2.5em; border-right: 2px solid gray; padding: 0.2em; margin-right: 0.5em; color: gray;}</style>';
}
style = style
+ '\n<!-- Style ends here -->\n';
text = style
+ '\n<!-- Code begins here -->\n'
+ '\n<pre>\n'
+ text
+ '\n</pre>\n<!-- Code ends here -->\n';
}
body {
tab-size: 4;
-moz-tab-size: 4;
margin: 0;
margin-top: 0;
/* Background pattern from Toptal Subtle Patterns */
background: url('../pictures/extra_clean_paper.png');
background-attachment: fixed;
height: 100%;
width: 100%;
position: absolute;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
.inner {
tab-size: 4;
-moz-tab-size: 4;
margin: 0;
margin-top: 0;
background: white;
height: 100%;
width: 80%;
display: table;
table-layout: fixed;
margin: 0 auto;
padding: 0;
position: relative;
font-family: sans-serif;
}
/* Navigation bar style */
header {
display: table-row;
background: #252628;
height: 40px;
float: right;
width: 100%;
position: relative;
display: block;
}
header ul {
background: #252628;
list-style: none;
padding: 0;
margin: 0;
position: fixed;
width: 80%;
top: 0;
z-index:9999;
}
header li {
float: left;
border-right: 2px solid LightGray;
height: 40px;
}
header a {
color: black;
display: inline-block;
font-size: large;
text-decoration: none;
padding: 4px;
padding-left: 10px;
padding-right: 10px;
width: auto;
color: LightGray;
line-height: 40px;
vertical-align: middle;
}
header li:hover {
background: #688B57;
}
header li ul {
display: none;
}
header li:hover > ul {
display:block;
list-style:none;
height: 40px;
margin-top: 40px;
background: #252628;
width: auto;
}
header ul li ul li{
height: 40px;
display: block;
float:none;
text-align: left;
border-top: 2px solid LightGray;
border-right: none;
}
.icon {
padding-right: 4px;
line-height: 40px;
vertical-align: middle;
}
/* Main */
main {
display: table-row;
width: 80%;
height: 100%;
}
/* Footer */
footer {
display: table-row;
background: #252628;
min-height: 60px;
height: auto;
width: 100%;
float: right;
bottom: 0;
position: relative;
}
footer ul {
text-align: center;
padding: 0;
overflow: hidden;
}
footer ul li {
list-style: none;
display: inline;
width: auto;
height: 60px;
padding: 40px;
line-height: 60px;
vertical-align: middle;
justify-content:center;
}
footer ul li a {
text-decoration: none;
color: LightGray;
}
/* Other */
#code {
background: LightGray;
font-family: monospace;
}
.column {
float: left;
width: 50%;
}
.caption {
text-decoration: none;
}
.icons {
width: 32px;
height: 32px;
border-radius: 50%;
text-align: right;
}
.right {
float: right;
width: 40px;
}
.rightLink {
width: 40px;
padding-left: 0;
padding-right: 0;
text-align: center;
}
h2 {
font-family: "Courier";
font-style: italic;
text-transform: uppercase;
color: #252628;
}
.list > li {
margin: 5px 0;
}
.input {
width: 80%;
border: 2px solid #252628;
padding: 20px;
background: LightGray;
}
.formText {
border: 1px solid #252628;
box-shadow: none;
margin-bottom: 30px;
resize: none;
width: 100%;
position: relative;
font-size: 12px;
height: 16px;
padding-top: 2px;
padding-bottom: 2px;
}
.formText:focus {
border:1px solid green;
}
figure {
text-align: center;
float: right;
display: block;
position: relative;
}
figure img {
display: block;
margin-bottom: 20px;
}
figcaption {
position: absolute;
bottom: 0;
}
<!DOCTYPE html>
<html lang='en'>
<!-- Head -->
<head>
<script>
window.onerror=function(msg, url, linenumber) {
alert('Error message: ' + msg + '\nURL: ' + url + '\nLine Number: ' + linenumber);
return true;
}
</script>
<meta charset='utf-8'>
<title>Highlighting</title>
<link rel='stylesheet' type='text/css' href='../css/style.css'>
</head>
<body>
<div class='inner'>
<!-- Navigation bar -->
<header>
<div class='nav'>
<ul>
<li><a href='../index.html'><img class='icon' src='../pictures/iconmonstr-home-7-24.png' alt=''/>Home</a></li>
<li><a href='tools.html'><img class='icon' src='../pictures/iconmonstr-wrench-2-24.png' alt=''/>Tools</a>
<ul>
<li><a href='highlighting.html'>Highlighting</a></li>
</ul>
</li>
<li><a href='about.html'><img class='icon' src='../pictures/iconmonstr-user-6-24.png' alt=''/>About</a></li>
<li><a href='contact.html'><img class='icon' src='../pictures/iconmonstr-email-2-24.png' alt=''/>Contact</a></li>
</ul>
</div>
</header>
<main>
<div style='margin:30px; margin-top: 50px'>
<h2>HTML syntax-highlighting for Java</h2>
<!-- Left column -->
<div class='column'>
<!-- Input Area -->
<h4>Input:</h4>
<div>
<textarea id='Input' rows='8' style='resize: none; background: LightGray; position: relative; width: 80%;'></textarea>
</div>
<br>
<form>
<input type='checkbox' id='checkbox1' name='checkbox1' value='numbers'>
<label for='checkbox1'>Include line numbers</label><br>
<input type='checkbox' id='checkbox2' name='checkbox2' value='background'>
<label for='checkbox2'>Include background-color</label>
</form>
<br><br>
<button type='button' id='highlightButton'>Highlight</button>
<!-- Output Area -->
<h4>Output:</h4>
<div>
<textarea id='Output' rows='8' style='resize: none; background: LightGray; position: relative; width: 80%;'></textarea>
</div>
</div>
<!-- Right Column -->
<div class='column'>
<h4>Preview</h4>
<div id='outputArea' style='overflow-y:auto; overflow-x:auto; height: 690px'></div>
</div>
</div>
</main>
<footer>
<ul>
<li><a href='../index.html'>Home</a></li>
<li><a href='about.html'>About</a></li>
<li><a href='license.html'>License</a></li>
<li><a href='impressum.html'>Impressum</a></li>
<li><a href='datenschutz.html'>Datenschutz</a></li>
</ul>
</footer>
</div>
<script src='../javascript/highlightSyntax.js'></script>
</body>
</html>
I've checked the syntactic validity of the javascript-code with this tool and the html/css-code with this one.
Example
If you want to test the code, you can use the following snippet:
import java.time.LocalDate;
public class Person {
//Local variable for dateOfBirth
private LocalDate dateOfBirth;
public Person(int year, int month, int day) {
//See API also: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
dateOfBirth = LocalDate.of(year, month, day);
//Keywords (e.g. int) are not highlighted in comments and strings
System.out.println("Hello (int)");
}
/*
* Getter "DateOfBirth"
*/
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
}
Question
All suggestions are welcome.