--- /dev/null
+// 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;
+ }
+})();