You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

604 lines
17 KiB

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