deep-cover 0.1.16 → 0.2.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +3 -8
  4. data/.travis.yml +4 -4
  5. data/CHANGELOG.md +10 -0
  6. data/Gemfile +3 -1
  7. data/README.md +9 -4
  8. data/Rakefile +6 -3
  9. data/deep_cover.gemspec +2 -2
  10. data/lib/deep_cover.rb +10 -0
  11. data/lib/deep_cover/analyser.rb +1 -2
  12. data/lib/deep_cover/analyser/base.rb +32 -0
  13. data/lib/deep_cover/analyser/branch.rb +19 -4
  14. data/lib/deep_cover/analyser/node.rb +52 -0
  15. data/lib/deep_cover/analyser/per_char.rb +18 -1
  16. data/lib/deep_cover/analyser/stats.rb +54 -0
  17. data/lib/deep_cover/backports.rb +1 -0
  18. data/lib/deep_cover/base.rb +17 -1
  19. data/lib/deep_cover/builtin_takeover.rb +5 -0
  20. data/lib/deep_cover/cli/deep_cover.rb +2 -1
  21. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +12 -10
  22. data/lib/deep_cover/config.rb +22 -8
  23. data/lib/deep_cover/core_ext/coverage_replacement.rb +22 -18
  24. data/lib/deep_cover/coverage.rb +3 -203
  25. data/lib/deep_cover/coverage/analysis.rb +36 -0
  26. data/lib/deep_cover/coverage/base.rb +91 -0
  27. data/lib/deep_cover/coverage/istanbul.rb +34 -0
  28. data/lib/deep_cover/coverage/persistence.rb +93 -0
  29. data/lib/deep_cover/covered_code.rb +12 -22
  30. data/lib/deep_cover/custom_requirer.rb +6 -2
  31. data/lib/deep_cover/node/base.rb +1 -1
  32. data/lib/deep_cover/node/case.rb +13 -2
  33. data/lib/deep_cover/node/exceptions.rb +2 -2
  34. data/lib/deep_cover/node/if.rb +21 -2
  35. data/lib/deep_cover/node/mixin/flow_accounting.rb +1 -0
  36. data/lib/deep_cover/node/send.rb +9 -2
  37. data/lib/deep_cover/node/short_circuit.rb +10 -0
  38. data/lib/deep_cover/parser_ext/range.rb +4 -4
  39. data/lib/deep_cover/reporter/html.rb +15 -0
  40. data/lib/deep_cover/reporter/html/base.rb +14 -0
  41. data/lib/deep_cover/reporter/html/index.rb +78 -0
  42. data/lib/deep_cover/reporter/html/site.rb +78 -0
  43. data/lib/deep_cover/reporter/html/source.rb +136 -0
  44. data/lib/deep_cover/reporter/html/template/assets/32px.png +0 -0
  45. data/lib/deep_cover/reporter/html/template/assets/40px.png +0 -0
  46. data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +338 -0
  47. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +4 -0
  48. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +1 -0
  49. data/lib/deep_cover/reporter/html/template/assets/jstree.css +1108 -0
  50. data/lib/deep_cover/reporter/html/template/assets/jstree.js +8424 -0
  51. data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +1069 -0
  52. data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
  53. data/lib/deep_cover/reporter/html/template/index.html.erb +75 -0
  54. data/lib/deep_cover/reporter/html/template/source.html.erb +35 -0
  55. data/lib/deep_cover/reporter/html/tree.rb +55 -0
  56. data/lib/deep_cover/tools/content_tag.rb +11 -0
  57. data/lib/deep_cover/tools/covered.rb +9 -0
  58. data/lib/deep_cover/tools/merge.rb +16 -0
  59. data/lib/deep_cover/tools/render_template.rb +13 -0
  60. data/lib/deep_cover/tools/transform_keys.rb +9 -0
  61. data/lib/deep_cover/version.rb +1 -1
  62. metadata +33 -7
  63. data/lib/deep_cover/analyser/ignore_uncovered.rb +0 -21
  64. data/lib/deep_cover/analyser/optionally_covered.rb +0 -19
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Reporter::HTML::Base
5
+ include Tools::ContentTag
6
+ def setup
7
+ DeepCover::Config::DEFAULTS.keys.map do |setting|
8
+ value = options[setting]
9
+ value = value.join(', ') if value.respond_to? :join
10
+ content_tag :span, value, class: setting
11
+ end.join
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Reporter
5
+ require_relative 'tree'
6
+ require_relative 'base'
7
+
8
+ class HTML::Index < Struct.new(:analysis, :options)
9
+ def initialize(analysis, **options)
10
+ raise ArgumentError unless analysis.is_a? Coverage::Analysis
11
+ super
12
+ end
13
+
14
+ include HTML::Tree
15
+ include HTML::Base
16
+
17
+ def stats_to_data
18
+ @map = Tools.transform_keys(analysis.stat_map, &:name)
19
+ tree = paths_to_tree(@map.keys)
20
+ transform_data(populate(tree))
21
+ end
22
+
23
+ def columns
24
+ _covered_code, analyser_map = analysis.analyser_map.first
25
+ columns = analyser_map.flat_map do |type, analyser|
26
+ [{
27
+ value: type,
28
+ header: analyser.class.human_name,
29
+ }, {
30
+ value: :"#{type}_percent",
31
+ header: '%',
32
+ },
33
+ ]
34
+ end
35
+ columns.unshift(width: 400, header: 'Path')
36
+ columns
37
+ end
38
+
39
+ private
40
+
41
+ # {a: {}} => [{text: a, data: stat_map[a]}]
42
+ # {b: {...}} => [{text: b, data: sum(stats), children: [...]}]
43
+ def populate(tree, dir = '')
44
+ tree.map do |path, children_hash|
45
+ full_path = [dir, path].join
46
+ if children_hash.empty?
47
+ {
48
+ text: %{<a href="#{full_path}.html">#{path}</a>},
49
+ data: @map[full_path],
50
+ }
51
+ else
52
+ children = populate(children_hash, "#{full_path}/")
53
+ data = Tools.merge(*children.map { |c| c[:data] }, :+)
54
+ {
55
+ text: path,
56
+ data: data,
57
+ children: children,
58
+ state: {opened: true},
59
+ }
60
+ end
61
+ end
62
+ end
63
+
64
+ # Modifies in place the tree:
65
+ # {per_char: Stat, ...} => {per_char: {ignored: ...}, per_char_percent: 55.55, ...}
66
+ def transform_data(tree)
67
+ return unless tree
68
+ tree.each do |node|
69
+ node[:data] = Tools.merge(
70
+ node[:data].transform_values(&:to_h),
71
+ *node[:data].map { |type, stat| {:"#{type}_percent" => stat.percent_covered} }
72
+ )
73
+ transform_data(node[:children])
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ require_relative 'index'
5
+ require_relative 'source'
6
+
7
+ module Reporter::HTML
8
+ class Site < Struct.new(:covered_codes, :options)
9
+ def initialize(covered_codes, **options)
10
+ raise ArgumentError unless covered_codes.all? { |c| c.is_a? CoveredCode }
11
+ super
12
+ end
13
+
14
+ include Memoize
15
+ memoize :analysis
16
+
17
+ def analysis
18
+ Coverage::Analysis.new(covered_codes, **options)
19
+ end
20
+
21
+ def path
22
+ Pathname(options[:output])
23
+ end
24
+
25
+ def save
26
+ clear
27
+ save_assets
28
+ save_index
29
+ save_pages
30
+ end
31
+
32
+ def clear
33
+ path.mkpath
34
+ path.rmtree
35
+ path.mkpath
36
+ end
37
+
38
+ def compile_stylesheet(source, dest)
39
+ Bundler.with_clean_env do
40
+ `sass #{source} #{dest}`
41
+ end
42
+ end
43
+
44
+ def render_index
45
+ Tools.render_template(:index, Index.new(analysis, **options))
46
+ end
47
+
48
+ def save_index
49
+ path.join('index.html').write(render_index)
50
+ end
51
+
52
+ def save_assets
53
+ require 'fileutils'
54
+ src = "#{__dir__}/template/assets"
55
+ dest = path.join('assets')
56
+ FileUtils.cp_r(src, dest)
57
+ compile_stylesheet "#{src}/deep_cover.css.sass", dest.join('deep_cover.css')
58
+ dest.join('deep_cover.css.sass').delete
59
+ end
60
+
61
+ def render_source(covered_code)
62
+ Tools.render_template(:source, Source.new(analysis.analyser_map.fetch(covered_code)))
63
+ end
64
+
65
+ def save_pages
66
+ covered_codes.each do |covered_code|
67
+ dest = path.join("#{covered_code.name}.html")
68
+ dest.dirname.mkpath
69
+ dest.write(render_source(covered_code))
70
+ end
71
+ end
72
+
73
+ def self.save(covered_codes, output: raise, **options)
74
+ Site.new(covered_codes, output: output, **options).save
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Reporter
5
+ require_relative 'base'
6
+
7
+ class HTML::Source < Struct.new(:analyser_map)
8
+ include Tools::Covered
9
+
10
+ def initialize(analyser_map)
11
+ raise ArgumentError unless analyser_map.values.all? { |a| a.is_a?(Analyser) }
12
+ super
13
+ end
14
+
15
+ include HTML::Base
16
+
17
+ def format_source
18
+ lines = convert_source.split("\n")
19
+ lines.map { |line| content_tag(:td, line) }
20
+ rows = lines.map.with_index do |line, i|
21
+ nb = content_tag(:td, i + 1, id: "L#{i + 1}", class: :nb)
22
+ content_tag(:tr, nb + content_tag(:td, line))
23
+ end
24
+ content_tag(:table, rows.join, class: :source)
25
+ end
26
+
27
+ def convert_source
28
+ @rewriter = ::Parser::Source::TreeRewriter.new(covered_code.buffer)
29
+ insert_node_tags
30
+ insert_branch_tags
31
+ html_escape
32
+ @rewriter.process
33
+ end
34
+
35
+ def root_path
36
+ Pathname('.').relative_path_from(Pathname(covered_code.name).dirname)
37
+ end
38
+
39
+ def stats
40
+ cells = analyser_map.map do |type, analyser|
41
+ data = analyser.stats
42
+ f = ->(kind) { content_tag(:span, data.public_send(kind), class: kind, title: kind) }
43
+ [content_tag(:th, analyser.class.human_name, class: type),
44
+ content_tag(:td, "#{f[:executed]} #{f[:ignored] if data.ignored > 0} / #{f[:potentially_executable]}", class: type),
45
+ ]
46
+ end
47
+ rows = cells.transpose.map { |line| content_tag(:tr, line.join) }
48
+ content_tag(:table, rows.join)
49
+ end
50
+
51
+ def analyser
52
+ analyser_map[:per_char]
53
+ end
54
+
55
+ def covered_code
56
+ analyser.covered_code
57
+ end
58
+
59
+ private
60
+
61
+ RUNS_CLASS = Hash.new('run').merge!(0 => 'not-run', nil => 'ignored')
62
+ RUNS_TITLE = Hash.new { |k, runs| "#{runs}x" }.merge!(0 => 'never run', nil => 'ignored')
63
+
64
+ def node_span(node, kind)
65
+ runs = analyser.node_runs(node)
66
+ %{<span class="node-#{node.type} kind-#{kind} #{RUNS_CLASS[runs]}" title="#{RUNS_TITLE[runs]}">}
67
+ end
68
+
69
+ def insert_node_tags
70
+ analyser.each_node do |node|
71
+ h = node.executed_loc_hash
72
+ h.each do |kind, range|
73
+ wrap(range, node_span(node, kind), '</span>')
74
+ end
75
+ exp = node.expression
76
+ if (exp.nil? || exp.empty?) && !analyser.node_covered?(node) && !node.parent.is_a?(Node::Branch) # Not executed empty bodies must show!
77
+ replace(exp, icon(:empty, 'empty node never run'))
78
+ wrap(exp, node_span(node, :empty))
79
+ end
80
+ end
81
+ end
82
+
83
+ ICONS = {
84
+ fork: 'code-fork',
85
+ empty: 'code',
86
+ }.freeze
87
+ def icon(type, title)
88
+ %{<i class="#{type}-icon fa fa-#{ICONS[type]}" aria-hidden="true" title="#{title}"></i>}
89
+ end
90
+
91
+ def fork_span(node, kind, id, title: nil, klass: nil)
92
+ runs = analyser_map[:branch].node_runs(node)
93
+ title ||= RUNS_TITLE[runs]
94
+ %{<span class="fork fork-#{kind} fork-#{RUNS_CLASS[runs]} #{klass}" data-fork-id="#{id}">#{icon(:fork, title)}}
95
+ end
96
+
97
+ def insert_branch_tags
98
+ analyser_map[:branch].each_node.with_index do |node, id|
99
+ node.branches.each do |branch|
100
+ exp = branch.expression
101
+ wrap(exp, fork_span(branch, :branch, id), '</span>') if exp
102
+ end
103
+ runs = analyser_map[:branch].node_runs(node)
104
+ if !covered?(runs) && analyser.node_covered?(node)
105
+ jumps_missing = node.branches.reject { |jump| analyser.node_covered?(jump) }
106
+ title = "#{node.branches_summary(jumps_missing)} not covered"
107
+ klass = 'fork-with-uncovered-branches'
108
+ end
109
+ wrap(node.expression, fork_span(node, :whole, id, title: title, klass: klass))
110
+ end
111
+ end
112
+
113
+ def replace(range, with)
114
+ @rewriter.replace(range, with) rescue binding.pry
115
+ end
116
+
117
+ def wrap(range, before, after = '</span>')
118
+ line = @rewriter.source_buffer.line_range(range.first_line)
119
+ pinned = range.with(end_pos: [range, line].map(&:end_pos).min)
120
+ @rewriter.wrap(pinned, before, after) rescue binding.pry
121
+ end
122
+
123
+ def html_escape
124
+ buffer = analyser.covered_code.buffer
125
+ source = buffer.source
126
+ {'<' => '&lt;', '>' => '&gt;', '&' => '&amp;'}.each do |char, escaped|
127
+ source.scan(char) do
128
+ m = Regexp.last_match
129
+ range = ::Parser::Source::Range.new(buffer, m.begin(0), m.end(0))
130
+ @rewriter.replace(range, escaped)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,338 @@
1
+ // Adapted from GitHub's scheme https://raw.githubusercontent.com/primer/github-syntax-light/master/lib/github-light.css
2
+ $pl-c: #6a737d /* comment punctuation.definition.comment string.comment */
3
+
4
+ /* constant entity.name.constant variable.other.constant variable.language support meta.property-name support.constant support.variable meta.module-reference markup.raw meta.diff.header meta.output */
5
+ $pl-c1: #005cc5 /* string variable */
6
+
7
+ $pl-en: #6f42c1 /* entity.name */
8
+
9
+ /* variable.parameter.function storage.modifier.package storage.modifier.import storage.type.java variable.other */
10
+ $pl-s1: #24292e /* string source */
11
+
12
+
13
+ $pl-ent: #22863a /* entity.name.tag markup.quote */
14
+
15
+
16
+ $pl-k: #d73a49 /* keyword storage storage.type */
17
+ $pl-k: #a419ea /* override */
18
+
19
+ /* string */
20
+ /* punctuation.definition.string source.regexp string.regexp.character-class */
21
+ /* string punctuation.section.embedded source */
22
+ /* string.regexp */
23
+ /* string.regexp constant.character.escape */
24
+ /* string.regexp source.ruby.embedded */
25
+ $pl-s: #032f62 /* string.regexp string.regexp.arbitrary-repitition */
26
+
27
+ /* variable */
28
+ $pl-v: #e36209 /* sublimelinter.mark.warning */
29
+
30
+ $pl-bu: #b31d28 /* invalid.broken invalid.deprecated invalid.unimplemented message.error brackethighlighter.unmatched sublimelinter.mark.error */
31
+
32
+ $pl-ii: #fafbfc /* invalid.illegal */
33
+ $pl-ii-background: #b31d28
34
+
35
+ $pl-sr: #22863a /* string.regexp constant.character.escape */
36
+
37
+ $pl-ml: #735c0f /* markup.list */
38
+
39
+ /* markup.heading */
40
+ /* markup.heading entity.name */
41
+ $pl-mh: #005cc5 /* meta.separator */
42
+
43
+ $pl-mi2: #f6f8fa /* markup.ignored markup.untracked */
44
+ $pl-mi2-background: #005cc5
45
+
46
+
47
+ // http://meyerweb.com/eric/tools/css/reset/
48
+ // v2.0 | 20110126
49
+ // License: none (public domain)
50
+
51
+
52
+ html, body, div, span, applet, object, iframe,
53
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
54
+ a, abbr, acronym, address, big, cite, code,
55
+ del, dfn, em, img, ins, kbd, q, s, samp,
56
+ small, strike, strong, sub, sup, tt, var,
57
+ b, u, i, center,
58
+ dl, dt, dd, ol, ul, li,
59
+ fieldset, form, label, legend,
60
+ table, caption, tbody, tfoot, thead, tr, th, td,
61
+ article, aside, canvas, details, embed,
62
+ figure, figcaption, footer, header, hgroup,
63
+ menu, nav, output, ruby, section, summary,
64
+ time, mark, audio, video
65
+ margin: 0
66
+ padding: 0
67
+ border: 0
68
+ font-size: 100%
69
+ // font: inherit
70
+ vertical-align: baseline
71
+
72
+ // HTML5 display-role reset for older browsers
73
+ article, aside, details, figcaption, figure,
74
+ footer, header, hgroup, menu, nav, section
75
+ display: block
76
+
77
+ body
78
+ line-height: 1
79
+
80
+ ol, ul
81
+ list-style: none
82
+
83
+ blockquote, q
84
+ quotes: none
85
+
86
+ blockquote:before, blockquote:after,
87
+ q:before, q:after
88
+ content: ''
89
+ content: none
90
+
91
+ table
92
+ border-collapse: collapse
93
+ border-spacing: 0
94
+
95
+
96
+ // Global settings
97
+ *
98
+ box-sizing: border-box
99
+ border-collapse: collapse
100
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif
101
+
102
+ // *** DeepCover Source ***
103
+
104
+ $dark-green: #25712b
105
+ $light-green: #74b770
106
+ $light-gray: #e1e4e8
107
+
108
+ %light-border
109
+ border: 1px solid $light-gray
110
+
111
+ .fork-branch > .fork-icon
112
+ display: none
113
+ %show-fork-icon
114
+ .fork-branch > .fork-icon
115
+ display: inherit
116
+ %center
117
+ width: 980px
118
+ margin: 0 auto 0 auto
119
+
120
+ $toggled: $light-green
121
+ $not-run: hsl(hue(#f00), 100%, 85%)
122
+ $ignored: hsl(0, 0, 85%)
123
+
124
+ // *** Common template ***
125
+ main
126
+ @extend %center
127
+ padding-top: 10px
128
+ .overlay
129
+ width: 100%
130
+ position: fixed
131
+ top: 0
132
+ z-index: 9999
133
+ .center
134
+ position: relative
135
+ @extend %center
136
+
137
+ header
138
+ width: 100%
139
+ position: relative
140
+ top: 0
141
+ background: #24292e
142
+ color: rgba(255,255,255,0.75)
143
+ padding-bottom: 4px
144
+
145
+ .content
146
+ position: relative
147
+ @extend %center
148
+
149
+ .info
150
+ padding: 4px 0 4px 0
151
+ color: #88d483
152
+ .deep-cover
153
+ transition: opacity 0.4s
154
+ opacity: 0.3
155
+ font-size: 24px
156
+ font-weight: 900
157
+ .deep
158
+ color: #74b770
159
+ .version
160
+ transition: opacity 0.4s
161
+ opacity: 0.1
162
+ font-size: 12px
163
+ font-weight: lighter
164
+ &:hover
165
+ .deep-cover
166
+ opacity: 0.8
167
+ .version
168
+ opacity: 0.6
169
+
170
+ // *** DeepCover Source ***
171
+ body.source &
172
+ .nav
173
+ a
174
+ color: white
175
+ body.source
176
+ .stats
177
+ th, td
178
+ padding: 6px
179
+ border-right: 1px solid $light-gray
180
+ td
181
+ padding-top: 0
182
+ table
183
+ @extend %light-border
184
+ position: absolute
185
+ background: white
186
+ right: 20px
187
+ top: 15px
188
+ color: $pl-s
189
+ .ignored
190
+ opacity: 0.7
191
+ &::before
192
+ content: "[+"
193
+ &::after
194
+ content: "]"
195
+
196
+ main
197
+ white-space: pre
198
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace
199
+ font-size: 12px
200
+ line-height: 20px
201
+
202
+ table.source
203
+ @extend %light-border
204
+ width: 100%
205
+
206
+ td
207
+ vertical-align: top
208
+
209
+ .nb
210
+ color: rgba(27,31,35,0.3)
211
+ text-align: right
212
+ width: 50px
213
+ padding-right: 10px
214
+ padding-left: 10px
215
+ // * Color scheme *
216
+
217
+ // Default color, for unexecutable code
218
+ color: $pl-c
219
+
220
+ // Executed code
221
+ .run
222
+ color: $pl-s
223
+
224
+ .not-run
225
+ background-color: $not-run
226
+ padding: 2px 0
227
+
228
+ .ignored
229
+ background-color: $ignored
230
+ padding: 2px 0
231
+
232
+ .kind-keyword, .kind-else
233
+ color: $pl-k
234
+
235
+ .node-def.kind-name
236
+ color: #3e920c
237
+
238
+ // Literals:
239
+ .kind-expression, .node-const
240
+ color: $pl-c1
241
+
242
+ .node-send
243
+ color: $dark-green
244
+
245
+ .node-str
246
+ color: $pl-s
247
+
248
+ // Icons
249
+ .icon-not-run
250
+ background-color: $not-run
251
+ border-color: red
252
+ .icon-ignored
253
+ background-color: $ignored
254
+ border-color: darken($ignored, 20%)
255
+ .icon
256
+ position: absolute
257
+ left: -2.2ex
258
+ width: 2ex
259
+ height: 1.5em
260
+ line-height: 1.5em
261
+ text-align: center
262
+ background-color: white
263
+ padding: 0
264
+ @extend %light-border
265
+
266
+ .kind-empty
267
+ position: relative
268
+ & > .empty-icon
269
+ @extend .icon
270
+ left: -2.5ex
271
+ width: 2.3ex
272
+ &.not-run > .empty-icon
273
+ @extend .icon-not-run
274
+
275
+ // Branches
276
+ .fork
277
+ position: relative
278
+ .fork-icon
279
+ @extend .icon
280
+ .fork-whole
281
+ & > .fork-icon
282
+ z-index: 1
283
+ &:hover
284
+ @extend %show-fork-icon
285
+ background: $light-gray
286
+ padding: 2px 0
287
+
288
+ .fork-with-uncovered-branches
289
+ &.fork-not-run > .fork-icon
290
+ @extend .icon-not-run
291
+ &.fork-ignored > .fork-icon
292
+ @extend .icon-ignored
293
+
294
+ &.show-forks
295
+ @extend %show-fork-icon
296
+ .fork-whole
297
+ background: $light-gray
298
+ .stats .branch
299
+ background: $toggled
300
+
301
+ // *** Index ***
302
+
303
+ .tree
304
+ // Overrides
305
+ .jstree-table-cell
306
+ padding-left: 0
307
+ span
308
+ padding-right: 4px
309
+ .jstree-table-header-cell
310
+ // Leave space for sort arrows:
311
+ padding-right: 17px
312
+ font-style: italic
313
+ .jstree-table-sort-icon
314
+ right: 4px
315
+ top: 8px
316
+ left: inherit
317
+ .jstree-table-separator
318
+ border: none
319
+
320
+ .jstree-table-column
321
+ text-align: right
322
+ border: gray 1px #ddd
323
+ .jstree-table-column-0
324
+ text-align: left
325
+
326
+ .percent-100
327
+ color: green
328
+ .percent-90
329
+ color: blue
330
+ .percent-80
331
+ color: brown
332
+ .percent-60, .percent-70
333
+ color: orange
334
+ .percent-00, .percent-10, .percent-20, .percent-30, .percent-40, .percent-50
335
+ color: red
336
+
337
+ footer
338
+ display: none