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