Skip to content

Commit 120c321

Browse files
author
WebCoder49
committed
[INCOMPLETE] Work more on special chars
1 parent 8ab2932 commit 120c321

File tree

2 files changed

+89
-28
lines changed

2 files changed

+89
-28
lines changed

plugins/special-chars.css

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
* Files: special-chars.js, special-chars.css
44
*/
55

6+
/* Main styling */
7+
8+
.code-input_special-char_container { /* pre element */
9+
line-height: 4;
10+
font-size: 20px;
11+
}
12+
613
.code-input_special-char {
714
display: inline-block;
815
position: relative;
@@ -14,43 +21,70 @@
1421

1522
text-decoration: none;
1623
text-shadow: none;
24+
25+
vertical-align: middle;
1726
}
1827

19-
.code-input_special-char::before {
20-
content: attr(data-top);
28+
.code-input_special-char::before, .code-input_special-char::after {
29+
white-space: pre;
2130
color: inherit;
2231
background-color: inherit;
2332

2433
font-size: calc(100% - 2px);
2534
position: absolute;
26-
line-height: normal;
27-
top: -1em;
35+
line-height: 1;
36+
37+
height: 2em;
38+
width: calc(100% - 2px);
39+
text-align: center;
40+
}
41+
42+
/* Default - Two bytes - 4 hex chars */
43+
44+
.code-input_special-char::before {
45+
content: attr(data-hex0) "\A" attr(data-hex1);
46+
47+
top: -1.5em;
2848
left: 0;
2949
padding: 0;
3050
padding-bottom: 2px;
3151
border-top: 1px solid;
3252
border-left: 1px solid;
3353
border-right: 1px solid;
34-
35-
height: 1.5em;
36-
width: calc(100% - 2px);
3754
}
38-
.code-input_special-char::after {
39-
content: attr(data-bottom);
40-
color: inherit;
41-
background-color: inherit;
4255

43-
font-size: calc(100% - 2px);
44-
position: absolute;
45-
line-height: normal;
46-
bottom: -1em;
56+
.code-input_special-char::after {
57+
content: attr(data-hex2) "\A" attr(data-hex3);
58+
59+
bottom: -1.5em;
4760
left: 0;
4861
padding: 0;
4962
padding-top: 2px;
5063
border-bottom: 1px solid;
5164
border-left: 1px solid;
5265
border-right: 1px solid;
66+
}
67+
68+
/* Zero-width - style so takes up no space */
69+
70+
.code-input_special-char_zero-width {
71+
z-index: 1;
72+
width: 1em;
73+
margin-left: -0.5em;
74+
margin-right: -0.5em;
75+
position: relative;
76+
77+
opacity: 0.75;
78+
}
5379

80+
/* One byte - 2 hex chars */
81+
.code-input_special-char_one-byte::before {
5482
height: 1.5em;
55-
width: calc(100% - 2px);
83+
top: -1em;
84+
content: attr(data-hex2);
85+
}
86+
.code-input_special-char_one-byte::after {
87+
height: 1.5em;
88+
bottom: -1em;
89+
content: attr(data-hex3);
5690
}

plugins/special-chars.js

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
* Render special characters as a symbol with their hex code.
33
* Files: special-chars.js, special-chars.css
44
*/
5+
6+
// INCOMPLETE: TODO Optimise regex - compile at start; make so won't change contents of HTML code
7+
58
codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
69
specialCharRegExp;
710

@@ -12,12 +15,14 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
1215
/**
1316
* Create a special characters plugin instance
1417
* @param {RegExp} specialCharRegExp The regular expression which matches special characters
18+
* @param {Boolean} colorInSpecialChars Whether or not to give special characters custom background colors based on their hex code
1519
*/
16-
constructor(specialCharRegExp = /(?!\n)(?!\t)[\u{0000}-\u{001F}]|[\u{0080}-\u{FFFF}]/ug) { // By default, covers many non-renderable ASCII characters
20+
constructor(specialCharRegExp = /(?!\n)(?!\t)[\u{0000}-\u{001F}]|[\u{007F}-\u{FFFF}]/ug, colorInSpecialChars = false) { // By default, covers many non-renderable ASCII characters
1721
super();
1822
this.specialCharRegExp = specialCharRegExp;
19-
this.cachedColors = {};
23+
this.colorInSpecialChars = colorInSpecialChars;
2024

25+
this.cachedColors = {};
2126
this.cachedWidths = {};
2227

2328
let canvas = document.createElement("canvas");
@@ -30,26 +35,42 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
3035
this.canvasContext.font = "20px 'Consolas'"; // TODO: Make dynamic
3136
}
3237

38+
/* Runs before elements are added into a `code-input`; Params: codeInput element) */
39+
beforeElementsAdded(codeInput) {
40+
codeInput.classList.add("code-input_special-char_container");
41+
}
42+
3343
/* Runs after code is highlighted; Params: codeInput element) */
3444
afterHighlight(codeInput) {
3545
let result_element = codeInput.querySelector("pre code");
3646
result_element.innerHTML = result_element.innerHTML.replaceAll(this.specialCharRegExp, this.specialCharReplacer.bind(this));
3747
}
38-
3948
specialCharReplacer(match_char, _match_char, index, whole_string, groups) {
4049
let hex_code = match_char.codePointAt(0);
4150

42-
let colors = this.getCharacterColor(hex_code);
51+
let colors;
52+
if(this.colorInSpecialChars) colors = this.getCharacterColor(hex_code);
4353

4454
hex_code = hex_code.toString(16);
45-
hex_code = ("00" + hex_code).substring(hex_code.length); // So 2 chars with leading 0
55+
hex_code = ("0000" + hex_code).substring(hex_code.length); // So 2 chars with leading 0
4656
hex_code = hex_code.toUpperCase();
4757

4858
let char_width = this.getCharacterWidth(match_char);
49-
console.log(hex_code, char_width);
5059

51-
// let background_color = hashed_last_hex_char + hashed_last_hex_char + hex_code.substr(0, 1) + hex_code.substr(0, 1) + hex_code.substr(1, 1) + hex_code.substr(1, 1) // So good range of colours
52-
return `<span class='code-input_special-char' data-top='${hex_code[0]}' data-bottom='${hex_code[1]}' style='background-color: #${colors[0]}; color: ${colors[1]}; width: ${char_width}px'></span>`;
60+
if(this.colorInSpecialChars) {
61+
if(char_width == 0) {
62+
return `<span class='code-input_special-char code-input_special-char_zero-width ${hex_code[0] == "0" && hex_code[1] == "0" ? "code-input_special-char_one-byte" : ""}' data-hex0='${hex_code[0]}' data-hex1='${hex_code[1]}' data-hex2='${hex_code[2]}' data-hex3='${hex_code[3]}' style='background-color: #${colors[0]}; color: ${colors[1]};'></span>`;
63+
} else {
64+
return `<span class='code-input_special-char ${hex_code[0] == "0" && hex_code[1] == "0" ? "code-input_special-char_one-byte" : ""}' data-hex0='${hex_code[0]}' data-hex1='${hex_code[1]}' data-hex2='${hex_code[2]}' data-hex3='${hex_code[3]}' style='background-color: #${colors[0]}; color: ${colors[1]}; width: ${char_width}px'></span>`;
65+
}
66+
} else {
67+
if(char_width == 0) {
68+
return `<span class='code-input_special-char code-input_special-char_zero-width ${hex_code[0] == "0" && hex_code[1] == "0" ? "code-input_special-char_one-byte" : ""}' data-hex0='${hex_code[0]}' data-hex1='${hex_code[1]}' data-hex2='${hex_code[2]}' data-hex3='${hex_code[3]}'></span>`;
69+
} else {
70+
return `<span class='code-input_special-char ${hex_code[0] == "0" && hex_code[1] == "0" ? "code-input_special-char_one-byte" : ""}' data-hex0='${hex_code[0]}' data-hex1='${hex_code[1]}' data-hex2='${hex_code[2]}' data-hex3='${hex_code[3]}' style='width: ${char_width}px'></span>`;
71+
}
72+
}
73+
5374
}
5475

5576
getCharacterColor(ascii_code) {
@@ -79,21 +100,27 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
79100
}
80101

81102
getCharacterWidth(char) {
103+
// Force zero-width characters
104+
if(new RegExp("\u00AD|\u02de|[\u0300-\u036F]|[\u0483-\u0489]|\u200b").test(char) ) { return 0 }
105+
// Non-renderable ASCII characters should all be rendered at same size
106+
if(char != "\u0096" && new RegExp("[\u{0000}-\u{001F}]|[\u{007F}-\u{009F}]", "g").test(char)) {
107+
let fallbackWidth = this.getCharacterWidth("\u0096");
108+
console.log(char.codePointAt(0).toString(16), "Fallback", fallbackWidth);
109+
return fallbackWidth;
110+
}
82111
// Lazy-load - TODO: Get a cleaner way of doing this
83112
if(char in this.cachedWidths) {
84113
return this.cachedWidths[char];
85114
}
86115

87-
this.canvasContext.fillText(char, 10, 20);
116+
// Try to get width
88117
let width = this.canvasContext.measureText(char).width;
89118
if(width > 20) {
90119
width /= 2; // Fix double-width-in-canvas Firefox bug
91120
} else if(width == 0) {
92121
let fallbackWidth = this.getCharacterWidth("\u0096");
93-
this.cachedWidths[char] = fallbackWidth;
94-
return fallbackWidth; // In Firefox some control chars don't render
122+
return fallbackWidth; // In Firefox some control chars don't render, but all control chars are the same width
95123
}
96-
console.log(char, this.canvasContext.measureText(char));
97124

98125
this.cachedWidths[char] = width;
99126
return width;

0 commit comments

Comments
 (0)