1+ ( function  ( )  { 
2+     // The merge HTMLPlugin was copied from https://github.com/highlightjs/highlight.js/issues/2889 
3+     var  mergeHTMLPlugin  =  ( function  ( )  { 
4+         'use strict' ; 
5+ 
6+         var  originalStream ; 
7+ 
8+         /** 
9+          * @param  {string } value 
10+          * @returns  {string } 
11+          */ 
12+         function  escapeHTML ( value )  { 
13+             return  value 
14+                 . replace ( / & / g,  '&' ) 
15+                 . replace ( / < / g,  '<' ) 
16+                 . replace ( / > / g,  '>' ) 
17+                 . replace ( / " / g,  '"' ) 
18+                 . replace ( / ' / g,  ''' ) ; 
19+         } 
20+ 
21+         /* plugin itself */ 
22+ 
23+         /** @type  {HLJSPlugin } */ 
24+         const  mergeHTMLPlugin  =  { 
25+             // preserve the original HTML token stream 
26+             "before:highlightElement" : ( {  el } )  =>  { 
27+                 originalStream  =  nodeStream ( el ) ; 
28+             } , 
29+             // merge it afterwards with the highlighted token stream 
30+             "after:highlightElement" : ( {  el,  result,  text } )  =>  { 
31+                 if  ( ! originalStream . length )  return ; 
32+ 
33+                 const  resultNode  =  document . createElement ( 'div' ) ; 
34+                 resultNode . innerHTML  =  result . value ; 
35+                 result . value  =  mergeStreams ( originalStream ,  nodeStream ( resultNode ) ,  text ) ; 
36+                 el . innerHTML  =  result . value ; 
37+             } 
38+         } ; 
39+ 
40+         /* Stream merging support functions */ 
41+ 
42+         /** 
43+          * @typedef  Event 
44+          * @property  {'start'|'stop' } event 
45+          * @property  {number } offset 
46+          * @property  {Node } node 
47+          */ 
48+ 
49+         /** 
50+          * @param  {Node } node 
51+          */ 
52+         function  tag ( node )  { 
53+             return  node . nodeName . toLowerCase ( ) ; 
54+         } 
55+ 
56+         /** 
57+          * @param  {Node } node 
58+          */ 
59+         function  nodeStream ( node )  { 
60+             /** @type  Event[] */ 
61+             const  result  =  [ ] ; 
62+             ( function  _nodeStream ( node ,  offset )  { 
63+                 for  ( let  child  =  node . firstChild ;  child ;  child  =  child . nextSibling )  { 
64+                     if  ( child . nodeType  ===  3 )  { 
65+                         offset  +=  child . nodeValue . length ; 
66+                     }  else  if  ( child . nodeType  ===  1 )  { 
67+                         result . push ( { 
68+                             event : 'start' , 
69+                             offset : offset , 
70+                             node : child 
71+                         } ) ; 
72+                         offset  =  _nodeStream ( child ,  offset ) ; 
73+                         // Prevent void elements from having an end tag that would actually 
74+                         // double them in the output. There are more void elements in HTML 
75+                         // but we list only those realistically expected in code display. 
76+                         if  ( ! tag ( child ) . match ( / b r | h r | i m g | i n p u t / ) )  { 
77+                             result . push ( { 
78+                                 event : 'stop' , 
79+                                 offset : offset , 
80+                                 node : child 
81+                             } ) ; 
82+                         } 
83+                     } 
84+                 } 
85+                 return  offset ; 
86+             } ) ( node ,  0 ) ; 
87+             return  result ; 
88+         } 
89+ 
90+         /** 
91+          * @param  {any } original - the original stream 
92+          * @param  {any } highlighted - stream of the highlighted source 
93+          * @param  {string } value - the original source itself 
94+          */ 
95+         function  mergeStreams ( original ,  highlighted ,  value )  { 
96+             let  processed  =  0 ; 
97+             let  result  =  '' ; 
98+             const  nodeStack  =  [ ] ; 
99+ 
100+             function  selectStream ( )  { 
101+                 if  ( ! original . length  ||  ! highlighted . length )  { 
102+                     return  original . length  ? original  : highlighted ; 
103+                 } 
104+                 if  ( original [ 0 ] . offset  !==  highlighted [ 0 ] . offset )  { 
105+                     return  ( original [ 0 ] . offset  <  highlighted [ 0 ] . offset )  ? original  : highlighted ; 
106+                 } 
107+ 
108+                 /* 
109+                 To avoid starting the stream just before it should stop the order is 
110+                 ensured that original always starts first and closes last: 
111+            
112+                 if (event1 == 'start' && event2 == 'start') 
113+                   return original; 
114+                 if (event1 == 'start' && event2 == 'stop') 
115+                   return highlighted; 
116+                 if (event1 == 'stop' && event2 == 'start') 
117+                   return original; 
118+                 if (event1 == 'stop' && event2 == 'stop') 
119+                   return highlighted; 
120+            
121+                 ... which is collapsed to: 
122+                 */ 
123+                 return  highlighted [ 0 ] . event  ===  'start'  ? original  : highlighted ; 
124+             } 
125+ 
126+             /** 
127+              * @param  {Node } node 
128+              */ 
129+             function  open ( node )  { 
130+                 /** @param  {Attr } attr */ 
131+                 function  attributeString ( attr )  { 
132+                     return  ' '  +  attr . nodeName  +  '="'  +  escapeHTML ( attr . value )  +  '"' ; 
133+                 } 
134+                 // @ts -ignore 
135+                 result  +=  '<'  +  tag ( node )  +  [ ] . map . call ( node . attributes ,  attributeString ) . join ( '' )  +  '>' ; 
136+             } 
137+ 
138+             /** 
139+              * @param  {Node } node 
140+              */ 
141+             function  close ( node )  { 
142+                 result  +=  '</'  +  tag ( node )  +  '>' ; 
143+             } 
144+ 
145+             /** 
146+              * @param  {Event } event 
147+              */ 
148+             function  render ( event )  { 
149+                 ( event . event  ===  'start'  ? open  : close ) ( event . node ) ; 
150+             } 
151+ 
152+             while  ( original . length  ||  highlighted . length )  { 
153+                 let  stream  =  selectStream ( ) ; 
154+                 result  +=  escapeHTML ( value . substring ( processed ,  stream [ 0 ] . offset ) ) ; 
155+                 processed  =  stream [ 0 ] . offset ; 
156+                 if  ( stream  ===  original )  { 
157+                     /* 
158+                     On any opening or closing tag of the original markup we first close 
159+                     the entire highlighted node stack, then render the original tag along 
160+                     with all the following original tags at the same offset and then 
161+                     reopen all the tags on the highlighted stack. 
162+                     */ 
163+                     nodeStack . reverse ( ) . forEach ( close ) ; 
164+                     do  { 
165+                         render ( stream . splice ( 0 ,  1 ) [ 0 ] ) ; 
166+                         stream  =  selectStream ( ) ; 
167+                     }  while  ( stream  ===  original  &&  stream . length  &&  stream [ 0 ] . offset  ===  processed ) ; 
168+                     nodeStack . reverse ( ) . forEach ( open ) ; 
169+                 }  else  { 
170+                     if  ( stream [ 0 ] . event  ===  'start' )  { 
171+                         nodeStack . push ( stream [ 0 ] . node ) ; 
172+                     }  else  { 
173+                         nodeStack . pop ( ) ; 
174+                     } 
175+                     render ( stream . splice ( 0 ,  1 ) [ 0 ] ) ; 
176+                 } 
177+             } 
178+             return  result  +  escapeHTML ( value . substr ( processed ) ) ; 
179+         } 
180+ 
181+         return  mergeHTMLPlugin ; 
182+     } ( ) ) ; 
183+ 
184+     hljs . addPlugin ( mergeHTMLPlugin ) ; 
185+     hljs . highlightAll ( ) ; 
186+ } ) ( ) ; 
0 commit comments