yard-chef 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/yard-chef.rb +12 -11
- data/lib/yard-chef/code_objects/attribute_object.rb +43 -0
- data/lib/yard-chef/code_objects/chef_object.rb +3 -3
- data/lib/yard-chef/code_objects/cookbook_object.rb +9 -1
- data/lib/yard-chef/code_objects/dependency_object.rb +52 -0
- data/lib/yard-chef/code_objects/provider_object.rb +7 -7
- data/lib/yard-chef/code_objects/recipe_object.rb +2 -1
- data/lib/yard-chef/code_objects/resource_object.rb +1 -1
- data/lib/yard-chef/handlers/actions.rb +2 -2
- data/lib/yard-chef/handlers/attribute.rb +38 -9
- data/lib/yard-chef/handlers/base.rb +14 -14
- data/lib/yard-chef/handlers/cookbook.rb +18 -8
- data/lib/yard-chef/handlers/dependency.rb +55 -0
- data/lib/yard-chef/handlers/recipe.rb +25 -13
- data/templates/default/action/html/action.erb +8 -0
- data/templates/default/action/html/setup.rb +1 -13
- data/templates/default/attribute/html/attribute.erb +7 -0
- data/templates/default/attribute/html/attribute_header.erb +1 -1
- data/templates/default/attribute/html/cookbook_attribute.erb +33 -0
- data/templates/default/attribute/html/{table.erb → resource_attribute.erb} +24 -2
- data/templates/default/attribute/html/setup.rb +2 -1
- data/templates/default/chef/html/cookbook_table.erb +323 -2
- data/templates/default/chef/html/setup.rb +1 -1
- data/templates/default/chef_tags/html/example.erb +11 -0
- data/templates/default/chef_tags/html/index.erb +3 -0
- data/templates/default/chef_tags/html/note.erb +10 -0
- data/templates/default/chef_tags/html/see.erb +8 -0
- data/templates/default/chef_tags/html/tag.erb +20 -0
- data/templates/default/chef_tags/setup.rb +43 -0
- data/templates/default/{action/html/action_summary.erb → cookbook/html/dependencies.erb} +7 -6
- data/templates/default/cookbook/html/libraries.erb +4 -2
- data/templates/default/cookbook/html/recipes.erb +1 -1
- data/templates/default/cookbook/html/setup.rb +18 -14
- data/templates/default/fulldoc/html/css/bootstrap.min.css +5 -0
- data/templates/default/fulldoc/html/css/common.css +146 -0
- data/templates/default/fulldoc/html/full_list_cookbooks.erb +6 -8
- data/templates/default/fulldoc/html/full_list_definitions.erb +9 -9
- data/templates/default/fulldoc/html/full_list_recipes.erb +5 -5
- data/templates/default/fulldoc/html/full_list_resources.erb +5 -5
- data/templates/default/fulldoc/html/js/app.js +213 -0
- data/templates/default/fulldoc/html/js/bootstrap.min.js +7 -0
- data/templates/default/fulldoc/html/js/d3.js +5 -0
- data/templates/default/fulldoc/html/js/jquery.js +4 -0
- data/templates/default/fulldoc/html/setup.rb +34 -24
- data/templates/default/layout/html/setup.rb +19 -9
- data/templates/default/provider/html/providers_summary.erb +4 -1
- data/templates/default/provider/html/setup.rb +2 -1
- data/templates/default/recipe/html/recipe_list.erb +1 -1
- data/templates/default/recipe/html/setup.rb +1 -1
- data/templates/default/resource/html/actions.erb +1 -1
- data/templates/default/resource/html/setup.rb +1 -1
- metadata +64 -55
- data/templates/default/action/html/action_list.erb +0 -42
- data/templates/default/action/html/source.erb +0 -34
- data/templates/default/chef/html/docstring.erb +0 -25
- data/templates/default/layout/html/cookbook_table.erb +0 -46
@@ -0,0 +1,55 @@
|
|
1
|
+
# Copyright (c) 2015 Aleksey Hariton
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# 'Software'), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'yard'
|
23
|
+
|
24
|
+
module YARD::Handlers
|
25
|
+
module Chef
|
26
|
+
# Handles "recipes" in a cookbook.
|
27
|
+
class DependencyHandler < Base
|
28
|
+
handles method_call(:depends)
|
29
|
+
|
30
|
+
def process
|
31
|
+
path_array = statement.file.to_s.split('/')
|
32
|
+
return unless path_array.include?('metadata.rb')
|
33
|
+
|
34
|
+
# Recipe declaration in metadata.rb
|
35
|
+
dependency_obj = ChefObject.register(cookbook, name, :dependency)
|
36
|
+
dependency_obj.docstring = statement.docstring
|
37
|
+
end
|
38
|
+
|
39
|
+
# Gets the recipe name from the metadata.rb.
|
40
|
+
#
|
41
|
+
# @return [String] the recipe name
|
42
|
+
#
|
43
|
+
def name
|
44
|
+
statement.parameters.first.jump(:string_content, :ident).source
|
45
|
+
end
|
46
|
+
|
47
|
+
# Gets the docstring for the recipe. The docstring is obtained from the
|
48
|
+
# description field in the recipe.
|
49
|
+
#
|
50
|
+
# @return [YARD::Docsting] the docstring
|
51
|
+
#
|
52
|
+
def parse_docs; end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -26,12 +26,32 @@ module YARD::Handlers
|
|
26
26
|
# Handles "recipes" in a cookbook.
|
27
27
|
class RecipeHandler < Base
|
28
28
|
handles method_call(:recipe)
|
29
|
+
handles :comment, :void_stmt
|
29
30
|
|
30
31
|
def process
|
31
|
-
|
32
|
+
path_array = statement.file.to_s.split('/')
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
# Recipe declaration in metadata.rb
|
35
|
+
if path_array.include?('metadata.rb') && (statement.jump(:ident).source == 'recipe')
|
36
|
+
description = ''
|
37
|
+
recipe_obj = ChefObject.register(cookbook, name, :recipe)
|
38
|
+
# YARD builds an abstract syntax tree (AST) which we need to traverse
|
39
|
+
# to obtain the complete docstring
|
40
|
+
statement.parameters[1].traverse do |child|
|
41
|
+
description << child.jump(:string_content).source if child.type == :string_content
|
42
|
+
end
|
43
|
+
recipe_obj.short_desc = YARD::DocstringParser.new.parse(description).to_docstring
|
44
|
+
recipe_obj.docstring = statement.docstring
|
45
|
+
end
|
46
|
+
|
47
|
+
# Recipe description in the head of recipe, leading comment block
|
48
|
+
if path_array.include? 'recipes'
|
49
|
+
recipe_obj = ChefObject.register(cookbook, ::File.basename(statement.file.to_s, '.rb'), :recipe)
|
50
|
+
if statement.docstring =~ /[\s\t]*\*?Description[:]?\*?/i
|
51
|
+
recipe_obj.docstring = statement.docstring
|
52
|
+
end
|
53
|
+
end
|
54
|
+
recipe_obj
|
35
55
|
end
|
36
56
|
|
37
57
|
# Gets the recipe name from the metadata.rb.
|
@@ -40,7 +60,7 @@ module YARD::Handlers
|
|
40
60
|
#
|
41
61
|
def name
|
42
62
|
recipe = statement.parameters.first.jump(:string_content, :ident).source
|
43
|
-
recipe = recipe.split(
|
63
|
+
recipe = recipe.split('::')[1] if recipe =~ /::/
|
44
64
|
recipe = 'default' if recipe == cookbook.name.to_s
|
45
65
|
recipe
|
46
66
|
end
|
@@ -50,15 +70,7 @@ module YARD::Handlers
|
|
50
70
|
#
|
51
71
|
# @return [YARD::Docsting] the docstring
|
52
72
|
#
|
53
|
-
def
|
54
|
-
description = ""
|
55
|
-
# YARD builds an abstract syntax tree (AST) which we need to traverse
|
56
|
-
# to obtain the complete docstring
|
57
|
-
statement.parameters[1].traverse do |child|
|
58
|
-
description << child.jump(:string_content).source if child.type == :string_content
|
59
|
-
end
|
60
|
-
YARD::DocstringParser.new.parse(description).to_docstring
|
61
|
-
end
|
73
|
+
def parse_docs; end
|
62
74
|
end
|
63
75
|
end
|
64
76
|
end
|
@@ -20,17 +20,5 @@
|
|
20
20
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
22
|
def init
|
23
|
-
|
24
|
-
when :cookbook
|
25
|
-
@providers = object.providers
|
26
|
-
sections.push :action_list, [:source]
|
27
|
-
when :provider
|
28
|
-
@actions = object.children_by_type(:action)
|
29
|
-
sections.push :action_summary
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def source
|
34
|
-
return if object.source.nil?
|
35
|
-
erb(:source)
|
23
|
+
sections.push :action, [T('chef_tags')] if object.type == :action
|
36
24
|
end
|
@@ -23,6 +23,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
%>
|
24
24
|
|
25
25
|
<% if @attributes.size > 0 %>
|
26
|
-
<%= object.type == :resource ?
|
26
|
+
<%= object.type == :resource ? '<h4>Attributes</h4>' : '<h2>Cookbook Attributes</h2>'%>
|
27
27
|
<ul><%= yieldall %></ul>
|
28
28
|
<% end %>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<% @attributes.each do |attribute| %>
|
2
|
+
<h3><strong><%= attribute.name %></strong></h3>
|
3
|
+
<div class="row">
|
4
|
+
<div class="col-md-12">
|
5
|
+
<p>
|
6
|
+
<strong>Description</strong><br />
|
7
|
+
</p>
|
8
|
+
<%= htmlify attribute.docstring %>
|
9
|
+
<%= yieldall :object => attribute, :owner => object %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
<div class="row">
|
13
|
+
<div class="col-md-12">
|
14
|
+
<p>
|
15
|
+
<strong>JSON and default value</strong>
|
16
|
+
</p>
|
17
|
+
<table class="table table-hover">
|
18
|
+
<tr>
|
19
|
+
<th>JSON</th>
|
20
|
+
<th>Default value</th>
|
21
|
+
</tr>
|
22
|
+
<tr>
|
23
|
+
<td>
|
24
|
+
<% if object.type == :cookbook %>
|
25
|
+
<%= htmlify attribute.to_json %>
|
26
|
+
<% end %>
|
27
|
+
</td>
|
28
|
+
<td><%= htmlify attribute.default %></td>
|
29
|
+
</tr>
|
30
|
+
</table>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
@@ -27,12 +27,34 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
27
27
|
<thead>
|
28
28
|
<th>Attribute</th>
|
29
29
|
<th>Description</th>
|
30
|
+
<% if object.type == :resource %>
|
31
|
+
<th>Type</th>
|
32
|
+
<% end %>
|
33
|
+
<th>Default</th>
|
30
34
|
</thead>
|
31
35
|
<tbody>
|
32
36
|
<% @attributes.each do |attribute| %>
|
33
37
|
<tr class="r<%= n %>">
|
34
|
-
<td
|
35
|
-
|
38
|
+
<td>
|
39
|
+
<strong><%= attribute.name %></strong>
|
40
|
+
<% if object.type == :cookbook %>
|
41
|
+
<br /><span>[<a href="#" onclick="return false;" class="toggleSource">View JSON</a>]</span><br /><span class="source_code"><%= htmlify attribute.to_json %></span>
|
42
|
+
<% end %>
|
43
|
+
</td>
|
44
|
+
<td>
|
45
|
+
<%= htmlify attribute.docstring %>
|
46
|
+
<%= yieldall :object => attribute, :owner => object %>
|
47
|
+
</td>
|
48
|
+
<% if object.type == :resource %>
|
49
|
+
<td><%= attribute.kind_of %></td>
|
50
|
+
<% end %>
|
51
|
+
<td>
|
52
|
+
<% if attribute.default.size > 60 %>
|
53
|
+
<span>[<a href="#" onclick="return false;" class="toggleSource">View source</a>]</span><br /><span class="source_code"><%= htmlify attribute.default %></span>
|
54
|
+
<% else %>
|
55
|
+
<%= htmlify attribute.default %>
|
56
|
+
<%end%>
|
57
|
+
</td>
|
36
58
|
</tr>
|
37
59
|
<% n = n == 2 ? 1 : 2 %>
|
38
60
|
<% end %>
|
@@ -22,7 +22,30 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
22
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
23
|
%>
|
24
24
|
|
25
|
-
<
|
25
|
+
<h2>Cookbooks Dependencies Tree</h2>
|
26
|
+
|
27
|
+
<!-- Button trigger modal -->
|
28
|
+
<button type="button" class="btn btn-primary btn-normal" data-toggle="modal" data-target="#dependenciesTree">
|
29
|
+
Show cookbooks dependencies tree
|
30
|
+
</button>
|
31
|
+
|
32
|
+
<!-- Modal -->
|
33
|
+
<div class="modal fade" id="dependenciesTree" tabindex="-1" role="dialog" aria-labelledby="dependenciesTreeLabel">
|
34
|
+
<div class="modal-dialog modal-lg" role="document">
|
35
|
+
<div class="modal-content">
|
36
|
+
<div class="modal-header">
|
37
|
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
38
|
+
<h4 class="modal-title" id="dependenciesTreeLabel">Cookbooks dependency tree</h4>
|
39
|
+
</div>
|
40
|
+
<div class="modal-body">
|
41
|
+
Double click on node to open cookbook documentation.
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
|
48
|
+
<h2>Cookbooks List</h2>
|
26
49
|
<table>
|
27
50
|
<thead>
|
28
51
|
<tr>
|
@@ -36,10 +59,308 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
36
59
|
<% @cookbooks.each do |cookbook| %>
|
37
60
|
<tr class="r<%= n %>">
|
38
61
|
<td><%= linkify cookbook %></td>
|
39
|
-
<td><%= cookbook.short_desc %></td>
|
62
|
+
<td><%= htmlify cookbook.short_desc %></td>
|
40
63
|
<td><%= cookbook.version %></td>
|
41
64
|
</tr>
|
42
65
|
<% n = n == 2 ? 1 : 2 %>
|
43
66
|
<% end %>
|
44
67
|
</tbody>
|
45
68
|
</table>
|
69
|
+
|
70
|
+
<!--
|
71
|
+
<script>
|
72
|
+
|
73
|
+
// http://blog.thomsonreuters.com/index.php/mobile-patent-suits-graphic-of-the-day/
|
74
|
+
var links = [
|
75
|
+
<% @cookbooks.each do |cookbook| %>
|
76
|
+
<% cookbook.dependencies.each do |dependency|%>
|
77
|
+
{source: "<%= cookbook.name %>", target: "<%= dependency.name %>", type: "dependency"},
|
78
|
+
<% end %>
|
79
|
+
<% end %>
|
80
|
+
];
|
81
|
+
|
82
|
+
var nodes = {};
|
83
|
+
|
84
|
+
// Compute the distinct nodes from the links.
|
85
|
+
links.forEach(function(link) {
|
86
|
+
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
|
87
|
+
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
|
88
|
+
});
|
89
|
+
|
90
|
+
var margin = {top: -5, right: -5, bottom: -5, left: -5},
|
91
|
+
width = "960",
|
92
|
+
height = "900";
|
93
|
+
|
94
|
+
var zoom = d3.behavior.zoom()
|
95
|
+
.scaleExtent([1, 10])
|
96
|
+
.on("zoom", zoomed);
|
97
|
+
|
98
|
+
var force = d3.layout.force()
|
99
|
+
.nodes(d3.values(nodes))
|
100
|
+
.links(links)
|
101
|
+
.size([width, height])
|
102
|
+
.linkDistance(90)
|
103
|
+
.charge(-300)
|
104
|
+
.on("tick", tick)
|
105
|
+
.start();
|
106
|
+
|
107
|
+
var drag = force.drag()
|
108
|
+
.on("dragstart", dragstart);
|
109
|
+
|
110
|
+
var svg = d3.select("span#graph").append("svg");
|
111
|
+
|
112
|
+
// define arrow markers for graph links
|
113
|
+
svg.append('svg:defs').append('svg:marker')
|
114
|
+
.attr('id', 'end-arrow')
|
115
|
+
.attr('viewBox', '0 -5 10 10')
|
116
|
+
.attr('refX', 6)
|
117
|
+
.attr('markerWidth', 3)
|
118
|
+
.attr('markerHeight', 3)
|
119
|
+
.attr('orient', 'auto')
|
120
|
+
.append('svg:path')
|
121
|
+
.attr('d', 'M0,-5L10,0L0,5')
|
122
|
+
.attr('fill', '#000');
|
123
|
+
|
124
|
+
svg.attr("width", width)
|
125
|
+
.attr("height", height)
|
126
|
+
.append("g")
|
127
|
+
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
|
128
|
+
.call(zoom)
|
129
|
+
.on("dblclick.zoom", null);
|
130
|
+
|
131
|
+
var link = svg.selectAll(".link")
|
132
|
+
.data(force.links())
|
133
|
+
.enter().append("line")
|
134
|
+
.attr("class", "link");
|
135
|
+
|
136
|
+
var node = svg.selectAll(".node")
|
137
|
+
.data(force.nodes())
|
138
|
+
.enter().append("g")
|
139
|
+
.attr("class", "node")
|
140
|
+
.on("mouseover", mouseover)
|
141
|
+
.on("mouseout", mouseout)
|
142
|
+
.on("dblclick", dblclick)
|
143
|
+
.call(drag)
|
144
|
+
.call(force.drag);
|
145
|
+
|
146
|
+
node.append("circle")
|
147
|
+
.attr("r", 8);
|
148
|
+
|
149
|
+
node.append("text")
|
150
|
+
.attr("x", 12)
|
151
|
+
.attr("dy", ".35em")
|
152
|
+
.text(function(d) { return d.name; });
|
153
|
+
|
154
|
+
function tick() {
|
155
|
+
link.attr("x1", function(d) { return d.source.x; })
|
156
|
+
.attr("y1", function(d) { return d.source.y; })
|
157
|
+
.attr("x2", function(d) { return d.target.x; })
|
158
|
+
.attr("y2", function(d) { return d.target.y; });
|
159
|
+
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
|
160
|
+
}
|
161
|
+
|
162
|
+
function zoomed() {
|
163
|
+
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
|
164
|
+
}
|
165
|
+
function dblclick(d) {
|
166
|
+
d3.select(this).classed("fixed", d.fixed = false);
|
167
|
+
}
|
168
|
+
|
169
|
+
function mouseover() {
|
170
|
+
d3.select(this).select("circle").transition()
|
171
|
+
.duration(750)
|
172
|
+
.attr("r", 16);
|
173
|
+
}
|
174
|
+
|
175
|
+
function mouseout() {
|
176
|
+
d3.select(this).select("circle").transition()
|
177
|
+
.duration(750)
|
178
|
+
.attr("r", 8);
|
179
|
+
}
|
180
|
+
|
181
|
+
function dragstart(d) {
|
182
|
+
d3.event.sourceEvent.stopPropagation();
|
183
|
+
d3.select(this).classed("fixed", d.fixed = true);
|
184
|
+
}
|
185
|
+
|
186
|
+
</script>
|
187
|
+
-->
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
<%
|
193
|
+
cookbooks = @cookbooks
|
194
|
+
%>
|
195
|
+
|
196
|
+
<script>
|
197
|
+
var treeData = [
|
198
|
+
{
|
199
|
+
"name": "cookbooks",
|
200
|
+
"parent": "null",
|
201
|
+
"children": [
|
202
|
+
<% while (cookbook = cookbooks.pop) do %>
|
203
|
+
{
|
204
|
+
"name": "<%= cookbook.name %>",
|
205
|
+
"parent": "cookbooks",
|
206
|
+
<% dependencies = cookbook.dependencies.map {|d| d}
|
207
|
+
if dependencies.size > 0
|
208
|
+
%>
|
209
|
+
"children": [
|
210
|
+
<%
|
211
|
+
while(dependency = dependencies.pop)%>
|
212
|
+
{
|
213
|
+
"name": "<%= dependency.name %>",
|
214
|
+
"parent": "<%= cookbook.name %>"
|
215
|
+
}<%= ',' if dependencies.size > 0 %>
|
216
|
+
<% end %>
|
217
|
+
]
|
218
|
+
<% end %>
|
219
|
+
}<%= ',' if cookbooks.size > 0 %>
|
220
|
+
<% end %>
|
221
|
+
]
|
222
|
+
}
|
223
|
+
];
|
224
|
+
// ************** Generate the tree diagram *****************
|
225
|
+
var margin = {top: 20, right: 120, bottom: 20, left: 120},
|
226
|
+
width = 1024 - margin.right - margin.left,
|
227
|
+
height = 800 - margin.top - margin.bottom;
|
228
|
+
|
229
|
+
var i = 0,
|
230
|
+
duration = 750,
|
231
|
+
root;
|
232
|
+
|
233
|
+
var tree = d3.layout.tree()
|
234
|
+
.size([height, width]);
|
235
|
+
|
236
|
+
var diagonal = d3.svg.diagonal()
|
237
|
+
.projection(function(d) { return [d.y, d.x]; });
|
238
|
+
|
239
|
+
var svg = d3.select("div.modal-body").append("svg")
|
240
|
+
.attr("width", width + margin.right + margin.left)
|
241
|
+
.attr("height", height + margin.top + margin.bottom)
|
242
|
+
.append("g")
|
243
|
+
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
244
|
+
|
245
|
+
root = treeData[0];
|
246
|
+
root.x0 = height / 2;
|
247
|
+
root.y0 = 0;
|
248
|
+
|
249
|
+
function collapse(d) {
|
250
|
+
if (d.children) {
|
251
|
+
d._children = d.children;
|
252
|
+
d._children.forEach(collapse);
|
253
|
+
d.children = null;
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
root.children.forEach(collapse);
|
258
|
+
update(root);
|
259
|
+
|
260
|
+
d3.select(self.frameElement).style("height", "800px");
|
261
|
+
|
262
|
+
function update(source) {
|
263
|
+
|
264
|
+
// Compute the new tree layout.
|
265
|
+
var nodes = tree.nodes(root).reverse(),
|
266
|
+
links = tree.links(nodes);
|
267
|
+
|
268
|
+
// Normalize for fixed-depth.
|
269
|
+
nodes.forEach(function(d) { d.y = d.depth * 180; });
|
270
|
+
|
271
|
+
// Update the nodes…
|
272
|
+
var node = svg.selectAll("g.node")
|
273
|
+
.data(nodes, function(d) { return d.id || (d.id = ++i); });
|
274
|
+
|
275
|
+
// Enter any new nodes at the parent's previous position.
|
276
|
+
var nodeEnter = node.enter().append("g")
|
277
|
+
.attr("class", "node")
|
278
|
+
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
|
279
|
+
.on("click", click)
|
280
|
+
.on("dblclick", cookbook_click);
|
281
|
+
|
282
|
+
nodeEnter.append("circle")
|
283
|
+
.attr("r", 1e-6)
|
284
|
+
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
|
285
|
+
|
286
|
+
nodeEnter.append("text")
|
287
|
+
.attr("x", function(d) { return 10; })
|
288
|
+
.attr("y", function(d) { return -6; })
|
289
|
+
.attr("dy", ".35em")
|
290
|
+
.attr("text-anchor", function(d) { return "start"; })
|
291
|
+
.text(function(d) { return d.name; })
|
292
|
+
.style("fill-opacity", 1e-6);
|
293
|
+
|
294
|
+
// Transition nodes to their new position.
|
295
|
+
var nodeUpdate = node.transition()
|
296
|
+
.duration(duration)
|
297
|
+
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
|
298
|
+
|
299
|
+
nodeUpdate.select("circle")
|
300
|
+
.attr("r", 7)
|
301
|
+
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
|
302
|
+
|
303
|
+
nodeUpdate.select("text")
|
304
|
+
.style("fill-opacity", 1);
|
305
|
+
|
306
|
+
// Transition exiting nodes to the parent's new position.
|
307
|
+
var nodeExit = node.exit().transition()
|
308
|
+
.duration(duration)
|
309
|
+
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
|
310
|
+
.remove();
|
311
|
+
|
312
|
+
nodeExit.select("circle")
|
313
|
+
.attr("r", 1e-6);
|
314
|
+
|
315
|
+
nodeExit.select("text")
|
316
|
+
.style("fill-opacity", 1e-6);
|
317
|
+
|
318
|
+
// Update the links…
|
319
|
+
var link = svg.selectAll("path.link")
|
320
|
+
.data(links, function(d) { return d.target.id; });
|
321
|
+
|
322
|
+
// Enter any new links at the parent's previous position.
|
323
|
+
link.enter().insert("path", "g")
|
324
|
+
.attr("class", "link")
|
325
|
+
.attr("d", function(d) {
|
326
|
+
var o = {x: source.x0, y: source.y0};
|
327
|
+
return diagonal({source: o, target: o});
|
328
|
+
});
|
329
|
+
|
330
|
+
// Transition links to their new position.
|
331
|
+
link.transition()
|
332
|
+
.duration(duration)
|
333
|
+
.attr("d", diagonal);
|
334
|
+
|
335
|
+
// Transition exiting nodes to the parent's new position.
|
336
|
+
link.exit().transition()
|
337
|
+
.duration(duration)
|
338
|
+
.attr("d", function(d) {
|
339
|
+
var o = {x: source.x, y: source.y};
|
340
|
+
return diagonal({source: o, target: o});
|
341
|
+
})
|
342
|
+
.remove();
|
343
|
+
|
344
|
+
// Stash the old positions for transition.
|
345
|
+
nodes.forEach(function(d) {
|
346
|
+
d.x0 = d.x;
|
347
|
+
d.y0 = d.y;
|
348
|
+
});
|
349
|
+
}
|
350
|
+
|
351
|
+
// Toggle children on click.
|
352
|
+
function click(d) {
|
353
|
+
if (d.children) {
|
354
|
+
d._children = d.children;
|
355
|
+
d.children = null;
|
356
|
+
} else {
|
357
|
+
d.children = d._children;
|
358
|
+
d._children = null;
|
359
|
+
}
|
360
|
+
update(d);
|
361
|
+
}
|
362
|
+
// Toggle children on click.
|
363
|
+
function cookbook_click(d) {
|
364
|
+
window.location.href = "chef/" + d.name + ".html";
|
365
|
+
}
|
366
|
+
</script>
|