yard-chef 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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>
|