33 * Files: special-chars.js, special-chars.css
44 */
55
6- // INCOMPLETE: TODO Optimise regex - compile at start; make so won't change contents of HTML code
6+ // INCOMPLETE: TODO Optimise regex - compile at start; Update CSS for character display; clean up + comment
77
88codeInput . plugins . SpecialChars = class extends codeInput . Plugin {
99 specialCharRegExp ;
@@ -17,7 +17,7 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
1717 * @param {RegExp } specialCharRegExp The regular expression which matches special characters
1818 * @param {Boolean } colorInSpecialChars Whether or not to give special characters custom background colors based on their hex code
1919 */
20- constructor ( specialCharRegExp = / (? ! \n ) (? ! \t ) [ \u{0000} - \u{001F} ] | [ \u{007F} - \u{FFFF} ] / ug, colorInSpecialChars = false ) { // By default, covers many non-renderable ASCII characters
20+ constructor ( specialCharRegExp = / (? ! \n ) (? ! \t ) [ \u{0000} - \u{001F} ] | [ \u{007F} - \u{009F} ] | [ \u{0200} - \u{ FFFF}] / ug, colorInSpecialChars = true ) { // By default, covers many non-renderable ASCII characters
2121 super ( ) ;
2222 this . specialCharRegExp = specialCharRegExp ;
2323 this . colorInSpecialChars = colorInSpecialChars ;
@@ -43,9 +43,45 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
4343 /* Runs after code is highlighted; Params: codeInput element) */
4444 afterHighlight ( codeInput ) {
4545 let result_element = codeInput . querySelector ( "pre code" ) ;
46- result_element . innerHTML = result_element . innerHTML . replaceAll ( this . specialCharRegExp , this . specialCharReplacer . bind ( this ) ) ;
46+ this . recursivelyReplaceText ( result_element ) ;
4747 }
48- specialCharReplacer ( match_char , _match_char , index , whole_string , groups ) {
48+
49+ recursivelyReplaceText ( element ) {
50+ for ( let i = 0 ; i < element . childNodes . length ; i ++ ) {
51+
52+ let nextNode = element . childNodes [ i ] ;
53+ if ( nextNode . nodeName == "#text" && nextNode . nodeValue != "" ) {
54+ // Replace in here
55+ let oldValue = nextNode . nodeValue ;
56+
57+ this . specialCharRegExp . lastIndex = 0 ;
58+ let searchResult = this . specialCharRegExp . exec ( oldValue ) ;
59+ if ( searchResult != null ) {
60+ let charIndex = searchResult . index ; // Start as returns end
61+
62+ nextNode = nextNode . splitText ( charIndex + 1 ) . previousSibling ;
63+
64+ if ( charIndex > 0 ) {
65+ nextNode = nextNode . splitText ( charIndex ) ; // Keep those before in difft. span
66+ }
67+
68+ if ( nextNode . textContent != "" ) {
69+ let replacementElement = this . specialCharReplacer ( nextNode . textContent ) ;
70+ nextNode . parentNode . insertBefore ( replacementElement , nextNode ) ;
71+ nextNode . textContent = "" ;
72+ }
73+ }
74+ } else if ( nextNode . nodeType == 1 ) {
75+ if ( nextNode . className != "code-input_special-char" && nextNode . nodeValue != "" ) {
76+ // Element - recurse
77+ this . recursivelyReplaceText ( nextNode ) ;
78+ }
79+ }
80+ }
81+ }
82+
83+ specialCharReplacer ( match_char ) {
84+ console . log ( 2 , match_char ) ;
4985 let hex_code = match_char . codePointAt ( 0 ) ;
5086
5187 let colors ;
@@ -57,20 +93,22 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
5793
5894 let char_width = this . getCharacterWidth ( match_char ) ;
5995
96+ // Create element with hex code
97+ let result = document . createElement ( "span" ) ;
98+ result . classList . add ( "code-input_special-char" ) ;
99+ result . setAttribute ( "data-hex0" , hex_code [ 0 ] ) ;
100+ result . setAttribute ( "data-hex1" , hex_code [ 1 ] ) ;
101+ result . setAttribute ( "data-hex2" , hex_code [ 2 ] ) ;
102+ result . setAttribute ( "data-hex3" , hex_code [ 3 ] ) ;
103+ // Handle zero-width chars
104+ if ( char_width == 0 ) result . classList . add ( "code-input_special-char_zero-width" ) ;
105+ else result . style . width = char_width + "px" ;
106+
60107 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- }
108+ result . style . backgroundColor = "#" + colors [ 0 ] ;
109+ result . style . color = colors [ 1 ] ;
72110 }
73-
111+ return result ;
74112 }
75113
76114 getCharacterColor ( ascii_code ) {
0 commit comments