mirror of
https://github.com/golang/go
synced 2024-11-05 22:26:10 -07:00
0e57ebad1d
The code as written would use JavaScript to show the "Play" button in the menu when the Playground is enabled, making the button appear after the page first renders, causing a visible "bounce" in the menu. This change checks the condition (whether this is not the front page, or "wide") in the template code, instead of with JavaScript after the page has loaded. Change-Id: Iccf0a344116372f3207158793be8c7734e67a4ae Reviewed-on: https://go-review.googlesource.com/c/140798 Reviewed-by: Andrew Bonventre <andybons@golang.org>
626 lines
17 KiB
JavaScript
626 lines
17 KiB
JavaScript
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
/* A little code to ease navigation of these documents.
|
|
*
|
|
* On window load we:
|
|
* + Generate a table of contents (generateTOC)
|
|
* + Bind foldable sections (bindToggles)
|
|
* + Bind links to foldable sections (bindToggleLinks)
|
|
*/
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// Mobile-friendly topbar menu
|
|
$(function() {
|
|
var menu = $('#menu');
|
|
var menuButton = $('#menu-button');
|
|
var menuButtonArrow = $('#menu-button-arrow');
|
|
menuButton.click(function(event) {
|
|
menu.toggleClass('menu-visible');
|
|
menuButtonArrow.toggleClass('vertical-flip');
|
|
event.preventDefault();
|
|
return false;
|
|
});
|
|
});
|
|
|
|
/* Generates a table of contents: looks for h2 and h3 elements and generates
|
|
* links. "Decorates" the element with id=="nav" with this table of contents.
|
|
*/
|
|
function generateTOC() {
|
|
if ($('#manual-nav').length > 0) {
|
|
return;
|
|
}
|
|
|
|
var nav = $('#nav');
|
|
if (nav.length === 0) {
|
|
return;
|
|
}
|
|
|
|
var toc_items = [];
|
|
$(nav).nextAll('h2, h3').each(function() {
|
|
var node = this;
|
|
if (node.id == '')
|
|
node.id = 'tmp_' + toc_items.length;
|
|
var link = $('<a/>').attr('href', '#' + node.id).text($(node).text());
|
|
var item;
|
|
if ($(node).is('h2')) {
|
|
item = $('<dt/>');
|
|
} else { // h3
|
|
item = $('<dd class="indent"/>');
|
|
}
|
|
item.append(link);
|
|
toc_items.push(item);
|
|
});
|
|
if (toc_items.length <= 1) {
|
|
return;
|
|
}
|
|
|
|
var dl1 = $('<dl/>');
|
|
var dl2 = $('<dl/>');
|
|
|
|
var split_index = (toc_items.length / 2) + 1;
|
|
if (split_index < 8) {
|
|
split_index = toc_items.length;
|
|
}
|
|
for (var i = 0; i < split_index; i++) {
|
|
dl1.append(toc_items[i]);
|
|
}
|
|
for (/* keep using i */; i < toc_items.length; i++) {
|
|
dl2.append(toc_items[i]);
|
|
}
|
|
|
|
var tocTable = $('<table class="unruled"/>').appendTo(nav);
|
|
var tocBody = $('<tbody/>').appendTo(tocTable);
|
|
var tocRow = $('<tr/>').appendTo(tocBody);
|
|
|
|
// 1st column
|
|
$('<td class="first"/>').appendTo(tocRow).append(dl1);
|
|
// 2nd column
|
|
$('<td/>').appendTo(tocRow).append(dl2);
|
|
}
|
|
|
|
function bindToggle(el) {
|
|
$('.toggleButton', el).click(function() {
|
|
if ($(this).closest(".toggle, .toggleVisible")[0] != el) {
|
|
// Only trigger the closest toggle header.
|
|
return;
|
|
}
|
|
|
|
if ($(el).is('.toggle')) {
|
|
$(el).addClass('toggleVisible').removeClass('toggle');
|
|
} else {
|
|
$(el).addClass('toggle').removeClass('toggleVisible');
|
|
}
|
|
});
|
|
}
|
|
|
|
function bindToggles(selector) {
|
|
$(selector).each(function(i, el) {
|
|
bindToggle(el);
|
|
});
|
|
}
|
|
|
|
function bindToggleLink(el, prefix) {
|
|
$(el).click(function() {
|
|
var href = $(el).attr('href');
|
|
var i = href.indexOf('#'+prefix);
|
|
if (i < 0) {
|
|
return;
|
|
}
|
|
var id = '#' + prefix + href.slice(i+1+prefix.length);
|
|
if ($(id).is('.toggle')) {
|
|
$(id).find('.toggleButton').first().click();
|
|
}
|
|
});
|
|
}
|
|
function bindToggleLinks(selector, prefix) {
|
|
$(selector).each(function(i, el) {
|
|
bindToggleLink(el, prefix);
|
|
});
|
|
}
|
|
|
|
function setupDropdownPlayground() {
|
|
if (!$('#page').is('.wide')) {
|
|
return; // don't show on front page
|
|
}
|
|
var button = $('#playgroundButton');
|
|
var div = $('#playground');
|
|
var setup = false;
|
|
button.toggle(function() {
|
|
button.addClass('active');
|
|
div.show();
|
|
if (setup) {
|
|
return;
|
|
}
|
|
setup = true;
|
|
playground({
|
|
'codeEl': $('.code', div),
|
|
'outputEl': $('.output', div),
|
|
'runEl': $('.run', div),
|
|
'fmtEl': $('.fmt', div),
|
|
'shareEl': $('.share', div),
|
|
'shareRedirect': '//play.golang.org/p/'
|
|
});
|
|
},
|
|
function() {
|
|
button.removeClass('active');
|
|
div.hide();
|
|
});
|
|
$('#menu').css('min-width', '+=60');
|
|
}
|
|
|
|
function setupInlinePlayground() {
|
|
'use strict';
|
|
// Set up playground when each element is toggled.
|
|
$('div.play').each(function (i, el) {
|
|
// Set up playground for this example.
|
|
var setup = function() {
|
|
var code = $('.code', el);
|
|
playground({
|
|
'codeEl': code,
|
|
'outputEl': $('.output', el),
|
|
'runEl': $('.run', el),
|
|
'fmtEl': $('.fmt', el),
|
|
'shareEl': $('.share', el),
|
|
'shareRedirect': '//play.golang.org/p/'
|
|
});
|
|
|
|
// Make the code textarea resize to fit content.
|
|
var resize = function() {
|
|
code.height(0);
|
|
var h = code[0].scrollHeight;
|
|
code.height(h+20); // minimize bouncing.
|
|
code.closest('.input').height(h);
|
|
};
|
|
code.on('keydown', resize);
|
|
code.on('keyup', resize);
|
|
code.keyup(); // resize now.
|
|
};
|
|
|
|
// If example already visible, set up playground now.
|
|
if ($(el).is(':visible')) {
|
|
setup();
|
|
return;
|
|
}
|
|
|
|
// Otherwise, set up playground when example is expanded.
|
|
var built = false;
|
|
$(el).closest('.toggle').click(function() {
|
|
// Only set up once.
|
|
if (!built) {
|
|
setup();
|
|
built = true;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// fixFocus tries to put focus to div#page so that keyboard navigation works.
|
|
function fixFocus() {
|
|
var page = $('div#page');
|
|
var topbar = $('div#topbar');
|
|
page.css('outline', 0); // disable outline when focused
|
|
page.attr('tabindex', -1); // and set tabindex so that it is focusable
|
|
$(window).resize(function (evt) {
|
|
// only focus page when the topbar is at fixed position (that is, it's in
|
|
// front of page, and keyboard event will go to the former by default.)
|
|
// by focusing page, keyboard event will go to page so that up/down arrow,
|
|
// space, etc. will work as expected.
|
|
if (topbar.css('position') == "fixed")
|
|
page.focus();
|
|
}).resize();
|
|
}
|
|
|
|
function toggleHash() {
|
|
var id = window.location.hash.substring(1);
|
|
// Open all of the toggles for a particular hash.
|
|
var els = $(
|
|
document.getElementById(id),
|
|
$('a[name]').filter(function() {
|
|
return $(this).attr('name') == id;
|
|
})
|
|
);
|
|
|
|
while (els.length) {
|
|
for (var i = 0; i < els.length; i++) {
|
|
var el = $(els[i]);
|
|
if (el.is('.toggle')) {
|
|
el.find('.toggleButton').first().click();
|
|
}
|
|
}
|
|
els = el.parent();
|
|
}
|
|
}
|
|
|
|
function personalizeInstallInstructions() {
|
|
var prefix = '?download=';
|
|
var s = window.location.search;
|
|
if (s.indexOf(prefix) != 0) {
|
|
// No 'download' query string; detect "test" instructions from User Agent.
|
|
if (navigator.platform.indexOf('Win') != -1) {
|
|
$('.testUnix').hide();
|
|
$('.testWindows').show();
|
|
} else {
|
|
$('.testUnix').show();
|
|
$('.testWindows').hide();
|
|
}
|
|
return;
|
|
}
|
|
|
|
var filename = s.substr(prefix.length);
|
|
var filenameRE = /^go1\.\d+(\.\d+)?([a-z0-9]+)?\.([a-z0-9]+)(-[a-z0-9]+)?(-osx10\.[68])?\.([a-z.]+)$/;
|
|
var m = filenameRE.exec(filename);
|
|
if (!m) {
|
|
// Can't interpret file name; bail.
|
|
return;
|
|
}
|
|
$('.downloadFilename').text(filename);
|
|
$('.hideFromDownload').hide();
|
|
|
|
var os = m[3];
|
|
var ext = m[6];
|
|
if (ext != 'tar.gz') {
|
|
$('#tarballInstructions').hide();
|
|
}
|
|
if (os != 'darwin' || ext != 'pkg') {
|
|
$('#darwinPackageInstructions').hide();
|
|
}
|
|
if (os != 'windows') {
|
|
$('#windowsInstructions').hide();
|
|
$('.testUnix').show();
|
|
$('.testWindows').hide();
|
|
} else {
|
|
if (ext != 'msi') {
|
|
$('#windowsInstallerInstructions').hide();
|
|
}
|
|
if (ext != 'zip') {
|
|
$('#windowsZipInstructions').hide();
|
|
}
|
|
$('.testUnix').hide();
|
|
$('.testWindows').show();
|
|
}
|
|
|
|
var download = "https://dl.google.com/go/" + filename;
|
|
|
|
var message = $('<p class="downloading">'+
|
|
'Your download should begin shortly. '+
|
|
'If it does not, click <a>this link</a>.</p>');
|
|
message.find('a').attr('href', download);
|
|
message.insertAfter('#nav');
|
|
|
|
window.location = download;
|
|
}
|
|
|
|
function updateVersionTags() {
|
|
var v = window.goVersion;
|
|
if (/^go[0-9.]+$/.test(v)) {
|
|
$(".versionTag").empty().text(v);
|
|
$(".whereTag").hide();
|
|
}
|
|
}
|
|
|
|
function addPermalinks() {
|
|
function addPermalink(source, parent) {
|
|
var id = source.attr("id");
|
|
if (id == "" || id.indexOf("tmp_") === 0) {
|
|
// Auto-generated permalink.
|
|
return;
|
|
}
|
|
if (parent.find("> .permalink").length) {
|
|
// Already attached.
|
|
return;
|
|
}
|
|
parent.append(" ").append($("<a class='permalink'>¶</a>").attr("href", "#" + id));
|
|
}
|
|
|
|
$("#page .container").find("h2[id], h3[id]").each(function() {
|
|
var el = $(this);
|
|
addPermalink(el, el);
|
|
});
|
|
|
|
$("#page .container").find("dl[id]").each(function() {
|
|
var el = $(this);
|
|
// Add the anchor to the "dt" element.
|
|
addPermalink(el, el.find("> dt").first());
|
|
});
|
|
}
|
|
|
|
$(".js-expandAll").click(function() {
|
|
if ($(this).hasClass("collapsed")) {
|
|
toggleExamples('toggle');
|
|
$(this).text("(Collapse All)");
|
|
} else {
|
|
toggleExamples('toggleVisible');
|
|
$(this).text("(Expand All)");
|
|
}
|
|
$(this).toggleClass("collapsed")
|
|
});
|
|
|
|
function toggleExamples(className) {
|
|
// We need to explicitly iterate through divs starting with "example_"
|
|
// to avoid toggling Overview and Index collapsibles.
|
|
$("[id^='example_']").each(function() {
|
|
// Check for state and click it only if required.
|
|
if ($(this).hasClass(className)) {
|
|
$(this).find('.toggleButton').first().click();
|
|
}
|
|
});
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
generateTOC();
|
|
addPermalinks();
|
|
bindToggles(".toggle");
|
|
bindToggles(".toggleVisible");
|
|
bindToggleLinks(".exampleLink", "example_");
|
|
bindToggleLinks(".overviewLink", "");
|
|
bindToggleLinks(".examplesLink", "");
|
|
bindToggleLinks(".indexLink", "");
|
|
setupDropdownPlayground();
|
|
setupInlinePlayground();
|
|
fixFocus();
|
|
setupTypeInfo();
|
|
setupCallgraphs();
|
|
toggleHash();
|
|
personalizeInstallInstructions();
|
|
updateVersionTags();
|
|
|
|
// godoc.html defines window.initFuncs in the <head> tag, and root.html and
|
|
// codewalk.js push their on-page-ready functions to the list.
|
|
// We execute those functions here, to avoid loading jQuery until the page
|
|
// content is loaded.
|
|
for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i]();
|
|
});
|
|
|
|
// -- analysis ---------------------------------------------------------
|
|
|
|
// escapeHTML returns HTML for s, with metacharacters quoted.
|
|
// It is safe for use in both elements and attributes
|
|
// (unlike the "set innerText, read innerHTML" trick).
|
|
function escapeHTML(s) {
|
|
return s.replace(/&/g, '&').
|
|
replace(/\"/g, '"').
|
|
replace(/\'/g, ''').
|
|
replace(/</g, '<').
|
|
replace(/>/g, '>');
|
|
}
|
|
|
|
// makeAnchor returns HTML for an <a> element, given an anchorJSON object.
|
|
function makeAnchor(json) {
|
|
var html = escapeHTML(json.Text);
|
|
if (json.Href != "") {
|
|
html = "<a href='" + escapeHTML(json.Href) + "'>" + html + "</a>";
|
|
}
|
|
return html;
|
|
}
|
|
|
|
function showLowFrame(html) {
|
|
var lowframe = document.getElementById('lowframe');
|
|
lowframe.style.height = "200px";
|
|
lowframe.innerHTML = "<p style='text-align: left;'>" + html + "</p>\n" +
|
|
"<div onclick='hideLowFrame()' style='position: absolute; top: 0; right: 0; cursor: pointer;'>✘</div>"
|
|
};
|
|
|
|
document.hideLowFrame = function() {
|
|
var lowframe = document.getElementById('lowframe');
|
|
lowframe.style.height = "0px";
|
|
}
|
|
|
|
// onClickCallers is the onclick action for the 'func' tokens of a
|
|
// function declaration.
|
|
document.onClickCallers = function(index) {
|
|
var data = document.ANALYSIS_DATA[index]
|
|
if (data.Callers.length == 1 && data.Callers[0].Sites.length == 1) {
|
|
document.location = data.Callers[0].Sites[0].Href; // jump to sole caller
|
|
return;
|
|
}
|
|
|
|
var html = "Callers of <code>" + escapeHTML(data.Callee) + "</code>:<br/>\n";
|
|
for (var i = 0; i < data.Callers.length; i++) {
|
|
var caller = data.Callers[i];
|
|
html += "<code>" + escapeHTML(caller.Func) + "</code>";
|
|
var sites = caller.Sites;
|
|
if (sites != null && sites.length > 0) {
|
|
html += " at line ";
|
|
for (var j = 0; j < sites.length; j++) {
|
|
if (j > 0) {
|
|
html += ", ";
|
|
}
|
|
html += "<code>" + makeAnchor(sites[j]) + "</code>";
|
|
}
|
|
}
|
|
html += "<br/>\n";
|
|
}
|
|
showLowFrame(html);
|
|
};
|
|
|
|
// onClickCallees is the onclick action for the '(' token of a function call.
|
|
document.onClickCallees = function(index) {
|
|
var data = document.ANALYSIS_DATA[index]
|
|
if (data.Callees.length == 1) {
|
|
document.location = data.Callees[0].Href; // jump to sole callee
|
|
return;
|
|
}
|
|
|
|
var html = "Callees of this " + escapeHTML(data.Descr) + ":<br/>\n";
|
|
for (var i = 0; i < data.Callees.length; i++) {
|
|
html += "<code>" + makeAnchor(data.Callees[i]) + "</code><br/>\n";
|
|
}
|
|
showLowFrame(html);
|
|
};
|
|
|
|
// onClickTypeInfo is the onclick action for identifiers declaring a named type.
|
|
document.onClickTypeInfo = function(index) {
|
|
var data = document.ANALYSIS_DATA[index];
|
|
var html = "Type <code>" + data.Name + "</code>: " +
|
|
" <small>(size=" + data.Size + ", align=" + data.Align + ")</small><br/>\n";
|
|
html += implementsHTML(data);
|
|
html += methodsetHTML(data);
|
|
showLowFrame(html);
|
|
};
|
|
|
|
// implementsHTML returns HTML for the implements relation of the
|
|
// specified TypeInfoJSON value.
|
|
function implementsHTML(info) {
|
|
var html = "";
|
|
if (info.ImplGroups != null) {
|
|
for (var i = 0; i < info.ImplGroups.length; i++) {
|
|
var group = info.ImplGroups[i];
|
|
var x = "<code>" + escapeHTML(group.Descr) + "</code> ";
|
|
for (var j = 0; j < group.Facts.length; j++) {
|
|
var fact = group.Facts[j];
|
|
var y = "<code>" + makeAnchor(fact.Other) + "</code>";
|
|
if (fact.ByKind != null) {
|
|
html += escapeHTML(fact.ByKind) + " type " + y + " implements " + x;
|
|
} else {
|
|
html += x + " implements " + y;
|
|
}
|
|
html += "<br/>\n";
|
|
}
|
|
}
|
|
}
|
|
return html;
|
|
}
|
|
|
|
|
|
// methodsetHTML returns HTML for the methodset of the specified
|
|
// TypeInfoJSON value.
|
|
function methodsetHTML(info) {
|
|
var html = "";
|
|
if (info.Methods != null) {
|
|
for (var i = 0; i < info.Methods.length; i++) {
|
|
html += "<code>" + makeAnchor(info.Methods[i]) + "</code><br/>\n";
|
|
}
|
|
}
|
|
return html;
|
|
}
|
|
|
|
// onClickComm is the onclick action for channel "make" and "<-"
|
|
// send/receive tokens.
|
|
document.onClickComm = function(index) {
|
|
var ops = document.ANALYSIS_DATA[index].Ops
|
|
if (ops.length == 1) {
|
|
document.location = ops[0].Op.Href; // jump to sole element
|
|
return;
|
|
}
|
|
|
|
var html = "Operations on this channel:<br/>\n";
|
|
for (var i = 0; i < ops.length; i++) {
|
|
html += makeAnchor(ops[i].Op) + " by <code>" + escapeHTML(ops[i].Fn) + "</code><br/>\n";
|
|
}
|
|
if (ops.length == 0) {
|
|
html += "(none)<br/>\n";
|
|
}
|
|
showLowFrame(html);
|
|
};
|
|
|
|
$(window).load(function() {
|
|
// Scroll window so that first selection is visible.
|
|
// (This means we don't need to emit id='L%d' spans for each line.)
|
|
// TODO(adonovan): ideally, scroll it so that it's under the pointer,
|
|
// but I don't know how to get the pointer y coordinate.
|
|
var elts = document.getElementsByClassName("selection");
|
|
if (elts.length > 0) {
|
|
elts[0].scrollIntoView()
|
|
}
|
|
});
|
|
|
|
// setupTypeInfo populates the "Implements" and "Method set" toggle for
|
|
// each type in the package doc.
|
|
function setupTypeInfo() {
|
|
for (var i in document.ANALYSIS_DATA) {
|
|
var data = document.ANALYSIS_DATA[i];
|
|
|
|
var el = document.getElementById("implements-" + i);
|
|
if (el != null) {
|
|
// el != null => data is TypeInfoJSON.
|
|
if (data.ImplGroups != null) {
|
|
el.innerHTML = implementsHTML(data);
|
|
el.parentNode.parentNode.style.display = "block";
|
|
}
|
|
}
|
|
|
|
var el = document.getElementById("methodset-" + i);
|
|
if (el != null) {
|
|
// el != null => data is TypeInfoJSON.
|
|
if (data.Methods != null) {
|
|
el.innerHTML = methodsetHTML(data);
|
|
el.parentNode.parentNode.style.display = "block";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function setupCallgraphs() {
|
|
if (document.CALLGRAPH == null) {
|
|
return
|
|
}
|
|
document.getElementById("pkg-callgraph").style.display = "block";
|
|
|
|
var treeviews = document.getElementsByClassName("treeview");
|
|
for (var i = 0; i < treeviews.length; i++) {
|
|
var tree = treeviews[i];
|
|
if (tree.id == null || tree.id.indexOf("callgraph-") != 0) {
|
|
continue;
|
|
}
|
|
var id = tree.id.substring("callgraph-".length);
|
|
$(tree).treeview({collapsed: true, animated: "fast"});
|
|
document.cgAddChildren(tree, tree, [id]);
|
|
tree.parentNode.parentNode.style.display = "block";
|
|
}
|
|
}
|
|
|
|
document.cgAddChildren = function(tree, ul, indices) {
|
|
if (indices != null) {
|
|
for (var i = 0; i < indices.length; i++) {
|
|
var li = cgAddChild(tree, ul, document.CALLGRAPH[indices[i]]);
|
|
if (i == indices.length - 1) {
|
|
$(li).addClass("last");
|
|
}
|
|
}
|
|
}
|
|
$(tree).treeview({animated: "fast", add: ul});
|
|
}
|
|
|
|
// cgAddChild adds an <li> element for document.CALLGRAPH node cgn to
|
|
// the parent <ul> element ul. tree is the tree's root <ul> element.
|
|
function cgAddChild(tree, ul, cgn) {
|
|
var li = document.createElement("li");
|
|
ul.appendChild(li);
|
|
li.className = "closed";
|
|
|
|
var code = document.createElement("code");
|
|
|
|
if (cgn.Callees != null) {
|
|
$(li).addClass("expandable");
|
|
|
|
// Event handlers and innerHTML updates don't play nicely together,
|
|
// hence all this explicit DOM manipulation.
|
|
var hitarea = document.createElement("div");
|
|
hitarea.className = "hitarea expandable-hitarea";
|
|
li.appendChild(hitarea);
|
|
|
|
li.appendChild(code);
|
|
|
|
var childUL = document.createElement("ul");
|
|
li.appendChild(childUL);
|
|
childUL.setAttribute('style', "display: none;");
|
|
|
|
var onClick = function() {
|
|
document.cgAddChildren(tree, childUL, cgn.Callees);
|
|
hitarea.removeEventListener('click', onClick)
|
|
};
|
|
hitarea.addEventListener('click', onClick);
|
|
|
|
} else {
|
|
li.appendChild(code);
|
|
}
|
|
code.innerHTML += " " + makeAnchor(cgn.Func);
|
|
return li
|
|
}
|
|
|
|
})();
|