Skip to content

Instantly share code, notes, and snippets.

@Kineolyan
Created June 24, 2017 14:36
Show Gist options
  • Save Kineolyan/78ad838b4426a46b050596d330fc5530 to your computer and use it in GitHub Desktop.
Save Kineolyan/78ad838b4426a46b050596d330fc5530 to your computer and use it in GitHub Desktop.
fj-agent
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style id="jsbin-css">
.bar-container {
height: 15px;
}
.bar-container + .bar-container {
border-top: 1px solid black;
}
.bar {
display: inline-block;
height: 15px;
background-color: green;
margin-bottom: 0;
margin-top: 0;
}
.node-bar {
background-color: red;
}
.selected-container .node-bar {
background-color: purple;
}
</style>
</head>
<script src="https://fb.me/react-15.1.0.js"></script>
<script src="https://fb.me/react-dom-15.1.0.js"></script>
<body>
<div id="root"></div>
<script id="jsbin-javascript">
const el = React.createElement;
const elp = (type, ...content) => el(type, null, ...content);
const roots = [
{
id: 'sync1',
name: 'Sync 1',
start: -1,
stop: 10,
children: [
{
id: 'cc11',
name: 'CC 11',
start: 20,
stop: 30,
children: []
}, {
id: 'cc12',
name: 'CC 12',
start: 25,
stop: 50,
children: [
{
id: 'cc121',
name: 'CC 121',
start: 30,
stop: 121,
children: []
}, {
id: 'cc122',
name: 'CC 122',
start: 40,
stop: 76,
children: []
}
]
}
]
},
{
id: 'sync2',
name: 'Sync 2',
start: 135,
stop: 209,
children: [
{
id: 'cc21',
name: 'CC 21',
start: 145,
stop: 456,
children: []
}, {
id: 'cc22',
name: 'CC 22',
start: 153,
stop: 178,
children: []
}
]
}
];
const nodes = {};
function processNodes(v) {
v.forEach(n => {
nodes[n.id] = n;
// Convert time from secs to nanosecs.
if (n.start > 0) { n.start *= 1000000; }
n.stop *= 1000000;
n.duration = n.start > 0 ? n.stop - n.start : 0;
processNodes(n.children);
});
}
processNodes(roots);
// Start exportable code from there
function exploreNodes(node, action) {
const nodes = [node];
let it;
while ((it = nodes.shift()) !== undefined) {
if (action(it) === false) {
return;
}
nodes.push(...it.children);
}
}
function getNodeFrame(node) {
return {
start: node.start > 0 ? node.start : node.stop,
stop: node.stop
};
}
function getNodeWindow(node) {
let start = Number.MAX_VALUE;
let stop = 0;
exploreNodes(node, n => {
const nodeTime = getNodeFrame(n);
if (nodeTime.start > 0 && nodeTime.start < start) {
start = nodeTime.start;
}
if (nodeTime.stop > stop) {
stop = nodeTime.stop;
}
});
if (start < 0) { start = stop; }
return {start, stop};
}
class Analysis extends React.Component {
constructor(props) {
super(props);
this.state = {
needle: '',
timeNode: null
};
this.cbks = {
updateNeedle: event => this.setState({needle: event.target.value}),
setTimeNode: nodeId => this.setState({timeNode: nodeId})
};
}
renderFilters() {
return elp(
'div',
el('input', {
placeholder: 'Class to search',
value: this.state.needle,
onChange: this.cbks.updateNeedle
}));
}
renderTimeline() {
if (this.state.timeNode) {
const node = this.props.nodes[this.state.timeNode];
return el(Timeline, {node});
}
}
renderChains() {
const needle = this.state.needle;
let roots = this.props.roots;
if (needle) {
roots = roots.filter(r => {
let toKeep = false;
exploreNodes(r, n => {
if (n.name.includes(needle)) {
toKeep = true;
return false;
}
})
return toKeep;
})
}
return el(Chains, {roots, filter: {needle}, onTimeNode: this.cbks.setTimeNode});
}
render() {
return elp(
'div',
this.renderFilters(),
this.renderChains(),
this.renderTimeline());
}
}
class Chains extends React.Component {
renderNode(node) {
const {start, stop} = getNodeWindow(node);
let nameDisplay;
const needle = this.props.filter.needle;
if (needle) {
let needleIdx = 0;
let idx;
const parts = [];
while ((idx = node.name.indexOf(needle, needleIdx)) >= 0) {
parts.push(node.name.substring(needleIdx, idx));
parts.push(el('span', {style: {backgroundColor: 'yellow'}}, needle));
needleIdx = idx + needle.length;
}
parts.push(node.name.substring(needleIdx));
nameDisplay = elp('b', ...parts);
} else {
nameDisplay = elp('b', node.name);
}
return elp(
'div',
nameDisplay,
` (${node.id})`,
el('span', {onClick: () => this.props.onTimeNode(node.id)}, ' (-:)'),
el('br'),
this.renderTimings(node.start, node.stop, node.duration),
el('br'),
node.children.length > 0
? `Operation ${this.renderTimings(start, stop, stop - start)}`
: null);
}
renderTimings(start, stop, duration) {
start = start > 0 ? start : stop;
return `${this.renderNsTime(duration)} ms [ ${this.renderNsTime(start)} -> ${this.renderNsTime(stop)} ]`
}
renderNsTime(time) {
return (time / 1000000).toFixed(2);
}
renderNodes(nodes) {
if (nodes.length > 0) {
const items = nodes.map(node => el(
'li',
{key: node.id},
this.renderNode(node),
this.renderNodes(node.children)));
return elp('ul', items);
}
}
renderData() {
return elp(
'div',
this.props.roots ? this.renderNodes(this.props.roots) : null);
}
render() {
return elp(
'div',
this.renderData());
}
}
class Timeline extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedNode: null
};
}
renderBar(min, max, node) {
const {start, stop} = getNodeFrame(node);
const range = max - min;
const barParts = [];
if (range !== 0) {
let values = [
start - min,
stop - start,
max - stop
]
// .map(size => size + 0.02 * range);
const total = values.reduce((a, b) => a + b, 0);
let percents = values
.map(size => 100 * size / total)
.map(percent => `${percent.toFixed(2)}%`);
// if (values[1] < 1) {
// percents = percents.map(p => `calc(${p} - 2px)`);
// percents[1] = '4px';
// }
// console.log(values, percents)
const [beforeP, barP, afterP] = percents;
const before = el(
'div',
{style: {width: beforeP}, className: 'bar'});
const bar = el(
'div',
{style: {width: barP}, className: 'bar node-bar'});
const after = el(
'div',
{style: {width: afterP}, className: 'bar'});
barParts.push(before, bar, after);
} else {
barParts.push(
el(
'div',
{
style: {width: '100%'},
className: 'bar node-bar',
onClick: () => console.log(node.name)}));
}
let containerClasses = 'bar-container';
if (node.id === this.state.selectedNode) {
containerClasses += ' selected-container';
}
return el(
'div',
{className: containerClasses, onClick: () => this.setState({selectedNode: node.id})},
...barParts);
}
renderTimeLine() {
const {start: min, stop: max} = getNodeWindow(this.props.node);
const bars = [];
exploreNodes(this.props.node, node => {
const bar = this.renderBar(min, max, node);
bars.push(bar);
});
return elp(
'div',
...bars);
}
detailSelectedNode() {
if (this.state.selectedNode) {
let node;
exploreNodes(this.props.node, n => {
if (n.id === this.state.selectedNode) {
node = n;
return false;
}
});
return elp(
'div',
elp('b', node.name));
}
}
render() {
return el(
'div',
{width: '100%'},
'Rendering timeline for node ',
elp('b', this.props.node.name),
this.renderTimeLine(),
this.detailSelectedNode());
}
}
ReactDOM.render(
el(Analysis, {roots, nodes}),
document.getElementById('root'));
</script>
<script id="jsbin-source-css" type="text/css">.bar-container {
height: 15px;
}
.bar-container + .bar-container {
border-top: 1px solid black;
}
.bar {
display: inline-block;
height: 15px;
background-color: green;
margin-bottom: 0;
margin-top: 0;
}
.node-bar {
background-color: red;
}
.selected-container .node-bar {
background-color: purple;
}</script>
<script id="jsbin-source-javascript" type="text/javascript">const el = React.createElement;
const elp = (type, ...content) => el(type, null, ...content);
const roots = [
{
id: 'sync1',
name: 'Sync 1',
start: -1,
stop: 10,
children: [
{
id: 'cc11',
name: 'CC 11',
start: 20,
stop: 30,
children: []
}, {
id: 'cc12',
name: 'CC 12',
start: 25,
stop: 50,
children: [
{
id: 'cc121',
name: 'CC 121',
start: 30,
stop: 121,
children: []
}, {
id: 'cc122',
name: 'CC 122',
start: 40,
stop: 76,
children: []
}
]
}
]
},
{
id: 'sync2',
name: 'Sync 2',
start: 135,
stop: 209,
children: [
{
id: 'cc21',
name: 'CC 21',
start: 145,
stop: 456,
children: []
}, {
id: 'cc22',
name: 'CC 22',
start: 153,
stop: 178,
children: []
}
]
}
];
const nodes = {};
function processNodes(v) {
v.forEach(n => {
nodes[n.id] = n;
// Convert time from secs to nanosecs.
if (n.start > 0) { n.start *= 1000000; }
n.stop *= 1000000;
n.duration = n.start > 0 ? n.stop - n.start : 0;
processNodes(n.children);
});
}
processNodes(roots);
// Start exportable code from there
function exploreNodes(node, action) {
const nodes = [node];
let it;
while ((it = nodes.shift()) !== undefined) {
if (action(it) === false) {
return;
}
nodes.push(...it.children);
}
}
function getNodeFrame(node) {
return {
start: node.start > 0 ? node.start : node.stop,
stop: node.stop
};
}
function getNodeWindow(node) {
let start = Number.MAX_VALUE;
let stop = 0;
exploreNodes(node, n => {
const nodeTime = getNodeFrame(n);
if (nodeTime.start > 0 && nodeTime.start < start) {
start = nodeTime.start;
}
if (nodeTime.stop > stop) {
stop = nodeTime.stop;
}
});
if (start < 0) { start = stop; }
return {start, stop};
}
class Analysis extends React.Component {
constructor(props) {
super(props);
this.state = {
needle: '',
timeNode: null
};
this.cbks = {
updateNeedle: event => this.setState({needle: event.target.value}),
setTimeNode: nodeId => this.setState({timeNode: nodeId})
};
}
renderFilters() {
return elp(
'div',
el('input', {
placeholder: 'Class to search',
value: this.state.needle,
onChange: this.cbks.updateNeedle
}));
}
renderTimeline() {
if (this.state.timeNode) {
const node = this.props.nodes[this.state.timeNode];
return el(Timeline, {node});
}
}
renderChains() {
const needle = this.state.needle;
let roots = this.props.roots;
if (needle) {
roots = roots.filter(r => {
let toKeep = false;
exploreNodes(r, n => {
if (n.name.includes(needle)) {
toKeep = true;
return false;
}
})
return toKeep;
})
}
return el(Chains, {roots, filter: {needle}, onTimeNode: this.cbks.setTimeNode});
}
render() {
return elp(
'div',
this.renderFilters(),
this.renderChains(),
this.renderTimeline());
}
}
class Chains extends React.Component {
renderNode(node) {
const {start, stop} = getNodeWindow(node);
let nameDisplay;
const needle = this.props.filter.needle;
if (needle) {
let needleIdx = 0;
let idx;
const parts = [];
while ((idx = node.name.indexOf(needle, needleIdx)) >= 0) {
parts.push(node.name.substring(needleIdx, idx));
parts.push(el('span', {style: {backgroundColor: 'yellow'}}, needle));
needleIdx = idx + needle.length;
}
parts.push(node.name.substring(needleIdx));
nameDisplay = elp('b', ...parts);
} else {
nameDisplay = elp('b', node.name);
}
return elp(
'div',
nameDisplay,
` (${node.id})`,
el('span', {onClick: () => this.props.onTimeNode(node.id)}, ' (-:)'),
el('br'),
this.renderTimings(node.start, node.stop, node.duration),
el('br'),
node.children.length > 0
? `Operation ${this.renderTimings(start, stop, stop - start)}`
: null);
}
renderTimings(start, stop, duration) {
start = start > 0 ? start : stop;
return `${this.renderNsTime(duration)} ms [ ${this.renderNsTime(start)} -> ${this.renderNsTime(stop)} ]`
}
renderNsTime(time) {
return (time / 1000000).toFixed(2);
}
renderNodes(nodes) {
if (nodes.length > 0) {
const items = nodes.map(node => el(
'li',
{key: node.id},
this.renderNode(node),
this.renderNodes(node.children)));
return elp('ul', items);
}
}
renderData() {
return elp(
'div',
this.props.roots ? this.renderNodes(this.props.roots) : null);
}
render() {
return elp(
'div',
this.renderData());
}
}
class Timeline extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedNode: null
};
}
renderBar(min, max, node) {
const {start, stop} = getNodeFrame(node);
const range = max - min;
const barParts = [];
if (range !== 0) {
let values = [
start - min,
stop - start,
max - stop
]
// .map(size => size + 0.02 * range);
const total = values.reduce((a, b) => a + b, 0);
let percents = values
.map(size => 100 * size / total)
.map(percent => `${percent.toFixed(2)}%`);
// if (values[1] < 1) {
// percents = percents.map(p => `calc(${p} - 2px)`);
// percents[1] = '4px';
// }
// console.log(values, percents)
const [beforeP, barP, afterP] = percents;
const before = el(
'div',
{style: {width: beforeP}, className: 'bar'});
const bar = el(
'div',
{style: {width: barP}, className: 'bar node-bar'});
const after = el(
'div',
{style: {width: afterP}, className: 'bar'});
barParts.push(before, bar, after);
} else {
barParts.push(
el(
'div',
{
style: {width: '100%'},
className: 'bar node-bar',
onClick: () => console.log(node.name)}));
}
let containerClasses = 'bar-container';
if (node.id === this.state.selectedNode) {
containerClasses += ' selected-container';
}
return el(
'div',
{className: containerClasses, onClick: () => this.setState({selectedNode: node.id})},
...barParts);
}
renderTimeLine() {
const {start: min, stop: max} = getNodeWindow(this.props.node);
const bars = [];
exploreNodes(this.props.node, node => {
const bar = this.renderBar(min, max, node);
bars.push(bar);
});
return elp(
'div',
...bars);
}
detailSelectedNode() {
if (this.state.selectedNode) {
let node;
exploreNodes(this.props.node, n => {
if (n.id === this.state.selectedNode) {
node = n;
return false;
}
});
return elp(
'div',
elp('b', node.name));
}
}
render() {
return el(
'div',
{width: '100%'},
'Rendering timeline for node ',
elp('b', this.props.node.name),
this.renderTimeLine(),
this.detailSelectedNode());
}
}
ReactDOM.render(
el(Analysis, {roots, nodes}),
document.getElementById('root'));</script></body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment