github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/docs/themes/hugo-theme-relearn/static/js/theme.js (about)

     1  window.relearn = window.relearn || {};
     2  
     3  var theme = true;
     4  var isIE = /*@cc_on!@*/false || !!document.documentMode;
     5  if( isIE ){
     6      // we don't support sidebar flyout in IE
     7      document.querySelector( 'body' ).classList.remove( 'mobile-support' );
     8  }
     9  else{
    10      document.querySelector( 'body' ).classList.add( 'mobile-support' );
    11  }
    12  
    13  var isPrint = document.querySelector( 'body' ).classList.contains( 'print' );
    14  
    15  var isRtl = document.querySelector( 'html' ).getAttribute( 'dir' ) == 'rtl';
    16  var lang = document.querySelector( 'html' ).getAttribute( 'lang' );
    17  var dir_padding_start = 'padding-left';
    18  var dir_padding_end = 'padding-right';
    19  var dir_key_start = 37;
    20  var dir_key_end = 39;
    21  var dir_scroll = 1;
    22  if( isRtl && !isIE ){
    23      dir_padding_start = 'padding-right';
    24      dir_padding_end = 'padding-left';
    25      dir_key_start = 39;
    26      dir_key_end = 37;
    27      dir_scroll = -1;
    28  }
    29  
    30  var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)
    31  
    32  var formelements = 'button, datalist, fieldset, input, label, legend, meter, optgroup, option, output, progress, select, textarea';
    33  
    34  // PerfectScrollbar
    35  var psc;
    36  var psm;
    37  var pst = new Map();
    38  var elc = document.querySelector('#R-body-inner');
    39  
    40  function regexEscape( s ){
    41      return s.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&' );
    42  }
    43  
    44  function documentFocus(){
    45      elc.focus();
    46      psc && psc.scrollbarY.focus();
    47  }
    48  
    49  function scrollbarWidth(){
    50      // https://davidwalsh.name/detect-scrollbar-width
    51      // Create the measurement node
    52      var scrollDiv = document.createElement("div");
    53      scrollDiv.className = "scrollbar-measure";
    54      document.body.appendChild(scrollDiv);
    55      // Get the scrollbar width
    56      var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
    57      // Delete the DIV
    58      document.body.removeChild(scrollDiv);
    59      return scrollbarWidth;
    60  }
    61  
    62  var scrollbarSize = scrollbarWidth();
    63  function adjustContentWidth(){
    64      var start = parseFloat( getComputedStyle( elc ).getPropertyValue( dir_padding_start ) );
    65      var end = start;
    66      if( elc.scrollHeight > elc.clientHeight ){
    67          // if we have a scrollbar reduce the end margin by the scrollbar width
    68          end = Math.max( 0, start - scrollbarSize );
    69      }
    70      elc.style[ dir_padding_end ] = '' + end + 'px';
    71  }
    72  
    73  function fixCodeTabs(){
    74      /* if only a single code block is contained in the tab and no style was selected, treat it like style=code */
    75      var codeTabContents = Array.from( document.querySelectorAll( '.tab-content.tab-panel-style' ) ).filter( function( tabContent ){
    76          return tabContent.querySelector( '*:scope > .tab-content-text > div.highlight:only-child, *:scope > .tab-content-text > pre:not(.mermaid).pre-code:only-child');
    77      });
    78  
    79      codeTabContents.forEach( function( tabContent ){
    80          var tabId = tabContent.dataset.tabItem;
    81          var tabPanel = tabContent.parentNode.parentNode;
    82          var tabButton = tabPanel.querySelector( '.tab-nav-button.tab-panel-style[data-tab-item="'+tabId+'"]' );
    83          if( tabContent.classList.contains( 'initial' ) ){
    84              tabButton.classList.remove( 'initial' );
    85              tabButton.classList.add( 'code' );
    86              tabContent.classList.remove( 'initial' );
    87              tabContent.classList.add( 'code' );
    88          }
    89          // mark code blocks for FF without :has()
    90          tabContent.classList.add( 'codify' );
    91      });
    92  }
    93  
    94  function switchTab(tabGroup, tabId) {
    95      var tabs = Array.from( document.querySelectorAll( '.tab-panel[data-tab-group="'+tabGroup+'"]' ) ).filter( function( e ){
    96          return !!e.querySelector( '[data-tab-item="'+tabId+'"]' );
    97      });
    98      var allTabItems = tabs && tabs.reduce( function( a, e ){
    99          return a.concat( Array.from( e.querySelectorAll( '[data-tab-item]' ) ).filter( function( es ){
   100              return es.parentNode.parentNode == e;
   101          }) );
   102      }, [] );
   103      var targetTabItems = tabs && tabs.reduce( function( a, e ){
   104          return a.concat( Array.from( e.querySelectorAll( '[data-tab-item="'+tabId+'"]' ) ).filter( function( es ){
   105              return es.parentNode.parentNode == e;
   106          }) );
   107      }, [] );
   108  
   109      // if event is undefined then switchTab was called from restoreTabSelection
   110      // so it's not a button event and we don't need to safe the selction or
   111      // prevent page jump
   112      var isButtonEvent = event && event.target && event.target.getBoundingClientRect;
   113      if(isButtonEvent){
   114        // save button position relative to viewport
   115        var yposButton = event.target.getBoundingClientRect().top;
   116      }
   117  
   118      allTabItems && allTabItems.forEach( function( e ){ e.classList.remove( 'active' ); e.removeAttribute( 'tabindex' ); });
   119      targetTabItems && targetTabItems.forEach( function( e ){ e.classList.add( 'active' ); e.setAttribute( 'tabindex', '-1' ); });
   120  
   121      if(isButtonEvent){
   122        initMermaid( true );
   123  
   124        // reset screen to the same position relative to clicked button to prevent page jump
   125        var yposButtonDiff = event.target.getBoundingClientRect().top - yposButton;
   126        window.scrollTo(window.scrollX, window.scrollY+yposButtonDiff);
   127  
   128        // Store the selection to make it persistent
   129        if(window.localStorage){
   130            var selectionsJSON = window.localStorage.getItem(window.relearn.absBaseUri+"/tab-selections");
   131            if(selectionsJSON){
   132              var tabSelections = JSON.parse(selectionsJSON);
   133            }else{
   134              var tabSelections = {};
   135            }
   136            tabSelections[tabGroup] = tabId;
   137            window.localStorage.setItem(window.relearn.absBaseUri+"/tab-selections", JSON.stringify(tabSelections));
   138        }
   139      }
   140  }
   141  
   142  function restoreTabSelections() {
   143      if(window.localStorage){
   144          var selectionsJSON = window.localStorage.getItem(window.relearn.absBaseUri+"/tab-selections");
   145          if(selectionsJSON){
   146            var tabSelections = JSON.parse(selectionsJSON);
   147          }else{
   148            var tabSelections = {};
   149          }
   150          Object.keys(tabSelections).forEach(function(tabGroup) {
   151            var tabItem = tabSelections[tabGroup];
   152            switchTab(tabGroup, tabItem);
   153          });
   154      }
   155  }
   156  
   157  function initMermaid( update, attrs ) {
   158      var doBeside = true;
   159      var isImageRtl = false;
   160  
   161      // we are either in update or initialization mode;
   162      // during initialization, we want to edit the DOM;
   163      // during update we only want to execute if something changed
   164      var decodeHTML = function( html ){
   165          var txt = document.createElement( 'textarea' );
   166          txt.innerHTML = html;
   167          return txt.value;
   168      };
   169  
   170      var parseGraph = function( graph ){
   171          // See https://github.com/mermaid-js/mermaid/blob/9a080bb975b03b2b1d4ef6b7927d09e6b6b62760/packages/mermaid/src/diagram-api/frontmatter.ts#L10
   172          // for reference on the regex originally taken from jekyll
   173          var YAML=1;
   174          var INIT=2;
   175          var GRAPH=3;
   176          var d = /^(?:\s*[\n\r])*(?:-{3}(\s*[\n\r](?:.*?)[\n\r])-{3}(?:\s*[\n\r]+)+)?(?:\s*(?:%%\s*\{\s*\w+\s*:([^%]*?)%%\s*[\n\r]?))?(.*)$/s
   177          var m = d.exec( graph );
   178          var yaml = {};
   179          var dir = {};
   180          var content = graph;
   181          if( m && m.length == 4 ){
   182              yaml = m[YAML] ? jsyaml.load( m[YAML] ) : yaml;
   183              dir = m[INIT] ? JSON.parse( '{ "init": ' + m[INIT] ).init : dir;
   184              content = m[GRAPH] ? m[GRAPH] : content;
   185          }
   186          var ret = { yaml: yaml, dir: dir, content: content.trim() }
   187          return ret;
   188      };
   189  
   190      var serializeGraph = function( graph ){
   191          var yamlPart = '';
   192          if( Object.keys( graph.yaml ).length ){
   193              yamlPart = '---\n' + jsyaml.dump( graph.yaml ) + '---\n';
   194          }
   195          var dirPart = '';
   196          if( Object.keys( graph.dir ).length ){
   197              dirPart = '%%{init: ' + JSON.stringify( graph.dir ) + '}%%\n';
   198          }
   199          return yamlPart + dirPart + graph.content;
   200      };
   201  
   202      var init_func = function( attrs ){
   203          var is_initialized = false;
   204          var theme = attrs.theme;
   205          document.querySelectorAll('.mermaid').forEach( function( element ){
   206              var parse = parseGraph( decodeHTML( element.innerHTML ) );
   207  
   208              if( parse.yaml.theme ){
   209                  parse.yaml.relearn_user_theme = true;
   210              }
   211              if( parse.dir.theme ){
   212                  parse.dir.relearn_user_theme = true;
   213              }
   214              if( !parse.yaml.relearn_user_theme && !parse.dir.relearn_user_theme ){
   215                  parse.yaml.theme = theme;
   216              }
   217              is_initialized = true;
   218  
   219              var graph = serializeGraph( parse );
   220              var new_element = document.createElement( 'div' );
   221              Array.from( element.attributes ).forEach( function( attr ){
   222                  new_element.setAttribute( attr.name, attr.value );
   223                  element.removeAttribute( attr.name );
   224              });
   225              new_element.classList.add( 'mermaid-container' );
   226              new_element.classList.remove( 'mermaid' );
   227              element.classList.add( 'mermaid' );
   228  
   229              element.innerHTML = graph;
   230              if( element.offsetParent !== null ){
   231                  element.classList.add( 'mermaid-render' );
   232              }
   233              new_element.innerHTML = '<div class="mermaid-code">' + graph + '</div>' + element.outerHTML;
   234              element.parentNode.replaceChild( new_element, element );
   235          });
   236          return is_initialized;
   237      }
   238  
   239      var update_func = function( attrs ){
   240          var is_initialized = false;
   241          var theme = attrs.theme;
   242          document.querySelectorAll( '.mermaid-container' ).forEach( function( e ){
   243              var element = e.querySelector( '.mermaid' );
   244              var code = e.querySelector( '.mermaid-code' );
   245              var parse = parseGraph( decodeHTML( code.innerHTML ) );
   246  
   247              if( element.classList.contains( 'mermaid-render' ) ){
   248                  if( parse.yaml.relearn_user_theme || parse.dir.relearn_user_theme ){
   249                      return;
   250                  }
   251                  if( parse.yaml.theme == theme || parse.dir.theme == theme ){
   252                      return;
   253                  }
   254              }
   255              if( element.offsetParent !== null ){
   256                  element.classList.add( 'mermaid-render' );
   257              }
   258              else{
   259                  element.classList.remove( 'mermaid-render' );
   260                  return;
   261              }
   262              is_initialized = true;
   263  
   264              parse.yaml.theme = theme;
   265              var graph = serializeGraph( parse );
   266              element.removeAttribute('data-processed');
   267              element.innerHTML = graph;
   268              code.innerHTML = graph;
   269          });
   270          return is_initialized;
   271      };
   272  
   273      var state = this;
   274      if( update && !state.is_initialized ){
   275          return;
   276      }
   277      if( typeof variants == 'undefined' ){
   278          return;
   279      }
   280      if( typeof mermaid == 'undefined' || typeof mermaid.mermaidAPI == 'undefined' ){
   281          return;
   282      }
   283  
   284      if( !state.is_initialized ){
   285          state.is_initialized = true;
   286          window.addEventListener( 'beforeprint', function(){
   287              initMermaid( true, {
   288                  'theme': variants.getColorValue( 'PRINT-MERMAID-theme' ),
   289              });
   290          }.bind( this ) );
   291          window.addEventListener( 'afterprint', function(){
   292              initMermaid( true );
   293          }.bind( this ) );
   294      }
   295  
   296      attrs = attrs || {
   297          'theme': variants.getColorValue( 'MERMAID-theme' ),
   298      };
   299  
   300      var search;
   301      if( update ){
   302          search = sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' );
   303          unmark();
   304      }
   305      var is_initialized = ( update ? update_func( attrs ) : init_func( attrs ) );
   306      if( is_initialized ){
   307          mermaid.initialize( Object.assign( { "securityLevel": "antiscript", "startOnLoad": false }, window.relearn.mermaidConfig, { theme: attrs.theme } ) );
   308          mermaid.run({
   309              postRenderCallback: function( id ){
   310                  // zoom for Mermaid
   311                  // https://github.com/mermaid-js/mermaid/issues/1860#issuecomment-1345440607
   312                  var svgs = d3.selectAll( 'body:not(.print) .mermaid-container.zoomable > .mermaid > #' + id );
   313                  svgs.each( function(){
   314                      var parent = this.parentElement;
   315                      // we need to copy the maxWidth, otherwise our reset button will not align in the upper right
   316                      parent.style.maxWidth = this.style.maxWidth || this.getAttribute( 'width' );
   317                      // if no unit is given for the width
   318                      parent.style.maxWidth = parent.style.maxWidth || 'calc( ' + this.getAttribute( 'width' ) + 'px + 1rem )';
   319                      var svg = d3.select( this );
   320                      svg.html( '<g>' + svg.html() + '</g>' );
   321                      var inner = svg.select( '*:scope > g' );
   322                      parent.insertAdjacentHTML( 'beforeend', '<span class="svg-reset-button" title="' + window.T_Reset_view + '"><i class="fas fa-undo-alt"></i></span>' );
   323                      var button = parent.querySelector( '.svg-reset-button' );
   324                      var zoom = d3.zoom().on( 'zoom', function( e ){
   325                          inner.attr( 'transform', e.transform );
   326                          if( e.transform.k == 1 && e.transform.x == 0 && e.transform.y == 0 ){
   327                              button.classList.remove( 'zoomed' );
   328                          }
   329                          else{
   330                              button.classList.add( 'zoomed' );
   331                          }
   332                      });
   333                      button.addEventListener( 'click', function( event ){
   334                          svg.transition()
   335                              .duration( 350 )
   336                              .call( zoom.transform, d3.zoomIdentity );
   337                          this.setAttribute( 'aria-label', window.T_View_reset );
   338                          this.classList.add( 'tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's'+(isImageRtl?'e':'w')) );
   339                      });
   340                      button.addEventListener( 'mouseleave', function() {
   341                          if( this.classList.contains( 'tooltipped' ) ){
   342                              this.classList.remove( 'tooltipped', 'tooltipped-w', 'tooltipped-se', 'tooltipped-sw' );
   343                              this.removeAttribute( 'aria-label' );
   344                          }
   345                      });
   346                      svg.call( zoom );
   347                  });
   348              },
   349              querySelector: '.mermaid.mermaid-render',
   350              suppressErrors: true
   351          });
   352      }
   353      if( update && search && search.length ){
   354          sessionStorage.setItem( window.relearn.absBaseUri+'/search-value', search );
   355          mark();
   356      }
   357  }
   358  
   359  function initOpenapi( update, attrs ){
   360      if( isIE ){
   361          return;
   362      }
   363  
   364      var state = this;
   365      if( update && !state.is_initialized ){
   366          return;
   367      }
   368      if( typeof variants == 'undefined' ){
   369          return;
   370      }
   371  
   372      if( !state.is_initialized ){
   373          state.is_initialized = true;
   374          window.addEventListener( 'beforeprint', function(){
   375              initOpenapi( true, { isPrintPreview: true } );
   376          }.bind( this ) );
   377          window.addEventListener( 'afterprint', function(){
   378              initOpenapi( true, { isPrintPreview: false } );
   379          }.bind( this ) );
   380      }
   381  
   382      attrs = attrs || {
   383          isPrintPreview: false
   384      };
   385  
   386      function addFunctionToResizeEvent(){
   387  
   388      }
   389      function getFirstAncestorByClass(){
   390  
   391      }
   392      function renderOpenAPI(oc) {
   393          var relBasePath = window.relearn.relBasePath;
   394          var mod = window.relearn.themeVariantModifier;
   395          var buster = window.themeUseOpenapi.assetsBuster ? '?' + window.themeUseOpenapi.assetsBuster : '';
   396          var print = isPrint || attrs.isPrintPreview ? "PRINT-" : "";
   397          var theme = print ? `${relBasePath}/css/theme-relearn-light${mod}.css${buster}` : document.querySelector( '#R-variant-style' ).attributes.href.value
   398          var swagger_theme = variants.getColorValue( print + 'OPENAPI-theme' );
   399          var swagger_code_theme = variants.getColorValue( print + 'OPENAPI-CODE-theme' );
   400  
   401          const openapiId = 'relearn-swagger-ui';
   402          const openapiIframeId = openapiId + "-iframe";
   403          const openapiIframe = document.getElementById(openapiIframeId);
   404          if (openapiIframe) {
   405              openapiIframe.remove();
   406          }
   407          const openapiErrorId = openapiId + '-error';
   408          const openapiError = document.getElementById(openapiErrorId);
   409          if (openapiError) {
   410              openapiError.remove();
   411          }
   412          const oi = document.createElement('iframe');
   413          oi.id = openapiIframeId;
   414          oi.classList.toggle('sc-openapi-iframe', true);
   415          oi.srcdoc =
   416              '<!doctype html>' +
   417              '<html lang="' + lang + '" dir="' + (isRtl ? 'rtl' : 'ltr') + '">' +
   418                  '<head>' +
   419                      '<link rel="stylesheet" href="' + window.themeUseOpenapi.css + '">' +
   420                      '<link rel="stylesheet" href="' + theme + '">' +
   421                      '<link rel="stylesheet" href="' + relBasePath + '/css/swagger.css' + buster + '">' +
   422                      '<link rel="stylesheet" href="' + relBasePath + '/css/swagger-' + swagger_theme + '.css' + buster + '">' +
   423                  '</head>' +
   424                  '<body>' +
   425                      '<a class="relearn-expander" href="" onclick="return relearn_collapse_all()">Collapse all</a>' +
   426                      '<a class="relearn-expander" href="" onclick="return relearn_expand_all()">Expand all</a>' +
   427                      '<div id="relearn-swagger-ui"></div>' +
   428                      '<script>' +
   429                          'function relearn_expand_all(){' +
   430                              'document.querySelectorAll( ".opblock-summary-control[aria-expanded=false]" ).forEach( btn => btn.click() );' +
   431                              'document.querySelectorAll( ".model-container > .model-box > button[aria-expanded=false]" ).forEach( btn => btn.click() );' +
   432                              'return false;' +
   433                          '}' +
   434                          'function relearn_collapse_all(){' +
   435                              'document.querySelectorAll( ".opblock-summary-control[aria-expanded=true]" ).forEach( btn => btn.click() );' +
   436                              'document.querySelectorAll( ".model-container > .model-box > .model-box > .model > span > button[aria-expanded=true]" ).forEach( btn => btn.click() );' +
   437                              'return false;' +
   438                          '}' +
   439                      '</script>' +
   440                  '</body>' +
   441              '</html>';
   442          oi.height = '100%';
   443          oi.width = '100%';
   444          oi.onload = function(){
   445              const openapiWrapper = getFirstAncestorByClass(oc, 'sc-openapi-wrapper');
   446              const openapiPromise = new Promise( function(resolve){ resolve() });
   447              openapiPromise
   448                  .then( function(){
   449                      var options = {
   450                          defaultModelsExpandDepth: 2,
   451                          defaultModelExpandDepth: 2,
   452                          docExpansion: isPrint || attrs.isPrintPreview ? 'full' : 'list',
   453                          domNode: oi.contentWindow.document.getElementById(openapiId),
   454                          filter: !( isPrint || attrs.isPrintPreview ),
   455                          layout: 'BaseLayout',
   456                          onComplete: function(){
   457                              if( isPrint || attrs.isPrintPreview ){
   458                                  oi.contentWindow.document.querySelectorAll( '.model-container > .model-box > button[aria-expanded=false]' ).forEach( function(btn){ btn.click() });
   459                                  setOpenAPIHeight(oi);
   460                              }
   461                          },
   462                          plugins: [
   463                              SwaggerUIBundle.plugins.DownloadUrl
   464                          ],
   465                          presets: [
   466                              SwaggerUIBundle.presets.apis,
   467                              SwaggerUIStandalonePreset,
   468                          ],
   469                          syntaxHighlight: {
   470                              activated: true,
   471                              theme: swagger_code_theme,
   472                          },
   473                          validatorUrl: 'none',
   474                      };
   475                      if( oc.dataset.openapiSpec ){
   476                          try{
   477                              Object.assign( options, { spec: JSON.parse( oc.dataset.openapiSpec ) });
   478                          } catch( err ){
   479                              try{
   480                                  Object.assign( options, { spec: jsyaml.load( oc.dataset.openapiSpec ) });
   481                              } catch( err ){
   482                                  console.error( 'OpenAPI: file "' + oc.dataset.openapiUrl + '" could not be parsed as JSON or YAML');
   483                              }
   484                          }
   485                      }
   486                      else{
   487                          Object.assign( options, { url: oc.dataset.openapiUrl });
   488                      }
   489                      SwaggerUIBundle( options );
   490                  })
   491                  .then( function(){
   492                      let observerCallback = function () {
   493                          setOpenAPIHeight(oi);
   494                      };
   495                      let observer = new MutationObserver(observerCallback);
   496                      observer.observe(oi.contentWindow.document.documentElement, {
   497                          childList: true,
   498                          subtree: true,
   499                      });
   500                  })
   501                  .then( function(){
   502                      if (openapiWrapper) {
   503                          openapiWrapper.classList.toggle('is-loading', false);
   504                      }
   505                      setOpenAPIHeight(oi);
   506                  })
   507                  .catch( function(error){
   508                      const ed = document.createElement('div');
   509                      ed.classList.add('sc-alert', 'sc-alert-error');
   510                      ed.innerHTML = error;
   511                      ed.id = openapiErrorId;
   512                      while (oc.lastChild) {
   513                          oc.removeChild(oc.lastChild);
   514                      }
   515                      if (openapiWrapper) {
   516                          openapiWrapper.classList.toggle('is-loading', false);
   517                          openapiWrapper.insertAdjacentElement('afterbegin', ed);
   518                      }
   519                  });
   520          };
   521          oc.appendChild(oi);
   522      }
   523      function setOpenAPIHeight(oi) {
   524          // add empirical offset if in print preview (GC 103)
   525          oi.style.height =
   526              (oi.contentWindow.document.documentElement.getBoundingClientRect().height + (attrs.isPrintPreview ? 200 : 0) )+
   527              'px';
   528      }
   529      function resizeOpenAPI() {
   530          let divi = document.getElementsByClassName('sc-openapi-iframe');
   531          for (let i = 0; i < divi.length; i++) {
   532              setOpenAPIHeight(divi[i]);
   533          }
   534      };
   535      let divo = document.getElementsByClassName('sc-openapi-container');
   536      for (let i = 0; i < divo.length; i++) {
   537          renderOpenAPI(divo[i]);
   538      }
   539      if (divo.length) {
   540          addFunctionToResizeEvent(resizeOpenAPI);
   541      }
   542  }
   543  
   544  function initAnchorClipboard(){
   545      document.querySelectorAll( 'h1~h2,h1~h3,h1~h4,h1~h5,h1~h6').forEach( function( element ){
   546          var url = encodeURI( (document.location.origin == "null" ? (document.location.protocol + "//" + document.location.host) : document.location.origin )+ document.location.pathname);
   547          var link = url + "#" + element.id;
   548          var new_element = document.createElement( 'span' );
   549          new_element.classList.add( 'anchor' );
   550          new_element.setAttribute( 'title', window.T_Copy_link_to_clipboard );
   551          new_element.setAttribute( 'data-clipboard-text', link );
   552          new_element.innerHTML = '<i class="fas fa-link fa-lg"></i>';
   553          element.appendChild( new_element );
   554      });
   555  
   556      var anchors = document.querySelectorAll( '.anchor' );
   557      for( var i = 0; i < anchors.length; i++ ) {
   558        anchors[i].addEventListener( 'mouseleave', function( e ){
   559          this.removeAttribute( 'aria-label' );
   560          this.classList.remove( 'tooltipped', 'tooltipped-se', 'tooltipped-sw' );
   561        });
   562      }
   563  
   564      var clip = new ClipboardJS( '.anchor' );
   565      clip.on( 'success', function( e ){
   566          e.clearSelection();
   567          e.trigger.setAttribute( 'aria-label', window.T_Link_copied_to_clipboard );
   568          e.trigger.classList.add( 'tooltipped', 'tooltipped-s'+(isRtl?'e':'w') );
   569      });
   570  }
   571  
   572  function initCodeClipboard(){
   573      function getCodeText( node ){
   574          // if highlight shortcode is used in inline lineno mode, remove lineno nodes before generating text, otherwise it doesn't hurt
   575          var code = node.cloneNode( true );
   576          Array.from( code.querySelectorAll( '*:scope > span > span:first-child:not(:last-child)' ) ).forEach( function( lineno ){
   577              lineno.remove();
   578          });
   579          var text = code.textContent;
   580          // remove a trailing line break, this may most likely
   581          // come from the browser / Hugo transformation
   582          text = text.replace( /\n$/, '' );
   583          return text;
   584      }
   585  
   586      function fallbackMessage( action ){
   587          var actionMsg = '';
   588          var actionKey = (action === 'cut' ? 'X' : 'C');
   589          if (/iPhone|iPad/i.test(navigator.userAgent)) {
   590              actionMsg = 'No support :(';
   591          }
   592          else if (/Mac/i.test(navigator.userAgent)) {
   593              actionMsg = 'Press ⌘-' + actionKey + ' to ' + action;
   594          }
   595          else {
   596              actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action;
   597          }
   598          return actionMsg;
   599      }
   600  
   601      var codeElements = document.querySelectorAll( 'code' );
   602      for( var i = 0; i < codeElements.length; i++ ){
   603          var code = codeElements[i];
   604          var text = getCodeText( code );
   605          var inPre = code.parentNode.tagName.toLowerCase() == 'pre';
   606          var inTable = inPre &&
   607              code.parentNode.parentNode.tagName.toLowerCase() == 'td' &&
   608              code.parentNode.parentNode.classList.contains('lntd');
   609          // avoid copy-to-clipboard for highlight shortcode in table lineno mode
   610          var isFirstLineCell = inTable &&
   611              code.parentNode.parentNode.parentNode.querySelector( 'td:first-child > pre > code' ) == code;
   612  
   613          if( !isFirstLineCell && ( inPre || text.length > 5 ) ){
   614              code.classList.add( 'copy-to-clipboard-code' );
   615              if( inPre ){
   616                  code.classList.add( 'copy-to-clipboard' );
   617                  code.parentNode.classList.add( 'pre-code' );
   618              }
   619              else{
   620                  var clone = code.cloneNode( true );
   621                  var span = document.createElement( 'span' );
   622                  span.classList.add( 'copy-to-clipboard' );
   623                  span.appendChild( clone );
   624                  code.parentNode.replaceChild( span, code );
   625                  code = clone;
   626              }
   627              var button = document.createElement( 'span' );
   628              button.classList.add( 'copy-to-clipboard-button' );
   629              button.setAttribute( 'title', window.T_Copy_to_clipboard );
   630              button.innerHTML = '<i class="far fa-copy"></i>';
   631              button.addEventListener( 'mouseleave', function() {
   632                  this.removeAttribute( 'aria-label' );
   633                  this.classList.remove( 'tooltipped', 'tooltipped-w', 'tooltipped-se', 'tooltipped-sw' );
   634              });
   635              if( inTable ){
   636                  var table = code.parentNode.parentNode.parentNode.parentNode.parentNode;
   637                  table.dataset.code = text;
   638                  table.parentNode.insertBefore( button, table.nextSibling );
   639              }
   640              else if( inPre ){
   641                  var pre = code.parentNode;
   642                  pre.dataset.code = text;
   643                  var p = pre.parentNode;
   644                  // indented code blocks are missing the div
   645                  while( p != document && ( p.tagName.toLowerCase() != 'div' || !p.classList.contains( 'highlight' ) ) ){
   646                      p = p.parentNode;
   647                  }
   648                  if( p == document ){
   649                      var clone = pre.cloneNode( true );
   650                      var div = document.createElement( 'div' );
   651                      div.classList.add( 'highlight' );
   652                      div.appendChild( clone );
   653                      pre.parentNode.replaceChild( div, pre );
   654                      pre = clone;
   655                  }
   656                  pre.parentNode.insertBefore( button, pre.nextSibling );
   657              }
   658              else{
   659                  code.dataset.code = text;
   660                  code.parentNode.insertBefore( button, code.nextSibling );
   661              }
   662          }
   663      }
   664  
   665      var clip = new ClipboardJS( '.copy-to-clipboard-button', {
   666          text: function( trigger ){
   667              if( !trigger.previousElementSibling ){
   668                  return '';
   669              }
   670              return trigger.previousElementSibling.dataset.code || '';
   671          }
   672      });
   673  
   674      clip.on( 'success', function( e ){
   675          e.clearSelection();
   676          var inPre = e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'pre';
   677          var isCodeRtl = !inPre ? isRtl : false;
   678          var doBeside = inPre || (e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'table' );
   679          e.trigger.setAttribute( 'aria-label', window.T_Copied_to_clipboard );
   680          e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's'+(isCodeRtl?'e':'w')) );
   681      });
   682  
   683      clip.on( 'error', function( e ){
   684          var inPre = e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'pre';
   685          var isCodeRtl = !inPre ? isRtl : false;
   686          var doBeside = inPre || (e.trigger.previousElementSibling && e.trigger.previousElementSibling.tagName.toLowerCase() == 'table' );
   687          e.trigger.setAttribute( 'aria-label', fallbackMessage(e.action) );
   688          e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's'+(isCodeRtl?'e':'w')) );
   689          var f = function(){
   690              e.trigger.setAttribute( 'aria-label', window.T_Copied_to_clipboard );
   691              e.trigger.classList.add( 'tooltipped', 'tooltipped-' + (doBeside ? 'w' : 's'+(isCodeRtl?'e':'w')) );
   692              document.removeEventListener( 'copy', f );
   693          };
   694          document.addEventListener( 'copy', f );
   695      });
   696  }
   697  
   698  function initChroma( update ){
   699      var chroma = variants.getColorValue( 'CODE-theme' );
   700      var link = document.querySelector( '#R-variant-chroma-style' );
   701      var old_path = link.getAttribute( 'href' );
   702      var new_path = old_path.replace( /^(.*\/chroma-).*?(\.css.*)$/, '$1' + chroma + '$2' );
   703      link.setAttribute( 'href', new_path );
   704  }
   705  
   706  function initArrowVerticalNav(){
   707      var topMain = 0;
   708      if( !isPrint ){
   709          topMain = document.querySelector("main").getClientRects()[0].top;
   710      }
   711  
   712      document.addEventListener('keydown', function(event){
   713          var elems = Array.from( document.querySelectorAll( `main :not(.include.hide-first-heading) > :where(
   714                  .article-subheading,
   715                  :not(.article-subheading) + h1:not(.a11y-only),
   716                  h1:not(.a11y-only):first-child,
   717                  h2, h3, h4, h5, h6
   718              ),
   719              main .include.hide-first-heading > :where( h1, h2, h3, h4, h5, h6 ) ~ :where( h1, h2, h3, h4, h5, h6 )
   720          ` ));
   721          if( !event.shiftKey && !event.ctrlKey && event.altKey && !event.metaKey ){
   722              if( event.which == 38 ){ // up
   723                  var target = isPrint ? document.querySelector( '#R-body' ) : document.querySelector( '.flex-block-wrapper' );
   724                  elems.some( function( elem, i ){
   725                      var top = elem.getBoundingClientRect().top;
   726                      var topBoundary = top - topMain;
   727                      if( topBoundary > -1 ){
   728                          target.scrollIntoView();
   729                          return true;
   730                      }
   731                      target = elem
   732                  })
   733              }
   734              else if( event.which == 40 ){ // down
   735                  elems.some( function( elem, i ){
   736                      var top = elem.getBoundingClientRect().top;
   737                      var topBoundary = top - topMain;
   738                      if( topBoundary > -1 && topBoundary < 1 ){
   739                          if( i+1 < elems.length ){
   740                              var target = elems[ i+1 ];
   741                              target.scrollIntoView();
   742                          }
   743                          return true;
   744                      }
   745                      if( topBoundary >= 1 ){
   746                          var target = elem;
   747                          target.scrollIntoView();
   748                          return true;
   749                      }
   750                  })
   751              }
   752          }
   753      });
   754  }
   755  
   756  function initArrowHorizontalNav(){
   757      if( isPrint ){
   758          return;
   759      }
   760  
   761      // button navigation
   762      var prev = document.querySelector( '.topbar-button-prev a' );
   763      prev && prev.addEventListener( 'click', navPrev );
   764      var next = document.querySelector( '.topbar-button-next a' );
   765      next && next.addEventListener( 'click', navNext );
   766  
   767      // keyboard navigation
   768      // avoid prev/next navigation if we are not at the start/end of the
   769      // horizontal area
   770      var el = document.querySelector('#R-body-inner');
   771      var scrollStart = 0;
   772      var scrollEnd = 0;
   773      document.addEventListener('keydown', function(event){
   774          if( !event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey ){
   775              if( event.which == dir_key_start ){
   776                  if( !scrollStart && +el.scrollLeft.toFixed()*dir_scroll <= 0 ){
   777                      prev && prev.click();
   778                  }
   779                  else if( scrollStart != -1 ){
   780                      clearTimeout( scrollStart );
   781                  }
   782                  scrollStart = -1;
   783              }
   784              if( event.which == dir_key_end ){
   785                  if( !scrollEnd && +el.scrollLeft.toFixed()*dir_scroll + +el.clientWidth.toFixed() >= +el.scrollWidth.toFixed() ){
   786                      next && next.click();
   787                  }
   788                  else if( scrollEnd != -1 ){
   789                      clearTimeout( scrollEnd );
   790                  }
   791                  scrollEnd = -1;
   792              }
   793          }
   794      });
   795      document.addEventListener('keyup', function(event){
   796          if( !event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey ){
   797              if( event.which == dir_key_start ){
   798                  // check for false indication if keyup is delayed after navigation
   799                  if( scrollStart == -1 ){
   800                      scrollStart = setTimeout( function(){ scrollStart = 0; }, 300 );
   801                  }
   802              }
   803              if( event.which == dir_key_end ){
   804                  if( scrollEnd == -1 ){
   805                      scrollEnd = setTimeout( function(){ scrollEnd = 0; }, 300 );
   806                  }
   807              }
   808          }
   809      });
   810  
   811      // avoid keyboard navigation for input fields
   812      document.querySelectorAll( formelements ).forEach( function( e ){
   813          e.addEventListener( 'keydown', function( event ){
   814              if( event.which == dir_key_start || event.which == dir_key_end ){
   815                  event.stopPropagation();
   816              }
   817          });
   818      });
   819  }
   820  
   821  function initMenuScrollbar(){
   822      if( isPrint ){
   823          return;
   824      }
   825  
   826      var elm = document.querySelector('#R-content-wrapper');
   827      var elt = document.querySelector('.topbar-button.topbar-flyout .topbar-content-wrapper');
   828  
   829      var autofocus = true;
   830      document.addEventListener('keydown', function(event){
   831          // for initial keyboard scrolling support, no element
   832          // may be hovered, but we still want to react on
   833          // cursor/page up/down. because we can't hack
   834          // the scrollbars implementation, we try to trick
   835          // it and give focus to the scrollbar - only
   836          // to just remove the focus right after scrolling
   837          // happend
   838          autofocus = false;
   839          if( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey || event.which < 32 || event.which > 40 ){
   840              // if tab key was pressed, we are ended with our initial
   841              // focus job
   842              return;
   843          }
   844  
   845          var c = elc && elc.matches(':hover');
   846          var m = elm && elm.matches(':hover');
   847          var t = elt && elt.matches(':hover');
   848          var f = event.target.matches( formelements );
   849          if( !c && !m && !t && !f ){
   850              // only do this hack if none of our scrollbars
   851              // is hovered
   852              // if we are showing the sidebar as a flyout we
   853              // want to scroll the content-wrapper, otherwise we want
   854              // to scroll the body
   855              var nt = document.querySelector('body').matches('.topbar-flyout');
   856              var nm = document.querySelector('body').matches('.sidebar-flyout');
   857              if( nt ){
   858                  var psb = pst.get( document.querySelector('.topbar-button.topbar-flyout') );
   859                  psb && psb.scrollbarY.focus();
   860              }
   861              else if( nm ){
   862                  psm && psm.scrollbarY.focus();
   863              }
   864              else{
   865                  document.querySelector('#R-body-inner').focus();
   866                  psc && psc.scrollbarY.focus();
   867              }
   868          }
   869      });
   870      // scrollbars will install their own keyboard handlers
   871      // that need to be executed inbetween our own handlers
   872      // PSC removed for #242 #243 #244
   873      // psc = elc && new PerfectScrollbar('#R-body-inner');
   874      psm = elm && new PerfectScrollbar('#R-content-wrapper', { scrollingThreshold: 2000, swipeEasing: false, wheelPropagation: false });
   875      document.querySelectorAll('.topbar-button .topbar-content-wrapper').forEach( function( e ){
   876          var button = getTopbarButtonParent( e );
   877          if( !button ){
   878              return;
   879          }
   880          pst.set( button, new PerfectScrollbar( e, { scrollingThreshold: 2000, swipeEasing: false, wheelPropagation: false }) );
   881          e.addEventListener( 'click', toggleTopbarFlyoutEvent );
   882      });
   883  
   884      document.addEventListener('keydown', function(){
   885          // if we facked initial scrolling, we want to
   886          // remove the focus to not leave visual markers on
   887          // the scrollbar
   888          if( autofocus ){
   889              psc && psc.scrollbarY.blur();
   890              psm && psm.scrollbarY.blur();
   891              pst.forEach( function( psb ){
   892                  psb.scrollbarY.blur();
   893              });
   894              autofocus = false;
   895          }
   896      });
   897      // on resize, we have to redraw the scrollbars to let new height
   898      // affect their size
   899      window.addEventListener('resize', function(){
   900          pst.forEach( function( psb ){
   901              setTimeout( function(){ psb.update(); }, 10 );
   902          });
   903          psm && setTimeout( function(){ psm.update(); }, 10 );
   904          psc && setTimeout( function(){ psc.update(); }, 10 );
   905      });
   906      // now that we may have collapsible menus, we need to call a resize
   907      // for the menu scrollbar if sections are expanded/collapsed
   908      document.querySelectorAll('#R-sidebar .collapsible-menu input').forEach( function(e){
   909          e.addEventListener('change', function(){
   910              psm && setTimeout( function(){ psm.update(); }, 10 );
   911          });
   912      });
   913      // bugfix for PS in RTL mode: the initial scrollbar position is off;
   914      // calling update() once, fixes this
   915      pst.forEach( function( psb ){
   916          setTimeout( function(){ psb.update(); }, 10 );
   917      });
   918      psm && setTimeout( function(){ psm.update(); }, 10 );
   919      psc && setTimeout( function(){ psc.update(); }, 10 );
   920  
   921      // finally, we want to adjust the contents end padding if there is a scrollbar visible
   922      window.addEventListener('resize', adjustContentWidth );
   923      adjustContentWidth();
   924  }
   925  
   926  function imageEscapeHandler( event ){
   927      if( event.key == "Escape" ){
   928          var image = event.target;
   929          image.click();
   930      }
   931  }
   932  
   933  function navShortcutHandler( event ){
   934      if( !event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 78 /* n */ ){
   935          toggleNav();
   936      }
   937  }
   938  
   939  function searchShortcutHandler( event ){
   940      if( !event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 70 /* f */ ){
   941          showSearch();
   942      }
   943  }
   944  
   945  function tocShortcutHandler( event ){
   946      if( !event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 84 /* t */ ){
   947          toggleToc();
   948      }
   949  }
   950  
   951  function editShortcutHandler( event ){
   952      if( !event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 87 /* w */ ){
   953          showEdit();
   954      }
   955  }
   956  
   957  function printShortcutHandler( event ){
   958      if( !event.shiftKey && event.altKey && event.ctrlKey && !event.metaKey && event.which == 80 /* p */ ){
   959          showPrint();
   960      }
   961  }
   962  
   963  function showSearch(){
   964      var s = document.querySelector( '#R-search-by' );
   965      if( !s ){
   966          return;
   967      }
   968      var b = document.querySelector( 'body' );
   969      if( s == document.activeElement ){
   970          if( b.classList.contains( 'sidebar-flyout' ) ){
   971              closeNav();
   972          }
   973          documentFocus();
   974      } else {
   975          if( !b.classList.contains( 'sidebar-flyout' ) ){
   976              openNav();
   977          }
   978          s.focus();
   979      }
   980  }
   981  
   982  function openNav(){
   983      closeSomeTopbarButtonFlyout();
   984      var b = document.querySelector( 'body' );
   985      b.classList.add( 'sidebar-flyout' );
   986      psm && setTimeout( function(){ psm.update(); }, 10 );
   987      psm && psm.scrollbarY.focus();
   988      var a = document.querySelector( '#R-sidebar a' )
   989      if( a ){
   990          a.focus();
   991      }
   992  }
   993  
   994  function closeNav(){
   995      var b = document.querySelector( 'body' );
   996      b.classList.remove( 'sidebar-flyout' );
   997      documentFocus();
   998  }
   999  
  1000  function toggleNav(){
  1001      var b = document.querySelector( 'body' );
  1002      if( b.classList.contains( 'sidebar-flyout' ) ){
  1003          closeNav();
  1004      }
  1005      else{
  1006          openNav();
  1007      }
  1008  }
  1009  
  1010  function navEscapeHandler( event ){
  1011      if( event.key == "Escape" ){
  1012          closeNav();
  1013      }
  1014  }
  1015  
  1016  function getTopbarButtonParent( e ){
  1017      var button = e;
  1018      while( button && !button.classList.contains( 'topbar-button' ) ){
  1019          button = button.parentElement;
  1020      }
  1021      return button;
  1022  }
  1023  
  1024  function openTopbarButtonFlyout( button ){
  1025      closeNav();
  1026      var body = document.querySelector( 'body' );
  1027      button.classList.add( 'topbar-flyout' );
  1028      body.classList.add( 'topbar-flyout' );
  1029      var psb = pst.get( button );
  1030      psb && setTimeout( function(){ psb.update(); }, 10 );
  1031      psb && psb.scrollbarY.focus();
  1032      var a = button.querySelector( '.topbar-content-wrapper a' );
  1033      if( a ){
  1034          a.focus();
  1035      }
  1036  }
  1037  
  1038  function closeTopbarButtonFlyout( button ){
  1039      var body = document.querySelector( 'body' );
  1040      button.classList.remove( 'topbar-flyout' );
  1041      body.classList.remove( 'topbar-flyout' );
  1042      documentFocus();
  1043  }
  1044  
  1045  function closeSomeTopbarButtonFlyout(){
  1046      var someButton = document.querySelector( '.topbar-button.topbar-flyout' );
  1047      if( someButton ){
  1048          closeTopbarButtonFlyout( someButton );
  1049      };
  1050      return someButton
  1051  }
  1052  
  1053  function toggleTopbarButtonFlyout( button ){
  1054      var someButton = closeSomeTopbarButtonFlyout();
  1055      if( button && button != someButton ){
  1056          openTopbarButtonFlyout( button );
  1057      }
  1058  }
  1059  
  1060  function toggleTopbarFlyout( e ){
  1061      var button = getTopbarButtonParent( e );
  1062      if( !button ){
  1063          return;
  1064      }
  1065      toggleTopbarButtonFlyout( button );
  1066  }
  1067  
  1068  function toggleTopbarFlyoutEvent( event ){
  1069      if( event.target.classList.contains( 'topbar-content' )
  1070          || event.target.classList.contains( 'topbar-content-wrapper' )
  1071          || event.target.classList.contains( 'ps__rail-x' )
  1072          || event.target.classList.contains( 'ps__rail-y' )
  1073          || event.target.classList.contains( 'ps__thumb-x' )
  1074          || event.target.classList.contains( 'ps__thumb-y' )
  1075          ){
  1076          // the scrollbar was used, don't close flyout
  1077          return;
  1078      }
  1079      toggleTopbarFlyout( event.target )
  1080  }
  1081  
  1082  function topbarFlyoutEscapeHandler( event ){
  1083      if( event.key == "Escape" ){
  1084          closeSomeTopbarButtonFlyout();
  1085      }
  1086  }
  1087  
  1088  function toggleToc(){
  1089      toggleTopbarButtonFlyout( document.querySelector( '.topbar-button-toc' ) );
  1090  }
  1091  
  1092  function showEdit(){
  1093      var l = document.querySelector( '.topbar-button-edit a' );
  1094      if( l ){
  1095          l.click();
  1096      }
  1097  }
  1098  
  1099  function showPrint(){
  1100      var l = document.querySelector( '.topbar-button-print a' );
  1101      if( l ){
  1102          l.click();
  1103      }
  1104  }
  1105  
  1106  function navPrev(){
  1107      var e = document.querySelector( '.topbar-button-prev a' );
  1108      location.href = e && e.getAttribute( 'href' );
  1109  };
  1110  
  1111  function navNext(){
  1112      var e = document.querySelector( '.topbar-button-next a' );
  1113      location.href = e && e.getAttribute( 'href' );
  1114  };
  1115  
  1116  function initToc(){
  1117      if( isPrint ){
  1118          return;
  1119      }
  1120  
  1121      document.addEventListener( 'keydown', editShortcutHandler );
  1122      document.addEventListener( 'keydown', navShortcutHandler );
  1123      document.addEventListener( 'keydown', printShortcutHandler );
  1124      document.addEventListener( 'keydown', searchShortcutHandler );
  1125      document.addEventListener( 'keydown', tocShortcutHandler );
  1126      document.addEventListener( 'keydown', navEscapeHandler );
  1127      document.addEventListener( 'keydown', topbarFlyoutEscapeHandler );
  1128  
  1129      var b = document.querySelector( '#R-body-overlay' );
  1130      if( b ){
  1131          b.addEventListener( 'click', closeNav );
  1132      }
  1133      var m = document.querySelector( '#R-main-overlay' );
  1134      if( m ){
  1135          m.addEventListener( 'click', closeSomeTopbarButtonFlyout );
  1136      }
  1137  
  1138      // finally give initial focus to allow keyboard scrolling in FF
  1139      documentFocus();
  1140  }
  1141  
  1142  function initSwipeHandler(){
  1143      if( !touchsupport ){
  1144          return;
  1145      }
  1146  
  1147      var startx = null;
  1148      var starty = null;
  1149      var handleStartX = function(evt) {
  1150          startx = evt.touches[0].clientX;
  1151          starty = evt.touches[0].clientY;
  1152      };
  1153      var handleMoveX = function(evt) {
  1154          if( startx !== null ){
  1155              var diffx = startx - evt.touches[0].clientX;
  1156              var diffy = starty - evt.touches[0].clientY || .1 ;
  1157              if( diffx / Math.abs( diffy ) < 2 ){
  1158                  // detect mostly vertical swipes and reset our starting pos
  1159                  // to not detect a horizontal move if vertical swipe is unprecise
  1160                  startx = evt.touches[0].clientX;
  1161              }
  1162              else if( diffx > 30 ){
  1163                  startx = null;
  1164                  starty = null;
  1165                  closeNav();
  1166              }
  1167          }
  1168      };
  1169      var handleEndX = function(evt) {
  1170          startx = null;
  1171          starty = null;
  1172      };
  1173  
  1174      var s = document.querySelector( '#R-body-overlay' );
  1175      s && s.addEventListener("touchstart", handleStartX, { capture: false, passive: true});
  1176      document.querySelector( '#R-sidebar' ).addEventListener("touchstart", handleStartX, { capture: false, passive: true});
  1177      document.querySelectorAll( '#R-sidebar *' ).forEach( function(e){ e.addEventListener("touchstart", handleStartX, { capture: false, passive: true}) });
  1178      s && s.addEventListener("touchmove", handleMoveX, { capture: false, passive: true});
  1179      document.querySelector( '#R-sidebar' ).addEventListener("touchmove", handleMoveX, { capture: false, passive: true});
  1180      document.querySelectorAll( '#R-sidebar *' ).forEach( function(e){ e.addEventListener("touchmove", handleMoveX, { capture: false, passive: true}) });
  1181      s && s.addEventListener("touchend", handleEndX, { capture: false, passive: true});
  1182      document.querySelector( '#R-sidebar' ).addEventListener("touchend", handleEndX, { capture: false, passive: true});
  1183      document.querySelectorAll( '#R-sidebar *' ).forEach( function(e){ e.addEventListener("touchend", handleEndX, { capture: false, passive: true}) });
  1184  }
  1185  
  1186  function initImage(){
  1187      document.querySelectorAll( '.lightbox-back' ).forEach( function(e){ e.addEventListener( 'keydown', imageEscapeHandler ); });
  1188  }
  1189  
  1190  function initExpand(){
  1191      document.querySelectorAll( '.expand > input' ).forEach( function(e){ e.addEventListener( 'change', initMermaid.bind( null, true, null ) ); });
  1192  }
  1193  
  1194  function clearHistory() {
  1195      var visitedItem = window.relearn.absBaseUri + '/visited-url/'
  1196      for( var item in sessionStorage ){
  1197          if( item.substring( 0, visitedItem.length ) === visitedItem ){
  1198              sessionStorage.removeItem( item );
  1199              var url = item.substring( visitedItem.length );
  1200              // in case we have `relativeURLs=true` we have to strip the
  1201              // relative path to root
  1202              url = url.replace( /\.\.\//g, '/' ).replace( /^\/+\//, '/' );
  1203              document.querySelectorAll( '[data-nav-id="'+url+'"]' ).forEach( function( e ){
  1204                  e.classList.remove( 'visited' );
  1205              });
  1206          }
  1207      }
  1208  }
  1209  
  1210  function initHistory() {
  1211      var visitedItem = window.relearn.absBaseUri + '/visited-url/'
  1212      sessionStorage.setItem( visitedItem+document.querySelector( 'body' ).dataset.url, 1);
  1213  
  1214      // loop through the sessionStorage and see if something should be marked as visited
  1215      for( var item in sessionStorage ){
  1216          if( item.substring( 0, visitedItem.length ) === visitedItem && sessionStorage.getItem( item ) == 1 ){
  1217              var url = item.substring( visitedItem.length );
  1218              // in case we have `relativeURLs=true` we have to strip the
  1219              // relative path to root
  1220              url = url.replace( /\.\.\//g, '/' ).replace( /^\/+\//, '/' );
  1221              document.querySelectorAll( '[data-nav-id="'+url+'"]' ).forEach( function( e ){
  1222                  e.classList.add( 'visited' );
  1223              });
  1224          }
  1225      }
  1226  }
  1227  
  1228  function initScrollPositionSaver(){
  1229      function savePosition( event ){
  1230          var state = window.history.state || {};
  1231          state = Object.assign( {}, ( typeof state === 'object' ) ? state : {} );
  1232          state.contentScrollTop = +elc.scrollTop;
  1233          window.history.replaceState( state, '', window.location );
  1234      };
  1235  
  1236      var ticking = false;
  1237      elc.addEventListener( 'scroll', function( event ){
  1238          if( !ticking ){
  1239              window.requestAnimationFrame( function(){
  1240                  savePosition();
  1241                  ticking = false;
  1242              });
  1243              ticking = true;
  1244          }
  1245      });
  1246  
  1247      document.addEventListener( "click", savePosition );
  1248  }
  1249  
  1250  function scrollToPositions() {
  1251      // show active menu entry
  1252      window.setTimeout( function(){
  1253          var e = document.querySelector( '#R-sidebar li.active a' );
  1254          if( e && e.scrollIntoView ){
  1255              e.scrollIntoView({
  1256                  block: 'center',
  1257              });
  1258          }
  1259      }, 10 );
  1260  
  1261      // scroll the content to point of interest;
  1262      // if we have a scroll position saved, the user was here
  1263      // before in his history stack and we want to reposition
  1264      // to the position he was when he left the page;
  1265      // otherwise if he used page search before, we want to position
  1266      // to its last outcome;
  1267      // otherwise he may want to see a specific fragment
  1268  
  1269      var state = window.history.state || {};
  1270      state = ( typeof state === 'object')  ? state : {};
  1271      if( state.hasOwnProperty( 'contentScrollTop' ) ){
  1272          window.setTimeout( function(){
  1273              elc.scrollTop = +state.contentScrollTop;
  1274          }, 10 );
  1275          return;
  1276      }
  1277  
  1278      var search = sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' );
  1279      if( search && search.length ){
  1280          search = regexEscape( search );
  1281          var found = elementContains( search, elc );
  1282          var searchedElem = found.length && found[ 0 ];
  1283          if( searchedElem ){
  1284              searchedElem.scrollIntoView( true );
  1285              var scrolledY = window.scrollY;
  1286              if( scrolledY ){
  1287                  window.scroll( 0, scrolledY - 125 );
  1288              }
  1289          }
  1290          return;
  1291      }
  1292  
  1293      if( window.location.hash && window.location.hash.length > 1 ){
  1294          window.setTimeout( function(){
  1295              try{
  1296                  var e = document.querySelector( window.location.hash );
  1297                  if( e && e.scrollIntoView ){
  1298                      e.scrollIntoView({
  1299                          block: 'start',
  1300                      });
  1301                  }
  1302              } catch( e ){}
  1303          }, 10 );
  1304          return;
  1305      }
  1306  }
  1307  
  1308  window.addEventListener( 'popstate', function ( event ){
  1309      scrollToPositions();
  1310  });
  1311  
  1312  const observer = new PerformanceObserver( function(){
  1313      scrollToPositions();
  1314  });
  1315  observer.observe({ type: "navigation" });
  1316  
  1317  function mark() {
  1318      // mark some additional stuff as searchable
  1319      var bodyInnerLinks = document.querySelectorAll( '#R-body-inner a:not(.lightbox-link):not(.btn):not(.lightbox-back)' );
  1320      for( var i = 0; i < bodyInnerLinks.length; i++ ){
  1321          bodyInnerLinks[i].classList.add( 'highlight' );
  1322      }
  1323  
  1324      var value = sessionStorage.getItem( window.relearn.absBaseUri + '/search-value' );
  1325      var highlightableElements = document.querySelectorAll( '.highlightable' );
  1326      highlight( highlightableElements, value, { element: 'mark', className: 'search' } );
  1327  
  1328      var markedElements = document.querySelectorAll( 'mark.search' );
  1329      for( var i = 0; i < markedElements.length; i++ ){
  1330          var parent = markedElements[i].parentNode;
  1331          while( parent && parent.classList ){
  1332              if( parent.classList.contains( 'expand' ) ){
  1333                  var expandInputs = parent.querySelectorAll( 'input:not(.expand-marked)' );
  1334                  if( expandInputs.length ){
  1335                      expandInputs[0].classList.add( 'expand-marked' );
  1336                      expandInputs[0].dataset.checked = expandInputs[0].checked ? 'true' : 'false';
  1337                      expandInputs[0].checked = true;
  1338                  }
  1339              }
  1340              if( parent.tagName.toLowerCase() === 'li' && parent.parentNode && parent.parentNode.tagName.toLowerCase() === 'ul' && parent.parentNode.classList.contains( 'collapsible-menu' )){
  1341                  var toggleInputs = parent.querySelectorAll( 'input:not(.menu-marked)' );
  1342                  if( toggleInputs.length ){
  1343                      toggleInputs[0].classList.add( 'menu-marked' );
  1344                      toggleInputs[0].dataset.checked = toggleInputs[0].checked ? 'true' : 'false';
  1345                      toggleInputs[0].checked = true;
  1346                  }
  1347              }
  1348              parent = parent.parentNode;
  1349          }
  1350      }
  1351      psm && setTimeout( function(){ psm.update(); }, 10 );
  1352  }
  1353  window.relearn.markSearch = mark;
  1354  
  1355  function highlight( es, words, options ){
  1356      var settings = {
  1357          className: 'highlight',
  1358          element: 'span',
  1359          caseSensitive: false,
  1360          wordsOnly: false
  1361      };
  1362      Object.assign( settings, options );
  1363  
  1364      if( !words ){ return; }
  1365      if( words.constructor === String ){
  1366          words = [ words ];
  1367      }
  1368      words = words.filter( function( word, i ){
  1369          return word != '';
  1370      });
  1371      words = words.map( function( word, i ){
  1372          return regexEscape( word );
  1373      });
  1374      if( words.length == 0 ){ return this; }
  1375  
  1376      var flag = settings.caseSensitive ? '' : 'i';
  1377      var pattern = "(" + words.join( '|' ) + ')';
  1378      if( settings.wordsOnly ){
  1379          pattern = '\\b' + pattern + '\\b';
  1380      }
  1381      var re = new RegExp( pattern, flag );
  1382  
  1383      for( var i = 0; i < es.length; i++ ){
  1384          highlightNode( es[i], re, settings.element, settings.className );
  1385      }
  1386  };
  1387  
  1388  function highlightNode( node, re, nodeName, className ){
  1389      if( node.nodeType === 3 && node.parentElement && node.parentElement.namespaceURI == 'http://www.w3.org/1999/xhtml' ) { // text nodes
  1390          var match = node.data.match( re );
  1391          if( match ){
  1392              var highlight = document.createElement( nodeName || 'span' );
  1393              highlight.className = className || 'highlight';
  1394              var wordNode = node.splitText( match.index );
  1395              wordNode.splitText( match[0].length );
  1396              var wordClone = wordNode.cloneNode( true );
  1397              highlight.appendChild( wordClone );
  1398              wordNode.parentNode.replaceChild( highlight, wordNode );
  1399              return 1; //skip added node in parent
  1400          }
  1401      } else if( (node.nodeType === 1 && node.childNodes) && // only element nodes that have children
  1402          !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
  1403          !(node.tagName === nodeName.toUpperCase() && node.className === className) ){ // skip if already highlighted
  1404          for( var i = 0; i < node.childNodes.length; i++ ){
  1405              i += highlightNode( node.childNodes[i], re, nodeName, className );
  1406          }
  1407      }
  1408      return 0;
  1409  };
  1410  
  1411  function unmark() {
  1412      sessionStorage.removeItem( window.relearn.absBaseUri + '/search-value' );
  1413      var markedElements = document.querySelectorAll( 'mark.search' );
  1414      for( var i = 0; i < markedElements.length; i++ ){
  1415          var parent = markedElements[i].parentNode;
  1416          while( parent && parent.classList ){
  1417              if( parent.tagName.toLowerCase() === 'li' && parent.parentNode && parent.parentNode.tagName.toLowerCase() === 'ul' && parent.parentNode.classList.contains( 'collapsible-menu' )){
  1418                  var toggleInputs = parent.querySelectorAll( 'input.menu-marked' );
  1419                  if( toggleInputs.length ){
  1420                      toggleInputs[0].checked = toggleInputs[0].dataset.checked === 'true';
  1421                      toggleInputs[0].dataset.checked = null;
  1422                      toggleInputs[0].classList.remove( 'menu-marked' );
  1423                  }
  1424              }
  1425              if( parent.classList.contains( 'expand' ) ){
  1426                  var expandInputs = parent.querySelectorAll( 'input.expand-marked' );
  1427                  if( expandInputs.length ){
  1428                      expandInputs[0].checked = expandInputs[0].dataset.checked === 'true';
  1429                      expandInputs[0].dataset.checked = null;
  1430                      expandInputs[0].classList.remove( 'expand-marked' );
  1431                  }
  1432              }
  1433              parent = parent.parentNode;
  1434          }
  1435      }
  1436  
  1437      var highlighted = document.querySelectorAll( '.highlightable' );
  1438      unhighlight( highlighted, { element: 'mark', className: 'search' } );
  1439      psm && setTimeout( function(){ psm.update(); }, 10 );
  1440  }
  1441  
  1442  function unhighlight( es, options ){
  1443      var settings = {
  1444          className: 'highlight',
  1445          element: 'span'
  1446      };
  1447      Object.assign( settings, options );
  1448  
  1449      for( var i = 0; i < es.length; i++ ){
  1450          var highlightedElements = es[i].querySelectorAll( settings.element + '.' + settings.className );
  1451          for( var j = 0; j < highlightedElements.length; j++ ){
  1452              var parent = highlightedElements[j].parentNode;
  1453              parent.replaceChild( highlightedElements[j].firstChild, highlightedElements[j] );
  1454              parent.normalize();
  1455          }
  1456      }
  1457  };
  1458  
  1459  // replace jQuery.createPseudo with https://stackoverflow.com/a/66318392
  1460  function elementContains( txt, e ){
  1461      var regex = RegExp( txt, 'i' );
  1462      var nodes = [];
  1463      if( e ){
  1464          var tree = document.createTreeWalker( e, 4 /* NodeFilter.SHOW_TEXT */, function( node ){
  1465              return regex.test( node.data );
  1466          }, false );
  1467          var node = null;
  1468          while( node = tree.nextNode() ){
  1469              nodes.push( node.parentElement );
  1470          }
  1471      }
  1472      return nodes;
  1473  }
  1474  
  1475  function searchInputHandler( value ){
  1476      unmark();
  1477      if( value.length ){
  1478          sessionStorage.setItem( window.relearn.absBaseUri+'/search-value', value );
  1479          mark();
  1480      }
  1481  }
  1482  
  1483  function initSearch() {
  1484      // sync input/escape between searchbox and searchdetail
  1485      var inputs = document.querySelectorAll( 'input.search-by' );
  1486      inputs.forEach( function( e ){
  1487          e.addEventListener( 'keydown', function( event ){
  1488              if( event.key == 'Escape' ){
  1489                  var input = event.target;
  1490                  var search = sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' );
  1491                  if( !search || !search.length ){
  1492                      input.blur();
  1493                  }
  1494                  searchInputHandler( '' );
  1495                  inputs.forEach( function( e ){
  1496                      e.value = '';
  1497                  });
  1498                  if( !search || !search.length ){
  1499                      documentFocus();
  1500                  }
  1501              }
  1502          });
  1503          e.addEventListener( 'input', function( event ){
  1504              var input = event.target;
  1505              var value = input.value;
  1506              searchInputHandler( value );
  1507              inputs.forEach( function( e ){
  1508                  if( e != input ){
  1509                      e.value = value;
  1510                  }
  1511              });
  1512          });
  1513      });
  1514  
  1515      document.querySelectorAll( '[data-search-clear]' ).forEach( function( e ){
  1516          e.addEventListener( 'click', function(){
  1517              inputs.forEach( function( e ){
  1518                  e.value = '';
  1519                  var event = document.createEvent( 'Event' );
  1520                  event.initEvent( 'input', false, false );
  1521                  e.dispatchEvent( event );
  1522              });
  1523              unmark();
  1524          });
  1525      });
  1526  
  1527      var urlParams = new URLSearchParams( window.location.search );
  1528      var value = urlParams.get( 'search-by' );
  1529      if( value ){
  1530          sessionStorage.setItem( window.relearn.absBaseUri+'/search-value', value );
  1531      }
  1532      mark();
  1533  
  1534      // set initial search value for inputs on page load
  1535      if( sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' ) ){
  1536          var search = sessionStorage.getItem( window.relearn.absBaseUri+'/search-value' );
  1537          inputs.forEach( function( e ){
  1538              e.value = search;
  1539              var event = document.createEvent( 'Event' );
  1540              event.initEvent( 'input', false, false );
  1541              e.dispatchEvent( event );
  1542          });
  1543      }
  1544  
  1545      window.relearn.isSearchInit = true;
  1546      window.relearn.runInitialSearch && window.relearn.runInitialSearch();
  1547  }
  1548  
  1549  function updateTheme( detail ){
  1550      if( window.relearn.lastVariant == detail.variant ){
  1551          return;
  1552      }
  1553      window.relearn.lastVariant = detail.variant;
  1554  
  1555      initChroma( true );
  1556      initMermaid( true );
  1557      initOpenapi( true );
  1558      document.dispatchEvent( new CustomEvent( 'themeVariantLoaded', {
  1559          detail: detail
  1560      }));
  1561  }
  1562  
  1563  (function(){
  1564      window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
  1565          initChroma( true );
  1566          initMermaid( true );
  1567          initOpenapi( true );
  1568      });
  1569  })();
  1570  
  1571  function useMermaid( config ){
  1572      if( !Object.assign ){
  1573          // We don't support Mermaid for IE11 anyways, so bail out early
  1574          return;
  1575      }
  1576      window.relearn.mermaidConfig = config;
  1577      if (typeof mermaid != 'undefined' && typeof mermaid.mermaidAPI != 'undefined') {
  1578          mermaid.initialize( Object.assign( { "securityLevel": "antiscript", "startOnLoad": false }, config ) );
  1579          if( config.theme && variants ){
  1580              var write_style = variants.findLoadedStylesheet( 'R-variant-style' );
  1581              write_style.setProperty( '--CONFIG-MERMAID-theme', config.theme );
  1582          }
  1583      }
  1584  }
  1585  if( window.themeUseMermaid ){
  1586      useMermaid( window.themeUseMermaid );
  1587  }
  1588  
  1589  function useOpenapi( config ){
  1590      if( config.css && config.cssInProject ){
  1591          config.css = window.relearn.relBasePath + config.css;
  1592      }
  1593  }
  1594  if( window.themeUseOpenapi ){
  1595      useOpenapi( window.themeUseOpenapi );
  1596  }
  1597  
  1598  ready( function(){
  1599      initArrowVerticalNav();
  1600      initArrowHorizontalNav();
  1601      initMermaid();
  1602      initOpenapi();
  1603      initMenuScrollbar();
  1604      initToc();
  1605      initAnchorClipboard();
  1606      initCodeClipboard();
  1607      fixCodeTabs();
  1608      restoreTabSelections();
  1609      initSwipeHandler();
  1610      initHistory();
  1611      initSearch();
  1612      initImage();
  1613      initExpand();
  1614      initScrollPositionSaver();
  1615  });
  1616  
  1617  (function(){
  1618      var body = document.querySelector( 'body' );
  1619      var topbar = document.querySelector( '#R-topbar' );
  1620      function addTopbarButtonInfos(){
  1621          // initially add some management infos to buttons and areas
  1622          var areas = body.querySelectorAll( '.topbar-area' );
  1623          areas.forEach( function( area ){
  1624              area.dataset.area = 'area-' + area.dataset.area;
  1625              var buttons = area.querySelectorAll( ':scope > .topbar-button' );
  1626              buttons.forEach( function( button ){
  1627                  button.dataset.origin = area.dataset.area;
  1628                  button.dataset.action = 'show';
  1629                  var placeholder = document.createElement( 'div' );
  1630                  placeholder.classList.add( 'topbar-placeholder' );
  1631                  placeholder.dataset.action = 'show';
  1632                  button.insertAdjacentElement( 'afterend', placeholder );
  1633              });
  1634              var placeholder = document.createElement( 'div' );
  1635              area.insertAdjacentElement( 'beforeend', placeholder );
  1636              var hidden = document.createElement( 'div' );
  1637              hidden.classList.add( 'topbar-hidden' );
  1638              hidden.dataset.area = area.dataset.area;
  1639              var hplaceholder = document.createElement( 'div' );
  1640              hidden.insertAdjacentElement( 'beforeend', hplaceholder );
  1641              area.insertAdjacentElement( 'afterend', hidden );
  1642          });
  1643      }
  1644      function moveAreaTopbarButtons( width ){
  1645          topbar.querySelectorAll( '.topbar-hidden .topbar-button' ).forEach( function( button ){
  1646              // move hidden to origins area
  1647              var placeholder = button.parentNode.parentNode.querySelector( ':scope > .topbar-area .topbar-placeholder[data-action="hide"]' );
  1648              placeholder.dataset.action = 'show';
  1649              button.dataset.action = 'show';
  1650              placeholder.insertAdjacentElement( 'beforebegin', button );
  1651          });
  1652          topbar.querySelectorAll( '.topbar-area .topbar-button' ).forEach( function( button ){
  1653              var current_area = button.dataset.action;
  1654              var origin_area = button.dataset.origin;
  1655              if( current_area != 'show' && origin_area != current_area ){
  1656                  // move moved to origins area
  1657                  var placeholder = topbar.querySelector( '.topbar-area[data-area="' + origin_area + '"] > .topbar-placeholder[data-action="' + current_area + '"]' );
  1658                  placeholder.dataset.action = 'show';
  1659                  button.dataset.action = 'show';
  1660                  placeholder.insertAdjacentElement( 'beforebegin', button );
  1661              }
  1662          });
  1663          Array.from( topbar.querySelectorAll( '.topbar-area .topbar-button' ) ).reverse().forEach( function( button ){
  1664              var parent = button.parentElement;
  1665              var current_area = parent.dataset.area;
  1666              var action = button.dataset[ 'width' + width.toUpperCase() ];
  1667              if( action == 'show' ){
  1668              }
  1669              else if( action == 'hide' ){
  1670                  // move to origins hidden
  1671                  var hidden = button.parentNode.parentNode.querySelector( ':scope > .topbar-hidden > *' );
  1672                  var placeholder = button.nextSibling;
  1673                  placeholder.dataset.action = action;
  1674                  button.dataset.action = action;
  1675                  hidden.insertAdjacentElement( 'beforebegin', button );
  1676              }
  1677              else if( action != current_area ){
  1678                  // move to action area
  1679                  var dest = button.parentNode.parentNode.querySelector( '.topbar-area[data-area="' + action + '"] > *' );
  1680                  if( dest ){
  1681                      var placeholder = button.nextSibling;
  1682                      placeholder.dataset.action = action;
  1683                      button.dataset.action = action;
  1684                      dest.insertAdjacentElement( 'beforebegin', button );
  1685                  }
  1686              }
  1687          });
  1688      }
  1689      function moveTopbarButtons(){
  1690          var isS = body.classList.contains( 'menu-width-s' );
  1691          var isM = body.classList.contains( 'menu-width-m' );
  1692          var isL = body.classList.contains( 'menu-width-l' );
  1693          // move buttons once, width has a distinct value
  1694          if( isS && !isM && !isL ){
  1695              moveAreaTopbarButtons( 's' )
  1696          }
  1697          else if( !isS && isM && !isL ){
  1698              moveAreaTopbarButtons( 'm' )
  1699          }
  1700          else if( !isS && !isM && isL ){
  1701              moveAreaTopbarButtons( 'l' )
  1702          }
  1703      }
  1704      function adjustEmptyTopbarContents(){
  1705          var buttons = Array.from( document.querySelectorAll( '.topbar-button > .topbar-content > .topbar-content-wrapper' ) );
  1706          // we have to reverse order to make sure to handle innermost areas first
  1707          buttons.reverse().forEach( function( wrapper ){
  1708              var button = getTopbarButtonParent( wrapper );
  1709              if( button ){
  1710                  var isEmpty = true;
  1711                  var area = wrapper.querySelector( ':scope > .topbar-area');
  1712                  if( area ){
  1713                      // if it's an area, we have to check each contained button
  1714                      // manually for its display property
  1715                      var areabuttons = area.querySelectorAll( ':scope > .topbar-button' );
  1716                      isEmpty = true;
  1717                      areabuttons.forEach( function( ab ){
  1718                          if( ab.style.display != 'none' ){
  1719                              isEmpty = false;
  1720                          }
  1721                      })
  1722                  }
  1723                  else{
  1724                      var clone = wrapper.cloneNode( true );
  1725                      var irrelevant = clone.querySelectorAll( "div.ps__rail-x, div.ps__rail-y" );
  1726                      irrelevant.forEach(function( e ) {
  1727                          e.parentNode.removeChild( e );
  1728                      });
  1729                      isEmpty = !clone.innerHTML.trim();
  1730                  }
  1731                  button.querySelector( 'button' ).disabled = isEmpty;
  1732                  button.style.display = isEmpty && button.dataset.contentEmpty == 'hide' ? 'none' : 'inline-block';
  1733              }
  1734          })
  1735      }
  1736      function setWidthS(e){ body.classList[ e.matches ? "add" : "remove" ]( 'menu-width-s' ); }
  1737      function setWidthM(e){ body.classList[ e.matches ? "add" : "remove" ]( 'menu-width-m' ); }
  1738      function setWidthL(e){ body.classList[ e.matches ? "add" : "remove" ]( 'menu-width-l' ); }
  1739      function onWidthChange( setWidth, e ){
  1740          setWidth( e );
  1741          moveTopbarButtons();
  1742          adjustEmptyTopbarContents();
  1743      }
  1744      var mqs = window.matchMedia( 'only screen and (max-width: 47.999rem)' );
  1745      mqs.addEventListener( 'change', onWidthChange.bind( null, setWidthS ) );
  1746      var mqm = window.matchMedia( 'only screen and (min-width: 48rem) and (max-width: 59.999rem)' );
  1747      mqm.addEventListener( 'change', onWidthChange.bind( null, setWidthM ) );
  1748      var mql = window.matchMedia( 'only screen and (min-width: 60rem)' );
  1749      mql.addEventListener( 'change', onWidthChange.bind( null, setWidthL ) );
  1750  
  1751      addTopbarButtonInfos();
  1752      setWidthS( mqs );
  1753      setWidthM( mqm );
  1754      setWidthL( mql );
  1755      moveTopbarButtons();
  1756      adjustEmptyTopbarContents();
  1757  })();
  1758  
  1759  (function(){
  1760      var body = document.querySelector( 'body' );
  1761      function setWidth(e){ body.classList[ e.matches ? "add" : "remove" ]( 'main-width-max' ); }
  1762      function onWidthChange( setWidth, e ){
  1763          setWidth( e );
  1764      }
  1765      var width = variants.getColorValue( 'MAIN-WIDTH-MAX' );
  1766      var mqm = window.matchMedia( 'screen and ( min-width: ' + width + ')' );
  1767      mqm.addEventListener( 'change', onWidthChange.bind( null, setWidth ) );
  1768      setWidth( mqm );
  1769  })();