saikuro_treemap 0.1
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.
- data/.gitignore +3 -0
- data/MIT-LICENSE.txt +21 -0
- data/README.textile +65 -0
- data/Rakefile +62 -0
- data/lib/saikuro_treemap/ccn_node.rb +76 -0
- data/lib/saikuro_treemap/parser.rb +162 -0
- data/lib/saikuro_treemap.rb +74 -0
- data/saikuro_treemap.gemspec +37 -0
- data/templates/css/treemap.css +33 -0
- data/templates/js/jit-min.js +27 -0
- data/templates/js/jit.js +16675 -0
- data/templates/js/saikuro-render.js +48 -0
- data/templates/saikuro.html.erb +27 -0
- data/test/ccn_node_test.rb +96 -0
- data/test/test_helper.rb +4 -0
- metadata +107 -0
data/.gitignore
ADDED
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2010 ThoughtWorks, Inc. (http://thoughtworks.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
h2. Saikuro Treemap Generator
|
2
|
+
|
3
|
+
This gem generates a "treemap":http://en.wikipedia.org/wiki/Treemapping
|
4
|
+
showing the complexity of ruby code on which it is run. It uses
|
5
|
+
"Saikuro":http://saikuro.rubyforge.org/ under the covers to analyze ruby
|
6
|
+
code complexity.
|
7
|
+
|
8
|
+
The size of blocks in the treemap is representative of the size of the
|
9
|
+
code element represented by that block. The color of the block indicates
|
10
|
+
complexity of that particular code element. Green is good, red is bad,
|
11
|
+
blue is somewhere in between.
|
12
|
+
|
13
|
+
Small evenly distributed blocks which are green in color is indicative of
|
14
|
+
good code.
|
15
|
+
|
16
|
+
h2. Usage
|
17
|
+
|
18
|
+
We clearly need to make the gem install a bit easier, but for now:
|
19
|
+
|
20
|
+
* checkout this repos from github
|
21
|
+
* build the saikuro_treemap gem...
|
22
|
+
* >rake gem
|
23
|
+
* install saikuro_treemap gem...
|
24
|
+
* >sudo gem install saikuro_treemap
|
25
|
+
* add a rake task like below to your project, adjusting :code_dirs for your project:
|
26
|
+
|
27
|
+
<pre>
|
28
|
+
require 'saikuro_treemap'
|
29
|
+
|
30
|
+
namespace :metrics do
|
31
|
+
desc 'generate ccn treemap'
|
32
|
+
task :ccn_treemap do
|
33
|
+
SaikuroTreemap.generate_treemap :code_dirs => ['lib']
|
34
|
+
`open reports/saikuro_treemap.html`
|
35
|
+
end
|
36
|
+
end
|
37
|
+
</pre>
|
38
|
+
|
39
|
+
* run the task to see the treemap in your browser
|
40
|
+
|
41
|
+
h2. License
|
42
|
+
|
43
|
+
Saikuro Treemap Generator is MIT Licensed
|
44
|
+
|
45
|
+
The MIT License
|
46
|
+
|
47
|
+
Copyright (c) 2010 ThoughtWorks, Inc. (http://thoughtworks.com)
|
48
|
+
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
50
|
+
of this software and associated documentation files (the "Software"), to deal
|
51
|
+
in the Software without restriction, including without limitation the rights
|
52
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
53
|
+
copies of the Software, and to permit persons to whom the Software is
|
54
|
+
furnished to do so, subject to the following conditions:
|
55
|
+
|
56
|
+
The above copyright notice and this permission notice shall be included in
|
57
|
+
all copies or substantial portions of the Software.
|
58
|
+
|
59
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
60
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
61
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
62
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
63
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
64
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
65
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
$: << 'lib'
|
5
|
+
require 'saikuro_treemap'
|
6
|
+
|
7
|
+
namespace :metrics do
|
8
|
+
desc 'generate ccn treemap'
|
9
|
+
task :ccn_treemap do
|
10
|
+
SaikuroTreemap.generate_treemap :code_dirs => ['lib']
|
11
|
+
`open reports/saikuro_treemap.html`
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'rubygems'
|
17
|
+
rescue LoadError
|
18
|
+
# Too bad.
|
19
|
+
else
|
20
|
+
task "saikuro_treemap.gemspec" do
|
21
|
+
spec = Gem::Specification.new do |s|
|
22
|
+
s.name = "saikuro_treemap"
|
23
|
+
s.version = 0.1
|
24
|
+
s.platform = Gem::Platform::RUBY
|
25
|
+
s.summary = "Generate CCN Treemap based on saikuro analysis"
|
26
|
+
|
27
|
+
s.description = <<-EOF
|
28
|
+
Generate CCN Treemap based on saikuro analysis
|
29
|
+
EOF
|
30
|
+
s.add_dependency 'json'
|
31
|
+
s.add_dependency 'Saikuro'
|
32
|
+
|
33
|
+
s.files = `git ls-files`.split("\n") + %w(saikuro_treemap.gemspec)
|
34
|
+
s.bindir = 'bin'
|
35
|
+
s.require_path = 'lib'
|
36
|
+
s.has_rdoc = false
|
37
|
+
s.extra_rdoc_files = ['README.textile']
|
38
|
+
s.test_files = Dir['test/{test,spec}_*.rb']
|
39
|
+
|
40
|
+
s.author = 'ThoughtWorks Studios'
|
41
|
+
s.email = 'studios@thoughtworks.com'
|
42
|
+
s.homepage = 'http://github.com/ThoughtWorksStudios/saikuro_treemap'
|
43
|
+
s.rubyforge_project = 'saikuro_treemap'
|
44
|
+
end
|
45
|
+
|
46
|
+
File.open("saikuro_treemap.gemspec", "w") { |f| f << spec.to_ruby }
|
47
|
+
end
|
48
|
+
|
49
|
+
task :gem => ["saikuro_treemap.gemspec"] do
|
50
|
+
sh "gem build saikuro_treemap.gemspec"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
task :test do
|
55
|
+
Rake::TestTask.new do |t|
|
56
|
+
t.libs << "test"
|
57
|
+
t.test_files = FileList['test/*test.rb']
|
58
|
+
t.verbose = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
task :default => :test
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module SaikuroTreemap
|
2
|
+
class CCNNode
|
3
|
+
attr_reader :path
|
4
|
+
|
5
|
+
def initialize(path, complexity=0, lines=0)
|
6
|
+
@path = path
|
7
|
+
@children = []
|
8
|
+
@complexity = complexity
|
9
|
+
@lines = lines
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_child(child)
|
13
|
+
@children << child
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_node(*pathes)
|
17
|
+
return self if pathes.join("::") == @path
|
18
|
+
return self if pathes.empty?
|
19
|
+
|
20
|
+
# Eumerable#find is buggy for recursive calls!
|
21
|
+
@children.each do |child|
|
22
|
+
if r = child.find_node(pathes)
|
23
|
+
return r
|
24
|
+
end
|
25
|
+
end
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_nodes(*pathes)
|
30
|
+
pathes.each_with_index {|_, i| find_or_create_node(*pathes[0..i]) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_json(*args)
|
34
|
+
hash = { 'name' => compact_name, 'id' => @path, 'children' => @children }
|
35
|
+
data = {}
|
36
|
+
data['complexity'] = @complexity if @complexity != 0
|
37
|
+
data['lines'] = @lines if @lines != 0
|
38
|
+
data['$area'] = area if area != 0
|
39
|
+
data['$color'] = color if area != 0
|
40
|
+
hash['data'] = data
|
41
|
+
hash.to_json(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def area
|
45
|
+
return @lines if leaf?
|
46
|
+
@children.inject(0) {|sum, child| sum + child.area }
|
47
|
+
end
|
48
|
+
|
49
|
+
def color
|
50
|
+
return "#101010" unless leaf?
|
51
|
+
return "#AE0000" if @complexity >= 10
|
52
|
+
return "#006500" if @complexity < 5
|
53
|
+
return "#4545C2"
|
54
|
+
end
|
55
|
+
|
56
|
+
def leaf?
|
57
|
+
return @children.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def compact_name
|
63
|
+
return '' if @path !~ /\S/
|
64
|
+
@path.split('::').last.split('#').last
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_or_create_node(*pathes)
|
68
|
+
find_node(*pathes) || create_node(*pathes)
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_node(*pathes)
|
72
|
+
parent = find_node(*pathes[0..-2])
|
73
|
+
parent.add_child(CCNNode.new(pathes.join("::")))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# almost all of code in this file is from metric_fu
|
2
|
+
# http://metric-fu.rubyforge.org/
|
3
|
+
module SaikuroTreemap
|
4
|
+
module Parser
|
5
|
+
def self.parse(saikuro_out_dir)
|
6
|
+
files = Dir[File.join(saikuro_out_dir, "/**/*.html")].collect do |path|
|
7
|
+
SFile.new(path) if SFile.is_valid_text_file?(path)
|
8
|
+
end
|
9
|
+
|
10
|
+
files.compact!
|
11
|
+
|
12
|
+
files = files.sort_by do |file|
|
13
|
+
file.elements.
|
14
|
+
max {|a,b| a.complexity.to_i <=> b.complexity.to_i}.
|
15
|
+
complexity.to_i
|
16
|
+
end.reverse
|
17
|
+
|
18
|
+
files.each do |file|
|
19
|
+
file.elements.each do |element|
|
20
|
+
element.defs.each do |defn|
|
21
|
+
defn.name = "#{element.name}##{defn.name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
files.collect(&:to_h)
|
27
|
+
end
|
28
|
+
|
29
|
+
class SFile
|
30
|
+
attr_reader :elements
|
31
|
+
|
32
|
+
def initialize(path)
|
33
|
+
@path = path
|
34
|
+
@file_handle = File.open(@path, "r")
|
35
|
+
@elements = []
|
36
|
+
get_elements
|
37
|
+
ensure
|
38
|
+
@file_handle.close if @file_handle
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.is_valid_text_file?(path)
|
42
|
+
File.open(path, "r") do |f|
|
43
|
+
if f.eof? || !f.readline.match(/--/)
|
44
|
+
return false
|
45
|
+
else
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def filename
|
52
|
+
File.basename(@path, '_cyclo.html')
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_h
|
56
|
+
merge_classes
|
57
|
+
{:classes => @elements}
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_elements
|
61
|
+
begin
|
62
|
+
while (line = @file_handle.readline) do
|
63
|
+
return [] if line.nil? || line !~ /\S/
|
64
|
+
element ||= nil
|
65
|
+
if line.match /START/
|
66
|
+
unless element.nil?
|
67
|
+
@elements << element
|
68
|
+
element = nil
|
69
|
+
end
|
70
|
+
line = @file_handle.readline
|
71
|
+
element = ParsingElement.new(line)
|
72
|
+
elsif line.match /END/
|
73
|
+
@elements << element if element
|
74
|
+
element = nil
|
75
|
+
else
|
76
|
+
element << line if element
|
77
|
+
end
|
78
|
+
end
|
79
|
+
rescue EOFError
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def merge_classes
|
86
|
+
new_elements = []
|
87
|
+
get_class_names.each do |target_class|
|
88
|
+
elements = @elements.find_all {|el| el.name == target_class }
|
89
|
+
complexity = 0
|
90
|
+
lines = 0
|
91
|
+
defns = []
|
92
|
+
elements.each do |el|
|
93
|
+
complexity += el.complexity.to_i
|
94
|
+
lines += el.lines.to_i
|
95
|
+
defns << el.defs
|
96
|
+
end
|
97
|
+
|
98
|
+
new_element = {:class_name => target_class,
|
99
|
+
:complexity => complexity,
|
100
|
+
:lines => lines,
|
101
|
+
:methods => defns.flatten.map {|d| d.to_h}}
|
102
|
+
new_element[:methods] = new_element[:methods].
|
103
|
+
sort_by {|x| x[:complexity] }.
|
104
|
+
reverse
|
105
|
+
|
106
|
+
new_elements << new_element
|
107
|
+
end
|
108
|
+
@elements = new_elements if new_elements
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_class_names
|
112
|
+
class_names = []
|
113
|
+
@elements.each do |element|
|
114
|
+
unless class_names.include?(element.name)
|
115
|
+
class_names << element.name
|
116
|
+
end
|
117
|
+
end
|
118
|
+
class_names
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
class ParsingElement
|
125
|
+
TYPE_REGEX=/Type:(.*) Name/
|
126
|
+
NAME_REGEX=/Name:(.*) Complexity/
|
127
|
+
COMPLEXITY_REGEX=/Complexity:(.*) Lines/
|
128
|
+
LINES_REGEX=/Lines:(.*)/
|
129
|
+
|
130
|
+
attr_reader :complexity, :lines, :defs, :element_type
|
131
|
+
attr_accessor :name
|
132
|
+
|
133
|
+
def initialize(line)
|
134
|
+
@line = line
|
135
|
+
@element_type = line.match(TYPE_REGEX)[1].strip
|
136
|
+
@name = line.match(NAME_REGEX)[1].strip
|
137
|
+
@complexity = line.match(COMPLEXITY_REGEX)[1].strip
|
138
|
+
@lines = line.match(LINES_REGEX)[1].strip
|
139
|
+
@defs = []
|
140
|
+
end
|
141
|
+
|
142
|
+
def <<(line)
|
143
|
+
@defs << ParsingElement.new(line)
|
144
|
+
end
|
145
|
+
|
146
|
+
def to_h
|
147
|
+
base = {:name => @name, :complexity => @complexity.to_i, :lines => @lines.to_i}
|
148
|
+
unless @defs.empty?
|
149
|
+
defs = @defs.map do |my_def|
|
150
|
+
my_def = my_def.to_h
|
151
|
+
my_def.delete(:defs)
|
152
|
+
my_def
|
153
|
+
end
|
154
|
+
base[:defs] = defs
|
155
|
+
end
|
156
|
+
return base
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
require 'erb'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
require 'saikuro_treemap/parser'
|
7
|
+
require 'saikuro_treemap/ccn_node'
|
8
|
+
|
9
|
+
module SaikuroTreemap
|
10
|
+
|
11
|
+
DEFAULT_CONFIG = {
|
12
|
+
:code_dirs => ['app/controllers', 'app/models', 'app/helpers' 'lib'],
|
13
|
+
:output_file => 'reports/saikuro_treemap.html',
|
14
|
+
:saikuro_args => [
|
15
|
+
"--warn_cyclo 5",
|
16
|
+
"--error_cyclo 7",
|
17
|
+
"--formater text",
|
18
|
+
"--cyclo --filter_cyclo 0"]
|
19
|
+
}
|
20
|
+
|
21
|
+
def self.generate_treemap(config={})
|
22
|
+
temp_dir = 'tmp/saikuro'
|
23
|
+
|
24
|
+
config = DEFAULT_CONFIG.merge(config)
|
25
|
+
|
26
|
+
config[:saikuro_args] << "--output_directory #{temp_dir}"
|
27
|
+
|
28
|
+
options_string = config[:saikuro_args] + config[:code_dirs].collect { |dir| "--input_directory '#{dir}'" }
|
29
|
+
|
30
|
+
|
31
|
+
rm_rf temp_dir
|
32
|
+
sh %{saikuro #{options_string.join(' ')}} do |ok, response|
|
33
|
+
unless ok
|
34
|
+
puts "Saikuro failed with exit status: #{response.exitstatus}"
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
saikuro_files = Parser.parse(temp_dir)
|
40
|
+
|
41
|
+
@ccn_node = create_ccn_root_node(saikuro_files)
|
42
|
+
FileUtils.mkdir_p(File.dirname(config[:output_file]))
|
43
|
+
|
44
|
+
File.open(config[:output_file], 'w') do |f|
|
45
|
+
f << ERB.new(File.read(template('saikuro.html.erb'))).result(binding)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.create_ccn_root_node(saikuro_files)
|
50
|
+
root_node = CCNNode.new('')
|
51
|
+
saikuro_files.each do |f|
|
52
|
+
f[:classes].each do |c|
|
53
|
+
class_node_name = c[:class_name]
|
54
|
+
namespaces = class_node_name.split("::")[0..-2]
|
55
|
+
root_node.create_nodes(*namespaces)
|
56
|
+
parent = root_node.find_node(*namespaces)
|
57
|
+
class_node = CCNNode.new(class_node_name, c[:complexity], c[:lines])
|
58
|
+
parent.add_child(class_node)
|
59
|
+
|
60
|
+
c[:methods].each do |m|
|
61
|
+
class_node.add_child(CCNNode.new(m[:name], m[:complexity], m[:lines]))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
root_node
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.template(file)
|
70
|
+
File.expand_path("../../templates/#{file}", __FILE__)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{saikuro_treemap}
|
5
|
+
s.version = "0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["ThoughtWorks Studios"]
|
9
|
+
s.date = %q{2010-07-16}
|
10
|
+
s.description = %q{ Generate CCN Treemap based on saikuro analysis
|
11
|
+
}
|
12
|
+
s.email = %q{studios@thoughtworks.com}
|
13
|
+
s.extra_rdoc_files = ["README.textile"]
|
14
|
+
s.files = [".gitignore", "MIT-LICENSE.txt", "README.textile", "Rakefile", "lib/saikuro_treemap.rb", "lib/saikuro_treemap/ccn_node.rb", "lib/saikuro_treemap/parser.rb", "saikuro_treemap.gemspec", "templates/css/treemap.css", "templates/js/jit-min.js", "templates/js/jit.js", "templates/js/saikuro-render.js", "templates/saikuro.html.erb", "test/ccn_node_test.rb", "test/test_helper.rb"]
|
15
|
+
s.homepage = %q{http://github.com/ThoughtWorksStudios/saikuro_treemap}
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{saikuro_treemap}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Generate CCN Treemap based on saikuro analysis}
|
20
|
+
s.test_files = ["test/test_helper.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
28
|
+
s.add_runtime_dependency(%q<Saikuro>, [">= 0"])
|
29
|
+
else
|
30
|
+
s.add_dependency(%q<json>, [">= 0"])
|
31
|
+
s.add_dependency(%q<Saikuro>, [">= 0"])
|
32
|
+
end
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<json>, [">= 0"])
|
35
|
+
s.add_dependency(%q<Saikuro>, [">= 0"])
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
html, body {
|
2
|
+
margin:0;
|
3
|
+
padding:0;
|
4
|
+
font-family: "Lucida Grande", Verdana;
|
5
|
+
font-size: 0.9em;
|
6
|
+
background-color:#F2F2F2;
|
7
|
+
}
|
8
|
+
|
9
|
+
.node {
|
10
|
+
color:#FFFFFF;
|
11
|
+
overflow:hidden;
|
12
|
+
cursor:pointer;
|
13
|
+
}
|
14
|
+
|
15
|
+
#infovis {
|
16
|
+
text-align: center;
|
17
|
+
position:relative;
|
18
|
+
width:90%;
|
19
|
+
height:900px;
|
20
|
+
margin:auto;
|
21
|
+
overflow:hidden;
|
22
|
+
}
|
23
|
+
|
24
|
+
.tip {
|
25
|
+
color: #fff;
|
26
|
+
width: auto;
|
27
|
+
background-color: black;
|
28
|
+
opacity:0.9;
|
29
|
+
filter:alpha(opacity=90);
|
30
|
+
font-size:10px;
|
31
|
+
font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;
|
32
|
+
padding:7px;
|
33
|
+
}
|