master
Raw Download raw file
  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, '&amp;').
275             replace(/\"/g, '&quot;').
276             replace(/\'/g, '&#39;').
277             replace(/</g, '&lt;').
278             replace(/>/g, '&gt;');
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  "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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 += "&nbsp;" + makeAnchor(cgn.Func);
512   return li
513}
514
515})();