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.

571 lines
16 KiB

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