Add d3 treemap of command logger data

This commit is contained in:
Kevin Sawicki
2012-12-27 16:46:48 -08:00
parent 1723aba993
commit bd68dc618a
4 changed files with 7925 additions and 28 deletions

View File

@@ -38,13 +38,3 @@ describe "CommandLogger", ->
expect(commandLogger.eventLog['core:backspace'].count).toBe 1
rootView.trigger 'command-logger:clear-data'
expect(commandLogger.eventLog['core:backspace']).toBeUndefined()
describe "when the command logger is toggled", ->
it "displays all the commands triggered", ->
editor.trigger 'core:backspace'
editor.trigger 'core:backspace'
rootView.trigger 'command-logger:toggle'
expect(rootView.find('.command-logger > li > .event-count:eq(0)')).toHaveText '2'
expect(rootView.find('.command-logger > li > .event-description:eq(0)')).toHaveText 'Core: Backspace'
expect(rootView.find('.command-logger > li > .event-count:eq(1)')).toHaveText '1'
expect(rootView.find('.command-logger > li > .event-description:eq(1)')).toHaveText 'Command Logger: Toggle'

View File

@@ -2,6 +2,7 @@
ScrollView = require 'scroll-view'
$ = require 'jquery'
_ = require 'underscore'
d3 = require 'd3.v3'
module.exports =
class CommandLogger extends ScrollView
@@ -9,7 +10,9 @@ class CommandLogger extends ScrollView
@instance = new CommandLogger(rootView, state?.eventLog)
@content: (rootView) ->
@ol class: 'command-logger', tabindex: -1
@div class: 'command-logger', tabindex: -1, =>
@h1 class: 'category-header', outlet: 'categoryHeader'
@div class: 'tree-map', outlet: 'treeMap'
@serialize: ->
@instance.serialize()
@@ -45,18 +48,105 @@ class CommandLogger extends ScrollView
else
@attach()
getHtml: ->
sorted = _.sortBy(@eventLog, (event) => -event.count)
$$$ ->
for eventName, details of sorted
@li =>
@span "#{details.count}", class: 'event-count'
@span "#{_.humanizeEventName(details.name)}", class: 'event-description'
@span "Last run on #{new Date(details.lastRun).toString()}", class: 'event-last-run'
createRootChildren: ->
categories = {}
for eventName, details of @eventLog
categoryStart = eventName.indexOf(':')
categoryName = _.humanizeEventName(eventName.substring(0, categoryStart))
category = categories[categoryName]
unless category
category = name: _.humanizeEventName(categoryName), children: []
categories[categoryName] = category
category.children.push
name: "#{_.humanizeEventName(eventName.substring(categoryStart + 1))} (#{details.count})"
size: details.count
_.toArray(categories)
addTreeMap: ->
root = {}
root.name = "All"
root.children = @createRootChildren()
node = root
@treeMap.empty()
w = @treeMap.width()
h = @treeMap.height()
x = d3.scale.linear().range([0, w])
y = d3.scale.linear().range([0, h])
color = d3.scale.category20()
setCategoryHeader = (node) =>
@categoryHeader.text("#{node.name} Commands")
setCategoryHeader(root)
zoom = (d) ->
setCategoryHeader(d)
kx = w / d.dx
ky = h / d.dy
x.domain([d.x, d.x + d.dx])
y.domain([d.y, d.y + d.dy])
t = svg.selectAll('g.node').transition()
.duration(750)
.attr('transform', (d) -> "translate(#{x(d.x)},#{y(d.y)})")
t.select('rect')
.attr('width', (d) -> kx * d.dx - 1)
.attr('height', (d) -> ky * d.dy - 1)
t.select('text')
.attr('x', (d) -> kx * d.dx / 2)
.attr('y', (d) -> ky * d.dy / 2)
.style('opacity', (d) -> if kx * d.dx > d.w then 1 else 0)
node = d
d3.event.stopPropagation()
treemap = d3.layout.treemap()
.round(false)
.size([w, h])
.sticky(true)
.value((d) -> d.size)
svg = d3.select('.command-logger .tree-map')
.append('div')
.style('width', "#{w}px")
.style('height', "#{h}px")
.append('svg:svg')
.attr('width', w)
.attr('height', h)
.append('svg:g')
.attr('transform', 'translate(.5,.5)')
nodes = treemap.nodes(root).filter((d) -> not d.children)
cell = svg.selectAll('g')
.data(nodes)
.enter()
.append('svg:g')
.attr('class', 'node')
.attr('transform', (d) -> "translate(#{d.x},#{d.y})")
.on('click', (d) -> if node is d.parent then zoom(root) else zoom(d.parent))
cell.append('svg:rect')
.attr('width', (d) -> d.dx - 1)
.attr('height', (d) -> d.dy - 1)
.style('fill', (d) -> color(d.parent.name))
cell.append('svg:text')
.attr('x', (d) -> d.dx / 2)
.attr('y', (d) -> d.dy / 2)
.attr('dy', '.35em')
.attr('text-anchor', 'middle')
.text((d) -> d.name)
.style('opacity', (d) -> d.w = this.getComputedTextLength(); if d.dx > d.w then 1 else 0)
d3.select('.command-logger').on('click', -> zoom(root))
attach: ->
@rootView.append(this)
@html(@getHtml())
@addTreeMap()
@focus()
detach: ->

View File

@@ -5,21 +5,26 @@
top: 0px;
left: 0px;
background: #1e1e1e;
color: #d2d2d2;
color: #eee;
overflow: auto;
opacity: 0.85;
z-index: 99;
padding: 20px;
}
.command-logger li {
padding: 5px;
.command-logger .category-header {
text-align: center;
padding-bottom: 10px;
}
.command-logger .event-count {
padding-right: 10px;
.command-logger .tree-map {
margin: auto;
position: relative;
width: 960px;
height: 700px;
background-color: #efefef;
}
.command-logger .event-description {
padding-right: 10px;
.command-logger .node text {
font-size: 10px;
fill: #111;
}

7812
vendor/d3.v3.js vendored Normal file

File diff suppressed because it is too large Load Diff