1010 */ 
1111"use strict" ; 
1212
13- const  BLACKLISTED_KEY_CONTROL_ELEMENTS  =  new  Set ( [ 
14-   "TEXTAREA" , 
15-   "INPUT" , 
16-   "SELECT" , 
17-   "BUTTON" , 
18- ] ) ; 
19- 
2013const  _ready  =  ( callback )  =>  { 
2114  if  ( document . readyState  !==  "loading" )  { 
2215    callback ( ) ; 
@@ -25,11 +18,73 @@ const _ready = (callback) => {
2518  } 
2619} ; 
2720
21+ /** 
22+  * highlight a given string on a node by wrapping it in 
23+  * span elements with the given class name. 
24+  */ 
25+ const  _highlight  =  ( node ,  addItems ,  text ,  className )  =>  { 
26+   if  ( node . nodeType  ===  Node . TEXT_NODE )  { 
27+     const  val  =  node . nodeValue ; 
28+     const  parent  =  node . parentNode ; 
29+     const  pos  =  val . toLowerCase ( ) . indexOf ( text ) ; 
30+     if  ( 
31+       pos  >=  0  && 
32+       ! parent . classList . contains ( className )  && 
33+       ! parent . classList . contains ( "nohighlight" ) 
34+     )  { 
35+       let  span ; 
36+ 
37+       const  closestNode  =  parent . closest ( "body, svg, foreignObject" ) ; 
38+       const  isInSVG  =  closestNode  &&  closestNode . matches ( "svg" ) ; 
39+       if  ( isInSVG )  { 
40+         span  =  document . createElementNS ( "http://www.w3.org/2000/svg" ,  "tspan" ) ; 
41+       }  else  { 
42+         span  =  document . createElement ( "span" ) ; 
43+         span . classList . add ( className ) ; 
44+       } 
45+ 
46+       span . appendChild ( document . createTextNode ( val . substr ( pos ,  text . length ) ) ) ; 
47+       parent . insertBefore ( 
48+         span , 
49+         parent . insertBefore ( 
50+           document . createTextNode ( val . substr ( pos  +  text . length ) ) , 
51+           node . nextSibling 
52+         ) 
53+       ) ; 
54+       node . nodeValue  =  val . substr ( 0 ,  pos ) ; 
55+ 
56+       if  ( isInSVG )  { 
57+         const  rect  =  document . createElementNS ( 
58+           "http://www.w3.org/2000/svg" , 
59+           "rect" 
60+         ) ; 
61+         const  bbox  =  parent . getBBox ( ) ; 
62+         rect . x . baseVal . value  =  bbox . x ; 
63+         rect . y . baseVal . value  =  bbox . y ; 
64+         rect . width . baseVal . value  =  bbox . width ; 
65+         rect . height . baseVal . value  =  bbox . height ; 
66+         rect . setAttribute ( "class" ,  className ) ; 
67+         addItems . push ( {  parent : parent ,  target : rect  } ) ; 
68+       } 
69+     } 
70+   }  else  if  ( node . matches  &&  ! node . matches ( "button, select, textarea" ) )  { 
71+     node . childNodes . forEach ( ( el )  =>  _highlight ( el ,  addItems ,  text ,  className ) ) ; 
72+   } 
73+ } ; 
74+ const  _highlightText  =  ( thisNode ,  text ,  className )  =>  { 
75+   let  addItems  =  [ ] ; 
76+   _highlight ( thisNode ,  addItems ,  text ,  className ) ; 
77+   addItems . forEach ( ( obj )  => 
78+     obj . parent . insertAdjacentElement ( "beforebegin" ,  obj . target ) 
79+   ) ; 
80+ } ; 
81+ 
2882/** 
2983 * Small JavaScript module for the documentation. 
3084 */ 
3185const  Documentation  =  { 
3286  init : ( )  =>  { 
87+     Documentation . highlightSearchWords ( ) ; 
3388    Documentation . initDomainIndexTable ( ) ; 
3489    Documentation . initOnKeyListeners ( ) ; 
3590  } , 
@@ -71,6 +126,51 @@ const Documentation = {
71126    Documentation . LOCALE  =  catalog . locale ; 
72127  } , 
73128
129+   /** 
130+    * highlight the search words provided in the url in the text 
131+    */ 
132+   highlightSearchWords : ( )  =>  { 
133+     const  highlight  = 
134+       new  URLSearchParams ( window . location . search ) . get ( "highlight" )  ||  "" ; 
135+     const  terms  =  highlight . toLowerCase ( ) . split ( / \s + / ) . filter ( x  =>  x ) ; 
136+     if  ( terms . length  ===  0 )  return ;  // nothing to do 
137+ 
138+     // There should never be more than one element matching "div.body" 
139+     const  divBody  =  document . querySelectorAll ( "div.body" ) ; 
140+     const  body  =  divBody . length  ? divBody [ 0 ]  : document . querySelector ( "body" ) ; 
141+     window . setTimeout ( ( )  =>  { 
142+       terms . forEach ( ( term )  =>  _highlightText ( body ,  term ,  "highlighted" ) ) ; 
143+     } ,  10 ) ; 
144+ 
145+     const  searchBox  =  document . getElementById ( "searchbox" ) ; 
146+     if  ( searchBox  ===  null )  return ; 
147+     searchBox . appendChild ( 
148+       document 
149+         . createRange ( ) 
150+         . createContextualFragment ( 
151+           '<p class="highlight-link">'  + 
152+             '<a href="javascript:Documentation.hideSearchWords()">'  + 
153+             Documentation . gettext ( "Hide Search Matches" )  + 
154+             "</a></p>" 
155+         ) 
156+     ) ; 
157+   } , 
158+ 
159+   /** 
160+    * helper function to hide the search marks again 
161+    */ 
162+   hideSearchWords : ( )  =>  { 
163+     document 
164+       . querySelectorAll ( "#searchbox .highlight-link" ) 
165+       . forEach ( ( el )  =>  el . remove ( ) ) ; 
166+     document 
167+       . querySelectorAll ( "span.highlighted" ) 
168+       . forEach ( ( el )  =>  el . classList . remove ( "highlighted" ) ) ; 
169+     const  url  =  new  URL ( window . location ) ; 
170+     url . searchParams . delete ( "highlight" ) ; 
171+     window . history . replaceState ( { } ,  "" ,  url ) ; 
172+   } , 
173+ 
74174  /** 
75175   * helper function to focus on search bar 
76176   */ 
@@ -110,11 +210,15 @@ const Documentation = {
110210    ) 
111211      return ; 
112212
213+     const  blacklistedElements  =  new  Set ( [ 
214+       "TEXTAREA" , 
215+       "INPUT" , 
216+       "SELECT" , 
217+       "BUTTON" , 
218+     ] ) ; 
113219    document . addEventListener ( "keydown" ,  ( event )  =>  { 
114-       // bail for input elements 
115-       if  ( BLACKLISTED_KEY_CONTROL_ELEMENTS . has ( document . activeElement . tagName ) )  return ; 
116-       // bail with special keys 
117-       if  ( event . altKey  ||  event . ctrlKey  ||  event . metaKey )  return ; 
220+       if  ( blacklistedElements . has ( document . activeElement . tagName ) )  return ;  // bail for input elements 
221+       if  ( event . altKey  ||  event . ctrlKey  ||  event . metaKey )  return ;  // bail with special keys 
118222
119223      if  ( ! event . shiftKey )  { 
120224        switch  ( event . key )  { 
@@ -136,6 +240,10 @@ const Documentation = {
136240              event . preventDefault ( ) ; 
137241            } 
138242            break ; 
243+           case  "Escape" :
244+             if  ( ! DOCUMENTATION_OPTIONS . ENABLE_SEARCH_SHORTCUTS )  break ; 
245+             Documentation . hideSearchWords ( ) ; 
246+             event . preventDefault ( ) ; 
139247        } 
140248      } 
141249
0 commit comments