зеркало из https://github.com/golang/tools.git
689 строки
20 KiB
JavaScript
689 строки
20 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;
|
|
}
|
|
|
|
// For search, we send the toc precomputed from server-side.
|
|
// TODO: Ideally, this should always be precomputed for all pages, but then
|
|
// we need to do HTML parsing on the server-side.
|
|
if (location.pathname === '/search') {
|
|
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');
|
|
|
|
// Hide inline playground if we click somewhere on the page.
|
|
// This is needed in mobile devices, where the "Play" button
|
|
// is not clickable once the playground opens up.
|
|
$('#page').click(function() {
|
|
if (button.hasClass('active')) {
|
|
button.click();
|
|
}
|
|
});
|
|
}
|
|
|
|
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;
|
|
}
|
|
})();
|