OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 /** |
| 6 * Adds toggle controls to the sidebar list. |
| 7 * |
| 8 * Controls are inserted as the first children of list items in the sidebar |
| 9 * which contain only text (not a link). Handlers are set up so that when a |
| 10 * toggle control is clicked, any <ul> elements who are siblings of the |
| 11 * control are hidden/revealed as appropriate given the control's state. |
| 12 * |
| 13 * If a list item possesses the class 'selected' its ancestor <ul> is |
| 14 * revealed by default (it represents the current page). |
| 15 */ |
5 (function() { | 16 (function() { |
6 /** | 17 var sidebar = document.getElementById('gc-sidebar'); |
7 * Toggles the display of nodes given the status of their associated controls. | 18 if (!sidebar) |
8 * | 19 return; |
9 * For each node passed to this function, check to see if a toggle has been | 20 |
10 * inserted into the node's parent. If yes, change the state of the toggle | 21 // Figure out which link matches the current page so it can be styled |
11 * and hide/reveal the node as needed. | 22 // differently. |
12 * | 23 var pathParts = document.location.pathname.replace(/\/+$/, '').split('/') |
13 * @param {NodeList|Node|Array.<Node>} Nodes to operate on. | 24 Array.prototype.forEach.call(sidebar.getElementsByTagName('a'), |
14 */ | 25 function(link) { |
15 function toggleList(list) { | 26 if (link.getAttribute('href') != pathParts[pathParts.length - 1]) |
16 if (typeof list.length != 'number') { | 27 return; |
17 list = Array(list); | 28 link.className = 'selected'; |
| 29 link.removeAttribute('href'); |
| 30 }); |
| 31 |
| 32 // Go through the items on the sidebar and add toggles. |
| 33 Array.prototype.forEach.call(sidebar.querySelectorAll('[toggleable]'), |
| 34 function(toggleable) { |
| 35 var buttonContent = toggleable.previousElementSibling; |
| 36 if (!buttonContent) { |
| 37 console.warn('Cannot toggle %s, no previous sibling', toggleable); |
| 38 return; |
18 } | 39 } |
19 | 40 |
20 for (var i = 0; i < list.length; i++) { | 41 var button = document.createElement('a'); |
21 var toggle = list[i].parentNode && | 42 button.className = 'button'; |
22 list[i].parentNode.firstChild; | 43 var toggleIndicator = document.createElement('div'); |
23 if (toggle && toggle.className.substring(0, 6) == 'toggle') { | 44 toggleIndicator.className = 'toggleIndicator'; |
24 var visible = toggle.className == 'toggle'; | 45 |
25 list[i].style.display = visible ? 'block' : 'none'; | 46 var isToggled = false; |
26 toggle.className = visible ? 'toggle selected' : 'toggle'; | 47 function toggle() { |
| 48 if (isToggled) { |
| 49 toggleable.classList.add('hidden'); |
| 50 toggleIndicator.classList.remove('toggled'); |
| 51 } else { |
| 52 toggleable.classList.remove('hidden'); |
| 53 toggleIndicator.classList.add('toggled'); |
27 } | 54 } |
28 } | 55 isToggled = !isToggled; |
29 }; | |
30 | |
31 /** | |
32 * Reveals the hidden ancestor of the passed node, adjusts toggles as needed. | |
33 * | |
34 * @param {Node} node The node whose ancestor is a hidden toggleable element. | |
35 */ | |
36 function revealAncestor(node) { | |
37 while (node.parentNode) { | |
38 if (node.style.display == 'none') { | |
39 toggleList(node); | |
40 break; | |
41 } | |
42 node = node.parentNode; | |
43 } | |
44 }; | |
45 | |
46 /** | |
47 * Adds toggle controls to the sidebar list. | |
48 * | |
49 * Controls are inserted as the first children of list items in the sidebar | |
50 * which contain only text (not a link). Handlers are set up so that when a | |
51 * toggle control is clicked, any <ul> elements who are siblings of the | |
52 * control are hidden/revealed as appropriate given the control's state. | |
53 * | |
54 * If a list item possesses the class "leftNavSelected" its ancestor <ul> is | |
55 * revealed by default (it represents the current page). | |
56 */ | |
57 function initSidebar() { | |
58 var toc = document.getElementById('gc-toc'); | |
59 if (!toc) | |
60 return; | |
61 | |
62 // Figure out which link matches the current page so it can be styled | |
63 // differently. | |
64 var links = toc.getElementsByTagName('a'); | |
65 var selectedNode = null; | |
66 var path_parts = document.location.pathname.replace(/\/+$/, '').split('/') | |
67 for (var i = 0; i < links.length; i++) { | |
68 if (links[i].getAttribute('href') != path_parts[path_parts.length - 1]) | |
69 continue; | |
70 links[i].className = 'leftNavSelected'; | |
71 links[i].removeAttribute('href'); | |
72 selectedNode = links[i]; | |
73 } | 56 } |
74 | 57 |
75 // Go through the items on the sidebar and add toggles. | 58 // TODO(kalman): needs a button role for accessibility? |
76 var items = toc.getElementsByTagName('li'); | 59 button.setAttribute('href', 'javascript:void(0)'); |
77 for (var i = 0; i < items.length; i++) { | 60 button.addEventListener('click', toggle); |
78 var item = items[i]; | 61 buttonContent.parentElement.insertBefore(button, buttonContent); |
79 if (!item.firstChild || item.firstChild.tagName != 'SPAN') | 62 button.appendChild(buttonContent); |
80 continue; | 63 button.appendChild(toggleIndicator); |
81 // Only assign toggles to text nodes in the sidebar. | |
82 var a = document.createElement('a'); | |
83 a.className = 'toggle selected'; | |
84 a.appendChild(document.createTextNode(' ')); | |
85 a.onclick = function() { | |
86 toggleList(this.parentNode.getElementsByTagName('ul')); | |
87 }; | |
88 item.firstChild.onclick = function() { | |
89 toggleList(this.parentNode.getElementsByTagName('ul')); | |
90 }; | |
91 item.insertBefore(a, item.firstChild); | |
92 toggleList(item.getElementsByTagName('ul')); | |
93 } | |
94 | 64 |
95 // Reveal the selected link. | 65 // Leave the toggle open if the selected link is a child. |
96 if (selectedNode) | 66 if (toggleable.querySelector('.selected')) |
97 revealAncestor(selectedNode); | 67 toggle(); |
98 }; | 68 else |
| 69 toggleable.classList.add('hidden'); |
| 70 }); |
99 | 71 |
100 initSidebar(); | 72 // Each level of the sidebar is displayed differently. Rather than trying |
| 73 // to nest CSS selectors in a crazy way, programmatically assign levels |
| 74 // to them. |
| 75 function addNestLevels(node, currentLevel) { |
| 76 node.classList.add('level' + currentLevel); |
| 77 if (node.tagName == 'UL') |
| 78 currentLevel++; |
| 79 Array.prototype.forEach.call(node.children, function(child) { |
| 80 addNestLevels(child, currentLevel); |
| 81 }); |
| 82 } |
| 83 |
| 84 addNestLevels(sidebar, 1); |
101 })() | 85 })() |
OLD | NEW |