0x/visualizer/actions.js
2018-08-28 13:08:01 +02:00

201 lines
5.6 KiB
JavaScript

'use strict'
module.exports = createActions
function createActions ({flamegraph, state}, emit) {
state.typeFilters.bgs = state.typeFilters.unhighlighted
const unmergedTags = tagNodesWithIds(state.trees.unmerged)
const mergedTags = tagNodesWithIds(state.trees.merged)
return {
focusNode,
search, control, zoom, typeFilters,
pushState, jumpToState
}
function focusNode () {
const save = pushState()
return (node) => {
const { merged } = state.control
const { nodesToIds } = merged ? mergedTags : unmergedTags
state.focusedNodeId = nodesToIds.get(node)
save()
}
}
function search () {
return ({type, value}) => {
if (type === 'key') {
if (!value) return flamegraph.clear()
flamegraph.search(value, 'cyan')
}
}
}
function highlightTypeFilters () {
return Object.assign(
{},
state.typeFilters.highlighted,
[...Array.from(state.typeFilters.exclude),
(state.typeFilters.enableInlinable ? '' : 'inlinable')
].reduce((o, k) => {
o[k] = state.typeFilters.unhighlighted[k]
return o
}, {}))
}
function control () {
return ({type}) => {
switch (type) {
case 'tiers':
state.control.tiers = !state.control.tiers
flamegraph.tiers(state.control.tiers)
state.typeFilters.bgs = state.control.tiers
? highlightTypeFilters()
: state.typeFilters.unhighlighted
emit(state)
return
case 'view':
state.control.merged = !state.control.merged
state.typeFilters.enableInlinable = !state.control.merged
state.key.enableOptUnopt = !state.control.merged
state.typeFilters.bgs = state.control.tiers
? highlightTypeFilters()
: state.typeFilters.unhighlighted
emit(state)
if (state.control.merged) flamegraph.renderTree(state.trees.merged)
else flamegraph.renderTree(state.trees.unmerged)
return
case 'optimized':
state.control.optimized = !state.control.optimized
// Deactivate the unoptimized button--d3-fg will auto clear its colours
state.control.unoptimized = false
emit(state)
if (!state.control.optimized) return flamegraph.clear('yellow')
flamegraph.search(RegExp('^\\*'), 'yellow')
return
case 'not-optimized':
state.control.unoptimized = !state.control.unoptimized
// Deactivate the optimized button--d3-fg will auto clear its colours
state.control.optimized = false
emit(state)
if (!state.control.unoptimized) return flamegraph.clear('lime')
flamegraph.search(/^~/, 'lime')
}
}
}
function zoom () {
var zoomLevel = 1
return ({type}) => {
switch (type) {
case 'in':
zoomLevel += 0.3
// Some ludicrous max just in case
if (zoomLevel > 10) zoomLevel = 10
flamegraph.setGraphZoom(zoomLevel)
return
case 'out':
zoomLevel -= 0.3
if (zoomLevel < 0.1) zoomLevel = 0.1
flamegraph.setGraphZoom(zoomLevel)
}
}
}
function typeFilters () {
const save = pushState()
return ({name}) => {
const checked = state.typeFilters.exclude.has(name)
if (checked) {
flamegraph.typeShow(name)
state.typeFilters.exclude.delete(name)
} else {
flamegraph.typeHide(name)
state.typeFilters.exclude.add(name)
}
if (state.control.tiers) state.typeFilters.bgs = highlightTypeFilters()
save()
emit(state)
}
}
function pushState () {
return () => {
const { merged } = state.control
const excludeTypes = Array.from(state.typeFilters.exclude)
const historyState = {
merged,
nodeId: state.focusedNodeId,
excludeTypes
}
window.history.pushState(historyState, '', `#${stringifyHistoryState(historyState)}`)
}
}
// Jump to a state based on a history entry.
function jumpToState () {
return ({merged, nodeId, excludeTypes}) => {
state.focusedNodeId = nodeId
state.control.merged = merged
state.typeFilters.enableInlinable = !merged
state.key.enableOptUnopt = !merged
// Diff type exclude state to reach the one described by the entry
const oldExclude = state.typeFilters.exclude
const newExclude = new Set(excludeTypes)
oldExclude.forEach((name) => {
if (!newExclude.has(name)) {
flamegraph.typeShow(name)
}
})
newExclude.forEach((name) => {
if (!oldExclude.has(name)) {
flamegraph.typeHide(name)
}
})
state.typeFilters.exclude = newExclude
state.typeFilters.bgs = state.control.tiers
? highlightTypeFilters()
: state.typeFilters.unhighlighted
flamegraph.renderTree(merged ? state.trees.merged : state.trees.unmerged)
const { idsToNodes } = merged ? mergedTags : unmergedTags
flamegraph.zoom(idsToNodes.get(nodeId))
emit(state)
}
}
}
// This just uses incrementing IDs but it will only
// be used for a single dataset, and it's deterministic enough for that
function tagNodesWithIds (data) {
let id = 0
const idsToNodes = new Map()
const nodesToIds = new Map()
tagNodes(data)
return {
idsToNodes,
nodesToIds
}
function tag (node) {
idsToNodes.set(id, node)
nodesToIds.set(node, id)
id++
}
function tagNodes (node) {
tag(node)
if (node.children) node.children.forEach(tagNodes)
}
}
function stringifyHistoryState (state) {
// Just use JSON I guess
return JSON.stringify(state)
}