golang.org/x/tools@v0.21.0/godoc/static/godocs.js (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /* A little code to ease navigation of these documents.
     6   *
     7   * On window load we:
     8   *  + Generate a table of contents (generateTOC)
     9   *  + Bind foldable sections (bindToggles)
    10   *  + Bind links to foldable sections (bindToggleLinks)
    11   */
    12  
    13  (function() {
    14    'use strict';
    15  
    16    // Mobile-friendly topbar menu
    17    $(function() {
    18      var menu = $('#menu');
    19      var menuButton = $('#menu-button');
    20      var menuButtonArrow = $('#menu-button-arrow');
    21      menuButton.click(function(event) {
    22        menu.toggleClass('menu-visible');
    23        menuButtonArrow.toggleClass('vertical-flip');
    24        event.preventDefault();
    25        return false;
    26      });
    27    });
    28  
    29    /* Generates a table of contents: looks for h2 and h3 elements and generates
    30     * links. "Decorates" the element with id=="nav" with this table of contents.
    31     */
    32    function generateTOC() {
    33      if ($('#manual-nav').length > 0) {
    34        return;
    35      }
    36  
    37      // For search, we send the toc precomputed from server-side.
    38      // TODO: Ideally, this should always be precomputed for all pages, but then
    39      // we need to do HTML parsing on the server-side.
    40      if (location.pathname === '/search') {
    41        return;
    42      }
    43  
    44      var nav = $('#nav');
    45      if (nav.length === 0) {
    46        return;
    47      }
    48  
    49      var toc_items = [];
    50      $(nav)
    51        .nextAll('h2, h3')
    52        .each(function() {
    53          var node = this;
    54          if (node.id == '') node.id = 'tmp_' + toc_items.length;
    55          var link = $('<a/>')
    56            .attr('href', '#' + node.id)
    57            .text($(node).text());
    58          var item;
    59          if ($(node).is('h2')) {
    60            item = $('<dt/>');
    61          } else {
    62            // h3
    63            item = $('<dd class="indent"/>');
    64          }
    65          item.append(link);
    66          toc_items.push(item);
    67        });
    68      if (toc_items.length <= 1) {
    69        return;
    70      }
    71      var dl1 = $('<dl/>');
    72      var dl2 = $('<dl/>');
    73  
    74      var split_index = toc_items.length / 2 + 1;
    75      if (split_index < 8) {
    76        split_index = toc_items.length;
    77      }
    78      for (var i = 0; i < split_index; i++) {
    79        dl1.append(toc_items[i]);
    80      }
    81      for (; /* keep using i */ i < toc_items.length; i++) {
    82        dl2.append(toc_items[i]);
    83      }
    84  
    85      var tocTable = $('<table class="unruled"/>').appendTo(nav);
    86      var tocBody = $('<tbody/>').appendTo(tocTable);
    87      var tocRow = $('<tr/>').appendTo(tocBody);
    88  
    89      // 1st column
    90      $('<td class="first"/>')
    91        .appendTo(tocRow)
    92        .append(dl1);
    93      // 2nd column
    94      $('<td/>')
    95        .appendTo(tocRow)
    96        .append(dl2);
    97    }
    98  
    99    function bindToggle(el) {
   100      $('.toggleButton', el).click(function() {
   101        if ($(this).closest('.toggle, .toggleVisible')[0] != el) {
   102          // Only trigger the closest toggle header.
   103          return;
   104        }
   105  
   106        if ($(el).is('.toggle')) {
   107          $(el)
   108            .addClass('toggleVisible')
   109            .removeClass('toggle');
   110        } else {
   111          $(el)
   112            .addClass('toggle')
   113            .removeClass('toggleVisible');
   114        }
   115      });
   116    }
   117  
   118    function bindToggles(selector) {
   119      $(selector).each(function(i, el) {
   120        bindToggle(el);
   121      });
   122    }
   123  
   124    function bindToggleLink(el, prefix) {
   125      $(el).click(function() {
   126        var href = $(el).attr('href');
   127        var i = href.indexOf('#' + prefix);
   128        if (i < 0) {
   129          return;
   130        }
   131        var id = '#' + prefix + href.slice(i + 1 + prefix.length);
   132        if ($(id).is('.toggle')) {
   133          $(id)
   134            .find('.toggleButton')
   135            .first()
   136            .click();
   137        }
   138      });
   139    }
   140    function bindToggleLinks(selector, prefix) {
   141      $(selector).each(function(i, el) {
   142        bindToggleLink(el, prefix);
   143      });
   144    }
   145  
   146    function setupDropdownPlayground() {
   147      if (!$('#page').is('.wide')) {
   148        return; // don't show on front page
   149      }
   150      var button = $('#playgroundButton');
   151      var div = $('#playground');
   152      var setup = false;
   153      button.toggle(
   154        function() {
   155          button.addClass('active');
   156          div.show();
   157          if (setup) {
   158            return;
   159          }
   160          setup = true;
   161          playground({
   162            codeEl: $('.code', div),
   163            outputEl: $('.output', div),
   164            runEl: $('.run', div),
   165            fmtEl: $('.fmt', div),
   166            shareEl: $('.share', div),
   167            shareRedirect: '//play.golang.org/p/',
   168          });
   169        },
   170        function() {
   171          button.removeClass('active');
   172          div.hide();
   173        }
   174      );
   175      $('#menu').css('min-width', '+=60');
   176  
   177      // Hide inline playground if we click somewhere on the page.
   178      // This is needed in mobile devices, where the "Play" button
   179      // is not clickable once the playground opens up.
   180      $('#page').click(function() {
   181        if (button.hasClass('active')) {
   182          button.click();
   183        }
   184      });
   185    }
   186  
   187    function setupInlinePlayground() {
   188      'use strict';
   189      // Set up playground when each element is toggled.
   190      $('div.play').each(function(i, el) {
   191        // Set up playground for this example.
   192        var setup = function() {
   193          var code = $('.code', el);
   194          playground({
   195            codeEl: code,
   196            outputEl: $('.output', el),
   197            runEl: $('.run', el),
   198            fmtEl: $('.fmt', el),
   199            shareEl: $('.share', el),
   200            shareRedirect: '//play.golang.org/p/',
   201          });
   202  
   203          // Make the code textarea resize to fit content.
   204          var resize = function() {
   205            code.height(0);
   206            var h = code[0].scrollHeight;
   207            code.height(h + 20); // minimize bouncing.
   208            code.closest('.input').height(h);
   209          };
   210          code.on('keydown', resize);
   211          code.on('keyup', resize);
   212          code.keyup(); // resize now.
   213        };
   214  
   215        // If example already visible, set up playground now.
   216        if ($(el).is(':visible')) {
   217          setup();
   218          return;
   219        }
   220  
   221        // Otherwise, set up playground when example is expanded.
   222        var built = false;
   223        $(el)
   224          .closest('.toggle')
   225          .click(function() {
   226            // Only set up once.
   227            if (!built) {
   228              setup();
   229              built = true;
   230            }
   231          });
   232      });
   233    }
   234  
   235    // fixFocus tries to put focus to div#page so that keyboard navigation works.
   236    function fixFocus() {
   237      var page = $('div#page');
   238      var topbar = $('div#topbar');
   239      page.css('outline', 0); // disable outline when focused
   240      page.attr('tabindex', -1); // and set tabindex so that it is focusable
   241      $(window)
   242        .resize(function(evt) {
   243          // only focus page when the topbar is at fixed position (that is, it's in
   244          // front of page, and keyboard event will go to the former by default.)
   245          // by focusing page, keyboard event will go to page so that up/down arrow,
   246          // space, etc. will work as expected.
   247          if (topbar.css('position') == 'fixed') page.focus();
   248        })
   249        .resize();
   250    }
   251  
   252    function toggleHash() {
   253      var id = window.location.hash.substring(1);
   254      // Open all of the toggles for a particular hash.
   255      var els = $(
   256        document.getElementById(id),
   257        $('a[name]').filter(function() {
   258          return $(this).attr('name') == id;
   259        })
   260      );
   261  
   262      while (els.length) {
   263        for (var i = 0; i < els.length; i++) {
   264          var el = $(els[i]);
   265          if (el.is('.toggle')) {
   266            el.find('.toggleButton')
   267              .first()
   268              .click();
   269          }
   270        }
   271        els = el.parent();
   272      }
   273    }
   274  
   275    function personalizeInstallInstructions() {
   276      var prefix = '?download=';
   277      var s = window.location.search;
   278      if (s.indexOf(prefix) != 0) {
   279        // No 'download' query string; detect "test" instructions from User Agent.
   280        if (navigator.platform.indexOf('Win') != -1) {
   281          $('.testUnix').hide();
   282          $('.testWindows').show();
   283        } else {
   284          $('.testUnix').show();
   285          $('.testWindows').hide();
   286        }
   287        return;
   288      }
   289  
   290      var filename = s.substr(prefix.length);
   291      var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
   292      var m = filenameRE.exec(filename);
   293      if (!m) {
   294        // Can't interpret file name; bail.
   295        return;
   296      }
   297      $('.downloadFilename').text(filename);
   298      $('.hideFromDownload').hide();
   299  
   300      var os = m[3];
   301      var ext = m[6];
   302      if (ext != 'tar.gz') {
   303        $('#tarballInstructions').hide();
   304      }
   305      if (os != 'darwin' || ext != 'pkg') {
   306        $('#darwinPackageInstructions').hide();
   307      }
   308      if (os != 'windows') {
   309        $('#windowsInstructions').hide();
   310        $('.testUnix').show();
   311        $('.testWindows').hide();
   312      } else {
   313        if (ext != 'msi') {
   314          $('#windowsInstallerInstructions').hide();
   315        }
   316        if (ext != 'zip') {
   317          $('#windowsZipInstructions').hide();
   318        }
   319        $('.testUnix').hide();
   320        $('.testWindows').show();
   321      }
   322  
   323      var download = 'https://dl.google.com/go/' + filename;
   324  
   325      var message = $(
   326        '<p class="downloading">' +
   327          'Your download should begin shortly. ' +
   328          'If it does not, click <a>this link</a>.</p>'
   329      );
   330      message.find('a').attr('href', download);
   331      message.insertAfter('#nav');
   332  
   333      window.location = download;
   334    }
   335  
   336    function updateVersionTags() {
   337      var v = window.goVersion;
   338      if (/^go[0-9.]+$/.test(v)) {
   339        $('.versionTag')
   340          .empty()
   341          .text(v);
   342        $('.whereTag').hide();
   343      }
   344    }
   345  
   346    function addPermalinks() {
   347      function addPermalink(source, parent) {
   348        var id = source.attr('id');
   349        if (id == '' || id.indexOf('tmp_') === 0) {
   350          // Auto-generated permalink.
   351          return;
   352        }
   353        if (parent.find('> .permalink').length) {
   354          // Already attached.
   355          return;
   356        }
   357        parent
   358          .append(' ')
   359          .append($("<a class='permalink'>&#xb6;</a>").attr('href', '#' + id));
   360      }
   361  
   362      $('#page .container')
   363        .find('h2[id], h3[id]')
   364        .each(function() {
   365          var el = $(this);
   366          addPermalink(el, el);
   367        });
   368  
   369      $('#page .container')
   370        .find('dl[id]')
   371        .each(function() {
   372          var el = $(this);
   373          // Add the anchor to the "dt" element.
   374          addPermalink(el, el.find('> dt').first());
   375        });
   376    }
   377  
   378    $('.js-expandAll').click(function() {
   379      if ($(this).hasClass('collapsed')) {
   380        toggleExamples('toggle');
   381        $(this).text('(Collapse All)');
   382      } else {
   383        toggleExamples('toggleVisible');
   384        $(this).text('(Expand All)');
   385      }
   386      $(this).toggleClass('collapsed');
   387    });
   388  
   389    function toggleExamples(className) {
   390      // We need to explicitly iterate through divs starting with "example_"
   391      // to avoid toggling Overview and Index collapsibles.
   392      $("[id^='example_']").each(function() {
   393        // Check for state and click it only if required.
   394        if ($(this).hasClass(className)) {
   395          $(this)
   396            .find('.toggleButton')
   397            .first()
   398            .click();
   399        }
   400      });
   401    }
   402  
   403    $(document).ready(function() {
   404      generateTOC();
   405      addPermalinks();
   406      bindToggles('.toggle');
   407      bindToggles('.toggleVisible');
   408      bindToggleLinks('.exampleLink', 'example_');
   409      bindToggleLinks('.overviewLink', '');
   410      bindToggleLinks('.examplesLink', '');
   411      bindToggleLinks('.indexLink', '');
   412      setupDropdownPlayground();
   413      setupInlinePlayground();
   414      fixFocus();
   415      setupTypeInfo();
   416      setupCallgraphs();
   417      toggleHash();
   418      personalizeInstallInstructions();
   419      updateVersionTags();
   420  
   421      // godoc.html defines window.initFuncs in the <head> tag, and root.html and
   422      // codewalk.js push their on-page-ready functions to the list.
   423      // We execute those functions here, to avoid loading jQuery until the page
   424      // content is loaded.
   425      for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i]();
   426    });
   427  
   428    // -- analysis ---------------------------------------------------------
   429  
   430    // escapeHTML returns HTML for s, with metacharacters quoted.
   431    // It is safe for use in both elements and attributes
   432    // (unlike the "set innerText, read innerHTML" trick).
   433    function escapeHTML(s) {
   434      return s
   435        .replace(/&/g, '&amp;')
   436        .replace(/\"/g, '&quot;')
   437        .replace(/\'/g, '&#39;')
   438        .replace(/</g, '&lt;')
   439        .replace(/>/g, '&gt;');
   440    }
   441  
   442    // makeAnchor returns HTML for an <a> element, given an anchorJSON object.
   443    function makeAnchor(json) {
   444      var html = escapeHTML(json.Text);
   445      if (json.Href != '') {
   446        html = "<a href='" + escapeHTML(json.Href) + "'>" + html + '</a>';
   447      }
   448      return html;
   449    }
   450  
   451    function showLowFrame(html) {
   452      var lowframe = document.getElementById('lowframe');
   453      lowframe.style.height = '200px';
   454      lowframe.innerHTML =
   455        "<p style='text-align: left;'>" +
   456        html +
   457        '</p>\n' +
   458        "<div onclick='hideLowFrame()' style='position: absolute; top: 0; right: 0; cursor: pointer;'>✘</div>";
   459    }
   460  
   461    document.hideLowFrame = function() {
   462      var lowframe = document.getElementById('lowframe');
   463      lowframe.style.height = '0px';
   464    };
   465  
   466    // onClickCallers is the onclick action for the 'func' tokens of a
   467    // function declaration.
   468    document.onClickCallers = function(index) {
   469      var data = document.ANALYSIS_DATA[index];
   470      if (data.Callers.length == 1 && data.Callers[0].Sites.length == 1) {
   471        document.location = data.Callers[0].Sites[0].Href; // jump to sole caller
   472        return;
   473      }
   474  
   475      var html =
   476        'Callers of <code>' + escapeHTML(data.Callee) + '</code>:<br/>\n';
   477      for (var i = 0; i < data.Callers.length; i++) {
   478        var caller = data.Callers[i];
   479        html += '<code>' + escapeHTML(caller.Func) + '</code>';
   480        var sites = caller.Sites;
   481        if (sites != null && sites.length > 0) {
   482          html += ' at line ';
   483          for (var j = 0; j < sites.length; j++) {
   484            if (j > 0) {
   485              html += ', ';
   486            }
   487            html += '<code>' + makeAnchor(sites[j]) + '</code>';
   488          }
   489        }
   490        html += '<br/>\n';
   491      }
   492      showLowFrame(html);
   493    };
   494  
   495    // onClickCallees is the onclick action for the '(' token of a function call.
   496    document.onClickCallees = function(index) {
   497      var data = document.ANALYSIS_DATA[index];
   498      if (data.Callees.length == 1) {
   499        document.location = data.Callees[0].Href; // jump to sole callee
   500        return;
   501      }
   502  
   503      var html = 'Callees of this ' + escapeHTML(data.Descr) + ':<br/>\n';
   504      for (var i = 0; i < data.Callees.length; i++) {
   505        html += '<code>' + makeAnchor(data.Callees[i]) + '</code><br/>\n';
   506      }
   507      showLowFrame(html);
   508    };
   509  
   510    // onClickTypeInfo is the onclick action for identifiers declaring a named type.
   511    document.onClickTypeInfo = function(index) {
   512      var data = document.ANALYSIS_DATA[index];
   513      var html =
   514        'Type <code>' +
   515        data.Name +
   516        '</code>: ' +
   517        '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<small>(size=' +
   518        data.Size +
   519        ', align=' +
   520        data.Align +
   521        ')</small><br/>\n';
   522      html += implementsHTML(data);
   523      html += methodsetHTML(data);
   524      showLowFrame(html);
   525    };
   526  
   527    // implementsHTML returns HTML for the implements relation of the
   528    // specified TypeInfoJSON value.
   529    function implementsHTML(info) {
   530      var html = '';
   531      if (info.ImplGroups != null) {
   532        for (var i = 0; i < info.ImplGroups.length; i++) {
   533          var group = info.ImplGroups[i];
   534          var x = '<code>' + escapeHTML(group.Descr) + '</code> ';
   535          for (var j = 0; j < group.Facts.length; j++) {
   536            var fact = group.Facts[j];
   537            var y = '<code>' + makeAnchor(fact.Other) + '</code>';
   538            if (fact.ByKind != null) {
   539              html += escapeHTML(fact.ByKind) + ' type ' + y + ' implements ' + x;
   540            } else {
   541              html += x + ' implements ' + y;
   542            }
   543            html += '<br/>\n';
   544          }
   545        }
   546      }
   547      return html;
   548    }
   549  
   550    // methodsetHTML returns HTML for the methodset of the specified
   551    // TypeInfoJSON value.
   552    function methodsetHTML(info) {
   553      var html = '';
   554      if (info.Methods != null) {
   555        for (var i = 0; i < info.Methods.length; i++) {
   556          html += '<code>' + makeAnchor(info.Methods[i]) + '</code><br/>\n';
   557        }
   558      }
   559      return html;
   560    }
   561  
   562    // onClickComm is the onclick action for channel "make" and "<-"
   563    // send/receive tokens.
   564    document.onClickComm = function(index) {
   565      var ops = document.ANALYSIS_DATA[index].Ops;
   566      if (ops.length == 1) {
   567        document.location = ops[0].Op.Href; // jump to sole element
   568        return;
   569      }
   570  
   571      var html = 'Operations on this channel:<br/>\n';
   572      for (var i = 0; i < ops.length; i++) {
   573        html +=
   574          makeAnchor(ops[i].Op) +
   575          ' by <code>' +
   576          escapeHTML(ops[i].Fn) +
   577          '</code><br/>\n';
   578      }
   579      if (ops.length == 0) {
   580        html += '(none)<br/>\n';
   581      }
   582      showLowFrame(html);
   583    };
   584  
   585    $(window).load(function() {
   586      // Scroll window so that first selection is visible.
   587      // (This means we don't need to emit id='L%d' spans for each line.)
   588      // TODO(adonovan): ideally, scroll it so that it's under the pointer,
   589      // but I don't know how to get the pointer y coordinate.
   590      var elts = document.getElementsByClassName('selection');
   591      if (elts.length > 0) {
   592        elts[0].scrollIntoView();
   593      }
   594    });
   595  
   596    // setupTypeInfo populates the "Implements" and "Method set" toggle for
   597    // each type in the package doc.
   598    function setupTypeInfo() {
   599      for (var i in document.ANALYSIS_DATA) {
   600        var data = document.ANALYSIS_DATA[i];
   601  
   602        var el = document.getElementById('implements-' + i);
   603        if (el != null) {
   604          // el != null => data is TypeInfoJSON.
   605          if (data.ImplGroups != null) {
   606            el.innerHTML = implementsHTML(data);
   607            el.parentNode.parentNode.style.display = 'block';
   608          }
   609        }
   610  
   611        var el = document.getElementById('methodset-' + i);
   612        if (el != null) {
   613          // el != null => data is TypeInfoJSON.
   614          if (data.Methods != null) {
   615            el.innerHTML = methodsetHTML(data);
   616            el.parentNode.parentNode.style.display = 'block';
   617          }
   618        }
   619      }
   620    }
   621  
   622    function setupCallgraphs() {
   623      if (document.CALLGRAPH == null) {
   624        return;
   625      }
   626      document.getElementById('pkg-callgraph').style.display = 'block';
   627  
   628      var treeviews = document.getElementsByClassName('treeview');
   629      for (var i = 0; i < treeviews.length; i++) {
   630        var tree = treeviews[i];
   631        if (tree.id == null || tree.id.indexOf('callgraph-') != 0) {
   632          continue;
   633        }
   634        var id = tree.id.substring('callgraph-'.length);
   635        $(tree).treeview({ collapsed: true, animated: 'fast' });
   636        document.cgAddChildren(tree, tree, [id]);
   637        tree.parentNode.parentNode.style.display = 'block';
   638      }
   639    }
   640  
   641    document.cgAddChildren = function(tree, ul, indices) {
   642      if (indices != null) {
   643        for (var i = 0; i < indices.length; i++) {
   644          var li = cgAddChild(tree, ul, document.CALLGRAPH[indices[i]]);
   645          if (i == indices.length - 1) {
   646            $(li).addClass('last');
   647          }
   648        }
   649      }
   650      $(tree).treeview({ animated: 'fast', add: ul });
   651    };
   652  
   653    // cgAddChild adds an <li> element for document.CALLGRAPH node cgn to
   654    // the parent <ul> element ul. tree is the tree's root <ul> element.
   655    function cgAddChild(tree, ul, cgn) {
   656      var li = document.createElement('li');
   657      ul.appendChild(li);
   658      li.className = 'closed';
   659  
   660      var code = document.createElement('code');
   661  
   662      if (cgn.Callees != null) {
   663        $(li).addClass('expandable');
   664  
   665        // Event handlers and innerHTML updates don't play nicely together,
   666        // hence all this explicit DOM manipulation.
   667        var hitarea = document.createElement('div');
   668        hitarea.className = 'hitarea expandable-hitarea';
   669        li.appendChild(hitarea);
   670  
   671        li.appendChild(code);
   672  
   673        var childUL = document.createElement('ul');
   674        li.appendChild(childUL);
   675        childUL.setAttribute('style', 'display: none;');
   676  
   677        var onClick = function() {
   678          document.cgAddChildren(tree, childUL, cgn.Callees);
   679          hitarea.removeEventListener('click', onClick);
   680        };
   681        hitarea.addEventListener('click', onClick);
   682      } else {
   683        li.appendChild(code);
   684      }
   685      code.innerHTML += '&nbsp;' + makeAnchor(cgn.Func);
   686      return li;
   687    }
   688  })();