github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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'>¶</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, '&') 436 .replace(/\"/g, '"') 437 .replace(/\'/g, ''') 438 .replace(/</g, '<') 439 .replace(/>/g, '>'); 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 ' <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 += ' ' + makeAnchor(cgn.Func); 686 return li; 687 } 688 })();