giblish 0.8.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/unit_tests.yml +30 -0
- data/.gitignore +7 -3
- data/.ruby-version +1 -1
- data/Changelog.adoc +59 -0
- data/README.adoc +261 -0
- data/docs/concepts/text_search.adoc +213 -0
- data/docs/concepts/text_search_im/cgi-search_request.puml +35 -0
- data/docs/concepts/text_search_im/cgi-search_request.svg +397 -0
- data/docs/concepts/text_search_im/search_request.puml +40 -0
- data/docs/concepts/text_search_im/search_request.svg +408 -0
- data/docs/howtos/trigger_generation.adoc +180 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/Render Documents.png +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/View Documents.png +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.graphml +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_hooks.svg +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.graphml +0 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/deploy_with_jenkins.svg +0 -0
- data/docs/howtos/trigger_generation_im/docgen_github.puml +51 -0
- data/docs/{setup_server_assets → howtos/trigger_generation_im}/giblish_deployment.graphml +0 -0
- data/docs/howtos/trigger_generation_im/post-receive-example.sh +50 -0
- data/docs/reference/box_flow_spec.adoc +22 -0
- data/docs/reference/search_spec.adoc +185 -0
- data/giblish.gemspec +50 -29
- data/lib/giblish/adocsrc_providers.rb +23 -0
- data/lib/giblish/application.rb +214 -41
- data/lib/giblish/cmdline.rb +273 -259
- data/lib/giblish/config_utils.rb +41 -0
- data/lib/giblish/configurator.rb +163 -0
- data/lib/giblish/conversion_info.rb +120 -0
- data/lib/giblish/docattr_providers.rb +125 -0
- data/lib/giblish/docid/docid.rb +181 -0
- data/lib/giblish/github_trigger/webhook_manager.rb +64 -0
- data/lib/giblish/gitrepos/checkoutmanager.rb +124 -0
- data/lib/giblish/{gititf.rb → gitrepos/gititf.rb} +30 -4
- data/lib/giblish/gitrepos/gitsummary.erb +61 -0
- data/lib/giblish/gitrepos/gitsummaryprovider.rb +78 -0
- data/lib/giblish/gitrepos/history_pb.rb +41 -0
- data/lib/giblish/indexbuilders/d3treegraph.rb +88 -0
- data/lib/giblish/indexbuilders/depgraphbuilder.rb +109 -0
- data/lib/giblish/indexbuilders/dotdigraphadoc.rb +174 -0
- data/lib/giblish/indexbuilders/standard_index.erb +10 -0
- data/lib/giblish/indexbuilders/subtree_indices.rb +132 -0
- data/lib/giblish/indexbuilders/templates/circles.html.erb +111 -0
- data/lib/giblish/indexbuilders/templates/flame.html.erb +61 -0
- data/lib/giblish/indexbuilders/templates/tree.html.erb +366 -0
- data/lib/giblish/indexbuilders/templates/treemap.html.erb +127 -0
- data/lib/giblish/indexbuilders/verbatimtree.rb +94 -0
- data/lib/giblish/pathtree.rb +473 -74
- data/lib/giblish/resourcepaths.rb +150 -0
- data/lib/giblish/search/expand_adoc.rb +55 -0
- data/lib/giblish/search/headingindexer.rb +312 -0
- data/lib/giblish/search/request_manager.rb +110 -0
- data/lib/giblish/search/searchquery.rb +68 -0
- data/lib/giblish/search/textsearcher.rb +349 -0
- data/lib/giblish/subtreeinfobuilder.rb +77 -0
- data/lib/giblish/treeconverter.rb +272 -0
- data/lib/giblish/utils.rb +142 -294
- data/lib/giblish/version.rb +1 -1
- data/lib/giblish.rb +10 -7
- data/scripts/hooks/post-receive.example +66 -0
- data/{docgen/scripts/githook_examples → scripts/hooks}/post-update.example +0 -0
- data/{docgen → scripts}/resources/css/adoc-colony.css +0 -0
- data/scripts/resources/css/giblish-serif.css +419 -0
- data/scripts/resources/css/giblish.css +1979 -419
- data/{docgen → scripts}/resources/fonts/Ubuntu-B.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-BI.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-R.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/Ubuntu-RI.ttf +0 -0
- data/{docgen → scripts}/resources/fonts/mplus1p-regular-fallback.ttf +0 -0
- data/{docgen → scripts}/resources/images/giblish_logo.png +0 -0
- data/{docgen → scripts}/resources/images/giblish_logo.svg +0 -0
- data/{docgen → scripts}/resources/themes/giblish.yml +0 -0
- data/scripts/wserv_development.rb +32 -0
- data/web_apps/cgi_search/gibsearch.rb +43 -0
- data/web_apps/gh_webhook_trigger/config.ru +2 -0
- data/web_apps/gh_webhook_trigger/gh_webhook_trigger.rb +73 -0
- data/web_apps/gh_webhook_trigger/public/dummy.txt +3 -0
- data/web_apps/sinatra_search/config.ru +2 -0
- data/web_apps/sinatra_search/public/dummy.txt +3 -0
- data/web_apps/sinatra_search/sinatra_search.rb +34 -0
- data/web_apps/sinatra_search/tmp/restart.txt +0 -0
- metadata +180 -71
- data/.rubocop.yml +0 -7
- data/.travis.yml +0 -3
- data/Changelog +0 -16
- data/Gemfile +0 -4
- data/README.adoc +0 -1
- data/Rakefile +0 -41
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/data/testdocs/malformed/no_header.adoc +0 -5
- data/data/testdocs/toplevel.adoc +0 -19
- data/data/testdocs/wellformed/adorned_purpose.adoc +0 -17
- data/data/testdocs/wellformed/docidtest/docid_1.adoc +0 -24
- data/data/testdocs/wellformed/docidtest/docid_2.adoc +0 -8
- data/data/testdocs/wellformed/simple.adoc +0 -14
- data/data/testdocs/wellformed/source_highlighting/highlight_source.adoc +0 -38
- data/docgen/resources/css/giblish.css +0 -1979
- data/docgen/scripts/Jenkinsfile +0 -18
- data/docgen/scripts/gen_adoc_org.sh +0 -58
- data/docs/README.adoc +0 -387
- data/docs/setup_server.adoc +0 -202
- data/lib/giblish/buildgraph.rb +0 -216
- data/lib/giblish/buildindex.rb +0 -459
- data/lib/giblish/core.rb +0 -451
- data/lib/giblish/docconverter.rb +0 -308
- data/lib/giblish/docid.rb +0 -180
- data/lib/giblish/docinfo.rb +0 -75
- data/lib/giblish/indexheadings.rb +0 -251
- data/lib/giblish-search.cgi +0 -459
- data/scripts/hooks/post-receive +0 -57
- data/scripts/publish_html.sh +0 -99
@@ -0,0 +1,174 @@
|
|
1
|
+
require_relative "../utils"
|
2
|
+
|
3
|
+
module Giblish
|
4
|
+
# Provides a graphviz-formatted digraph of the provided node<->id ref map.
|
5
|
+
#
|
6
|
+
# See https://graphviz.gitlab.io/doc/conv_info/lang.html for one definition of the
|
7
|
+
# language.
|
8
|
+
#
|
9
|
+
# A short example:
|
10
|
+
#
|
11
|
+
# [graphviz,target="docdeps",format="svg",svg-type="inline"]
|
12
|
+
# ....
|
13
|
+
# digraph document_deps {
|
14
|
+
# bgcolor="#33333310"
|
15
|
+
# node [shape=note,
|
16
|
+
# fillcolor="#ebf26680",
|
17
|
+
# style="filled,solid"
|
18
|
+
# ]
|
19
|
+
#
|
20
|
+
# rankdir="LR"
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# "D-3"[label="D-3\n Doc 3", URL="file3.html" ]
|
24
|
+
# "D-2"[label="D-2\n Doc 2", URL="my/file2.html" ]
|
25
|
+
# "D-1"[label="D-1\n Doc 1", URL="my/subdir/file1.html" ]
|
26
|
+
# "D-1" -> { "D-2" "D-3"}
|
27
|
+
# "D-2" -> { "D-1"}
|
28
|
+
# "D-3"
|
29
|
+
# }
|
30
|
+
# ....
|
31
|
+
class DotDigraphAdoc
|
32
|
+
# info_2_ids:: A {ConversionInfo => [doc id refs]} hash.
|
33
|
+
#
|
34
|
+
# The following properties are expected from the ConversionInfo:
|
35
|
+
# title:: String
|
36
|
+
# doc_id:: String
|
37
|
+
# dst_rel_path:: String - the relative path from a the repo top to a doc
|
38
|
+
# in the dst tree
|
39
|
+
#
|
40
|
+
# target::
|
41
|
+
# format::
|
42
|
+
# opts:: additional options {"opt" => "value"}. Currently supported:
|
43
|
+
#
|
44
|
+
# cachedir:: the directory to use for storing files produced during diagram generation.
|
45
|
+
# svg-type:: how to embed svg images
|
46
|
+
#
|
47
|
+
def initialize(info_2_ids:, target: "gibgraph", format: "svg", opts: {"svg-type" => "inline"})
|
48
|
+
@info_2_ids = info_2_ids
|
49
|
+
@target = target
|
50
|
+
@format = format
|
51
|
+
@opts = opts
|
52
|
+
|
53
|
+
@noid_docs = {}
|
54
|
+
@next_id = 0
|
55
|
+
end
|
56
|
+
|
57
|
+
def source
|
58
|
+
<<~DOC_STR
|
59
|
+
#{graph_header}
|
60
|
+
#{generate_labels}
|
61
|
+
#{generate_deps}
|
62
|
+
#{graph_footer}
|
63
|
+
DOC_STR
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def graph_header
|
69
|
+
opt_str = @opts.collect { |key, value| "#{key}=\"#{value}\"" }.join(",")
|
70
|
+
<<~DOC_STR
|
71
|
+
[graphviz,target="#{@target}",format="#{@format}",#{opt_str}]
|
72
|
+
....
|
73
|
+
digraph document_deps {
|
74
|
+
bgcolor="#33333310"
|
75
|
+
labeljust=l
|
76
|
+
node [shape=note,
|
77
|
+
fillcolor="#ebf26680",
|
78
|
+
style="filled,solid"
|
79
|
+
]
|
80
|
+
|
81
|
+
rankdir="LR"
|
82
|
+
|
83
|
+
DOC_STR
|
84
|
+
end
|
85
|
+
|
86
|
+
def graph_footer
|
87
|
+
<<~DOC_STR
|
88
|
+
}
|
89
|
+
....
|
90
|
+
DOC_STR
|
91
|
+
end
|
92
|
+
|
93
|
+
def make_dot_entry(doc_dict, conv_info)
|
94
|
+
title = if conv_info&.title.nil?
|
95
|
+
""
|
96
|
+
else
|
97
|
+
# remove html markup in the title for displaying in the tree
|
98
|
+
stripped_title = conv_info.title.gsub(/<.*?>/, "")
|
99
|
+
stripped_title.tr!('"', "'")
|
100
|
+
Giblish.break_line(stripped_title, 16).join("\n")
|
101
|
+
end
|
102
|
+
|
103
|
+
# title = conv_info&.title.nil? ? "" : Giblish.break_line(conv_info.title, 16).join("\n")
|
104
|
+
# create the label used to display the node in the graph
|
105
|
+
dot_entry = if conv_info.docid.nil?
|
106
|
+
doc_id = next_fake_id
|
107
|
+
@noid_docs[conv_info] = doc_id
|
108
|
+
"\"#{doc_id}\"[label=\"-\n#{title}\""
|
109
|
+
else
|
110
|
+
doc_id = conv_info.docid
|
111
|
+
"\"#{conv_info.docid}\"[label=\"#{conv_info.docid}\n#{title}\""
|
112
|
+
end
|
113
|
+
|
114
|
+
# add clickable links in the case of html output (this is not supported
|
115
|
+
# out-of-the-box for pdf).
|
116
|
+
rp = conv_info.dst_rel_path
|
117
|
+
dot_entry += case rp.extname
|
118
|
+
when ".html"
|
119
|
+
", URL=\"#{rp}\" ]"
|
120
|
+
else
|
121
|
+
" ]"
|
122
|
+
end
|
123
|
+
doc_dict[doc_id] = dot_entry
|
124
|
+
end
|
125
|
+
|
126
|
+
# create an entry in the 'dot' description for each
|
127
|
+
# document, sort them according to descending doc id to
|
128
|
+
# get them displayed in the opposite order in the graph
|
129
|
+
def generate_labels
|
130
|
+
node_dict = {}
|
131
|
+
@info_2_ids.each_key do |conv_info|
|
132
|
+
next unless conv_info.converted
|
133
|
+
|
134
|
+
make_dot_entry node_dict, conv_info
|
135
|
+
end
|
136
|
+
# sort the nodes by reverse doc id
|
137
|
+
node_dict = node_dict.sort.reverse.to_h
|
138
|
+
|
139
|
+
# produce the string with all node entries
|
140
|
+
node_dict.map do |_k, v|
|
141
|
+
v
|
142
|
+
end.join("\n")
|
143
|
+
end
|
144
|
+
|
145
|
+
def generate_deps
|
146
|
+
dep_str = ""
|
147
|
+
@info_2_ids.each do |conv_info, targets|
|
148
|
+
# set either the real or the generated id as source
|
149
|
+
src_part = if conv_info.docid.nil?
|
150
|
+
"\"#{@noid_docs[conv_info]}\""
|
151
|
+
else
|
152
|
+
"\"#{conv_info.docid}\""
|
153
|
+
end
|
154
|
+
|
155
|
+
if targets.length.zero?
|
156
|
+
dep_str += "#{src_part}\n"
|
157
|
+
next
|
158
|
+
end
|
159
|
+
|
160
|
+
dep_str += "#{src_part} -> {" + targets.reduce("") do |acc, target|
|
161
|
+
acc + " \"#{target}\""
|
162
|
+
end
|
163
|
+
# replace last comma with newline
|
164
|
+
dep_str += "}\n"
|
165
|
+
end
|
166
|
+
dep_str
|
167
|
+
end
|
168
|
+
|
169
|
+
def next_fake_id
|
170
|
+
@next_id += 1
|
171
|
+
"_generated_id_#{@next_id.to_s.rjust(4, "0")}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require "erb"
|
2
|
+
require_relative "verbatimtree"
|
3
|
+
require_relative "d3treegraph"
|
4
|
+
|
5
|
+
module Giblish
|
6
|
+
class SubtreeIndexBase < SubtreeSrcItf
|
7
|
+
attr_reader :src_location
|
8
|
+
|
9
|
+
DEFAULT_INDEX_ERB = "/standard_index.erb"
|
10
|
+
|
11
|
+
def initialize(pathtree, output_basename)
|
12
|
+
@pathtree = pathtree
|
13
|
+
@output_basename = output_basename
|
14
|
+
@src_location = pathtree.pathname.dirname
|
15
|
+
@title = pathtree.segment
|
16
|
+
end
|
17
|
+
|
18
|
+
def adoc_source
|
19
|
+
erb_template = File.read(__dir__ + DEFAULT_INDEX_ERB)
|
20
|
+
ERB.new(erb_template, trim_mode: "<>").result(binding)
|
21
|
+
end
|
22
|
+
|
23
|
+
def tree_summary
|
24
|
+
VerbatimTree.new(@pathtree.sort_leaf_first!, {dir_index_base_name: @output_basename}).source
|
25
|
+
# str = "++++\n"
|
26
|
+
# str += D3TreeGraph.new(tree: @pathtree, options: {dir_index_base_name: @output_basename}).source
|
27
|
+
# str += "\n++++"
|
28
|
+
# str
|
29
|
+
end
|
30
|
+
|
31
|
+
def document_details
|
32
|
+
details_str = ""
|
33
|
+
|
34
|
+
@pathtree.traverse_preorder do |_level, node|
|
35
|
+
next unless node.leaf?
|
36
|
+
|
37
|
+
d = node.data
|
38
|
+
details_str << (d.converted ? document_detail(node) : document_detail_fail(d))
|
39
|
+
end
|
40
|
+
details_str
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def document_detail_fail(node_data)
|
46
|
+
<<~FAIL_INFO
|
47
|
+
=== #{node_data.src_basename}
|
48
|
+
|
49
|
+
Source file::
|
50
|
+
#{node_data.src_node.segment}
|
51
|
+
|
52
|
+
Error detail::
|
53
|
+
#{node_data.error_msg}
|
54
|
+
|
55
|
+
''''
|
56
|
+
|
57
|
+
FAIL_INFO
|
58
|
+
end
|
59
|
+
|
60
|
+
# Show some details about file content
|
61
|
+
def document_detail(node)
|
62
|
+
node_data = node.data
|
63
|
+
<<~DETAIL_SRC
|
64
|
+
[[#{Giblish.to_valid_id(node.pathname.to_s, "_", "_", true)}]]
|
65
|
+
=== pass:[#{node_data.title.encode("utf-8")}]
|
66
|
+
|
67
|
+
#{"Doc id::\n_#{node_data.docid}_" unless node_data.docid.nil?}
|
68
|
+
|
69
|
+
#{"Purpose::\n#{node_data.purpose_str}" unless node_data.purpose_str.to_s.empty?}
|
70
|
+
|
71
|
+
#{if node_data.stderr.empty?
|
72
|
+
""
|
73
|
+
else
|
74
|
+
"Conversion issues::\n"\
|
75
|
+
"#{node_data.stderr.gsub(/^/, " * ")}"
|
76
|
+
end
|
77
|
+
}
|
78
|
+
|
79
|
+
Source file::
|
80
|
+
#{node_data.src_node.segment}
|
81
|
+
|
82
|
+
'''
|
83
|
+
|
84
|
+
DETAIL_SRC
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Generates a directory index with history info for all files under the
|
89
|
+
# given subdir node.
|
90
|
+
class SubtreeIndexGit < SubtreeIndexBase
|
91
|
+
# The fixed heading of the table used to display file history
|
92
|
+
HISTORY_TABLE_HEADING = <<~HISTORY_HEADER
|
93
|
+
File history::
|
94
|
+
|
95
|
+
[cols=\"2,3,8,3\",options=\"header\"]
|
96
|
+
|===
|
97
|
+
|Date |Author |Message |Sha1
|
98
|
+
HISTORY_HEADER
|
99
|
+
|
100
|
+
HISTORY_TABLE_FOOTING = <<~HIST_FOOTER
|
101
|
+
|
102
|
+
|===\n\n
|
103
|
+
HIST_FOOTER
|
104
|
+
|
105
|
+
def subtitle(dst_node)
|
106
|
+
"from #{dst_node.data.branch}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def document_detail_fail(node_data)
|
110
|
+
super(node_data) + generate_history_info(node_data)
|
111
|
+
end
|
112
|
+
|
113
|
+
def document_detail(node_data)
|
114
|
+
super(node_data) + generate_history_info(node_data)
|
115
|
+
end
|
116
|
+
|
117
|
+
def generate_history_info(node_data)
|
118
|
+
return "Could not find history information\n\n" unless node_data.respond_to?(:history)
|
119
|
+
|
120
|
+
# Generate table rows of history information
|
121
|
+
rows = node_data.history.collect do |h|
|
122
|
+
<<~HISTORY_ROW
|
123
|
+
|#{h.date.strftime("%Y-%m-%d")}
|
124
|
+
|#{h.author}
|
125
|
+
|#{h.message}
|
126
|
+
|#{h.sha1[0..7]} ...
|
127
|
+
HISTORY_ROW
|
128
|
+
end.join("\n\n")
|
129
|
+
HISTORY_TABLE_HEADING + rows + HISTORY_TABLE_FOOTING
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<meta charset="utf-8">
|
3
|
+
<style>
|
4
|
+
|
5
|
+
html, body{
|
6
|
+
height: 100%;
|
7
|
+
margin: 0;
|
8
|
+
padding: 0;
|
9
|
+
}
|
10
|
+
|
11
|
+
.node {
|
12
|
+
cursor: pointer;
|
13
|
+
}
|
14
|
+
|
15
|
+
.node:hover {
|
16
|
+
stroke: #000;
|
17
|
+
stroke-width: 1.5px;
|
18
|
+
}
|
19
|
+
|
20
|
+
.node--leaf {
|
21
|
+
fill: white;
|
22
|
+
}
|
23
|
+
|
24
|
+
.label {
|
25
|
+
font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
|
26
|
+
text-anchor: middle;
|
27
|
+
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
|
28
|
+
}
|
29
|
+
|
30
|
+
.label,
|
31
|
+
.node--root,
|
32
|
+
.node--leaf {
|
33
|
+
pointer-events: none;
|
34
|
+
}
|
35
|
+
|
36
|
+
</style>
|
37
|
+
<svg width="100%" height="100%"></svg>
|
38
|
+
<script src="https://d3js.org/d3.v4.min.js"></script>
|
39
|
+
<script>
|
40
|
+
// Copied from https://bl.ocks.org/mbostock/7607535
|
41
|
+
var width = window.innerWidth,
|
42
|
+
height = window.innerHeight;
|
43
|
+
var svg = d3.select("svg"),
|
44
|
+
margin = 20,
|
45
|
+
diameter = Math.min(width, height),
|
46
|
+
g = svg.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
|
47
|
+
|
48
|
+
var color = d3.scaleLinear()
|
49
|
+
.domain([-1, 5])
|
50
|
+
.range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
|
51
|
+
.interpolate(d3.interpolateHcl);
|
52
|
+
|
53
|
+
var pack = d3.pack()
|
54
|
+
.size([diameter - margin, diameter - margin])
|
55
|
+
.padding(2);
|
56
|
+
|
57
|
+
root = <%= tree.to_json %>;
|
58
|
+
root = d3.hierarchy(root)
|
59
|
+
.sum(function(d) { return d.children.length + 1; })
|
60
|
+
.sort(function(a, b) { return b.value - a.value; });
|
61
|
+
|
62
|
+
var focus = root,
|
63
|
+
nodes = pack(root).descendants(),
|
64
|
+
view;
|
65
|
+
|
66
|
+
var circle = g.selectAll("circle")
|
67
|
+
.data(nodes)
|
68
|
+
.enter().append("circle")
|
69
|
+
.attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
|
70
|
+
.style("fill", function(d) { return d.children ? color(d.depth) : null; })
|
71
|
+
.on("click", function(d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });
|
72
|
+
|
73
|
+
var text = g.selectAll("text")
|
74
|
+
.data(nodes)
|
75
|
+
.enter().append("text")
|
76
|
+
.attr("class", "label")
|
77
|
+
.style("fill-opacity", function(d) { return d.parent === root ? 1 : 0; })
|
78
|
+
.style("display", function(d) { return d.parent === root ? "inline" : "none"; })
|
79
|
+
.text(function(d) { return d.data.name; });
|
80
|
+
|
81
|
+
var node = g.selectAll("circle,text");
|
82
|
+
|
83
|
+
svg
|
84
|
+
.style("background", color(-1))
|
85
|
+
.on("click", function() { zoom(root); });
|
86
|
+
|
87
|
+
zoomTo([root.x, root.y, root.r * 2 + margin]);
|
88
|
+
|
89
|
+
function zoom(d) {
|
90
|
+
var focus0 = focus; focus = d;
|
91
|
+
|
92
|
+
var transition = d3.transition()
|
93
|
+
.duration(d3.event.altKey ? 7500 : 750)
|
94
|
+
.tween("zoom", function(d) {
|
95
|
+
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
|
96
|
+
return function(t) { zoomTo(i(t)); };
|
97
|
+
});
|
98
|
+
|
99
|
+
transition.selectAll("text")
|
100
|
+
.filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
|
101
|
+
.style("fill-opacity", function(d) { return d.parent === focus ? 1 : 0; })
|
102
|
+
.on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
|
103
|
+
.on("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
|
104
|
+
}
|
105
|
+
|
106
|
+
function zoomTo(v) {
|
107
|
+
var k = diameter / v[2]; view = v;
|
108
|
+
node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
|
109
|
+
circle.attr("r", function(d) { return d.r * k; });
|
110
|
+
}
|
111
|
+
</script>
|
@@ -0,0 +1,61 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.css">
|
7
|
+
<style>
|
8
|
+
html, body{
|
9
|
+
height: 100%;
|
10
|
+
margin: 0;
|
11
|
+
padding: 0;
|
12
|
+
font-size: 14px;
|
13
|
+
}
|
14
|
+
</style>
|
15
|
+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
|
16
|
+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"></script>
|
17
|
+
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.js"></script>
|
18
|
+
|
19
|
+
</head>
|
20
|
+
<body>
|
21
|
+
|
22
|
+
<div class="container">
|
23
|
+
<div id="chart">
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<script>
|
27
|
+
// Uses D3js flamegraph: https://github.com/spiermar/d3-flame-graph
|
28
|
+
var flameGraph = d3.flameGraph()
|
29
|
+
.width(window.innerWidth)
|
30
|
+
.cellHeight(18)
|
31
|
+
.transitionDuration(750)
|
32
|
+
.transitionEase(d3.easeCubic)
|
33
|
+
.sort(true)
|
34
|
+
.title("");
|
35
|
+
|
36
|
+
var tip = d3.tip()
|
37
|
+
.direction("s")
|
38
|
+
.offset([8, 0])
|
39
|
+
.attr('class', 'd3-flame-graph-tip')
|
40
|
+
.html(function(d) { return "name: " + d.data.name + ", value: " + d.data.value; });
|
41
|
+
|
42
|
+
flameGraph.tooltip(tip);
|
43
|
+
|
44
|
+
function setValue(d) {
|
45
|
+
var val = 0;
|
46
|
+
for( var i=0; i<d.children.length; i++){
|
47
|
+
setValue(d.children[i]);
|
48
|
+
val += d.children[i].value;
|
49
|
+
}
|
50
|
+
d.value = val == 0 ? 1 : val;
|
51
|
+
}
|
52
|
+
|
53
|
+
root = <%= tree.to_json %>;
|
54
|
+
setValue(root)
|
55
|
+
d3.select("#chart")
|
56
|
+
.datum(root)
|
57
|
+
.call(flameGraph);
|
58
|
+
</script>
|
59
|
+
|
60
|
+
</body>
|
61
|
+
</html>
|