<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="description" content="Online Ishikava / Fishbone diagram">
<link rel="stylesheet" type="text/css" media="screen" href="stylesheet.css">
<title>Online Ishikava / Fishbone diagram</title>
<style>
html, body{ margin: 0; padding: 0; overflow: hidden;background: none}
*{ font-family: monospace; }
.note {color:gray;font-size: 0.8em;}
.label-0{ font-size: 2em; }
.label-1{ font-size: 1.5em; fill: #111; }
.label-2{ font-size: 1em; fill: #444; }
.label-3{ font-size: .9em; fill: #888; }
.label-4{ font-size: .8em; fill: #aaa; }
.label-5{ font-size: .8em; fill: #aaa; }
.label-6{ font-size: .8em; fill: #aaa; }
.link-0{ stroke: #000; stroke-width: 2px}
.link-1{ stroke: #333; stroke-width: 1px}
.link-2,
.link-3,
.link-4,
.link-5,
.link-6{ stroke: #666; stroke-width: .5px; }
.link-positive { stroke: blue; }
.link-negative { stroke: red; }
.positive { fill: green; stroke: green; }
.negative { fill: red; stroke: red; }
</style>
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.1/d3.min.js" charset="utf-8"></script>
<script src="./d3.fishbone.js" charset="utf-8"></script>
<script src="./underscore-min.js" charset="utf-8"></script>
<script src="./underscore.string.min.js" charset="utf-8"></script>
</head>
<body>
<!-- HEADER -->
<div id="header_wrap" class="outer">
<header class="inner">
<h2 id="project_tagline">Online Ishikava / Fishbone diagram</h2>
</header>
</div>
<div style="padding: 2px;width: 24%;float:left;min-height: 1000px;">
<p class="note">- Type brainstorm protocol below</p>
<p class="note">- Redraw happens on Enter or use <button id="refresh">Refresh</button></p>
<p class="note">- Use double-click on node to drill down</p>
<p class="note">- Click to <a href="#">get back to root</a></p>
<textarea id="structure" style="width:100%; min-height: 600px;">
A Problem
- People
-- Fatigue
--- Why?
---- Why?
! Comment node with exclamation / @author / + for pros
! Comment node with exclamation / @author / - for cons
- Interactions
-- Precedent
--- Why?
- Processes
-- A lot of meetings
--- Why?
- Materials
-- Slow server
--- Why?
- Environment
-- Market is down
--- Why?
- Requirements
-- Too large stories
--- Why?
</textarea>
</div>
<div id="target" style="border: dotted 1px #000; min-height: 1000px; width: 75%;float:right"></div>
<script>
var drawInContext = function() {
var srcData = getData();
var k = ((window.location.hash || '').substr(1) || srcData[0].k);
var rec = srcData.filter(function (row) {
return row.k === k;
});
if (rec.length === 0) {
return [];
}
var path = rec[0].path + '/' + rec[0].k;
var subX = srcData.filter(function (row) {
return row.path.indexOf(path) === 0;
});
srcData = rec.concat(subX);
drawFishbone(srcData);
};
$('#structure').on('keypress', function (e) {
if (e.keyCode === 13) {
parseSrc();
drawInContext();
}
});
$('#refresh').on('click', function (e) {
parseSrc();
drawInContext();
});
var size = (function () {
var g = window.document.documentElement;
return {
width: $('#target').width(),
height: g.clientHeight - 65
};
});
var times = function (num) {
var r = [];
for (var i = 0; i < num; i++) r.push(i);
return r;
};
var svg;
var fishbone;
var srcData = [];
var feedback = [];
function parseTokenRow(str, i) {
var c = -1;
while (str.charAt(++c) === '-');
var token = str.substr(c).trim();
var parts = token.split('/');
return {
level: c,
key: (parts[0] + i),
name: (parts[0]),
rate: ((parts[1] === '+') ? (+1) : (-1))};
}
function parseCommentRow(str) {
return str
.trim()
.substr(1) // remove !
.split('/')
.map(function (p) {
return p.trim();
})
.reduce(function (memo, p) {
var c = p.charAt(0);
if (c === '+') {
memo.rate = (+1);
} else if (c === '-') {
memo.rate = (-1);
} else if (c === '@') {
memo.author = p.substr(1);
} else {
memo.comment = p;
}
return memo;
}, {});
}
function parseSrc() {
var v = $('#structure').val();
var t = v.split('\n');
var path = [];
feedback = [];
srcData = [];
t
.filter(function (rowStr) {
return !!rowStr.trim();
})
.forEach(function (rowStr, i) {
var s = rowStr.trim();
if (s.charAt(0) === '!') {
// comment
var c = parseCommentRow(s);
feedback.push({
k: path[path.length - 1],
rate: (c.rate || -1),
comment: c.comment,
author: c.author
});
} else {
// structure
var o = parseTokenRow(s, i);
var lvl = o.level;
var key = o.key;
var name = o.name;
var rate = o.rate;
path[lvl] = key;
path = path.slice(0, (lvl + 1));
srcData.push({
"k": key,
"name": name,
"path": ['.'].concat(path.slice(0, lvl)).join('/'),
"rate": rate
});
}
});
}
parseSrc();
var forceLayout = function () {
var s = size();
var force = fishbone.force();
force.size([s.width, s.height]);
force.start();
times(250).forEach(force.tick.bind(force));
force.stop();
force.start();
};
var onDblClickBranch = function (row) {
window.location.hash = ('#' + row.k);
};
var getData = function () {
return JSON.parse(JSON.stringify(srcData));
};
var drawFishbone = function (data) {
// create the configurable selection modifier
fishbone = d3.fishbone(
size(),
function () {
return data;
},
function () {
return feedback;
},
onDblClickBranch,
function () {});
var target = d3.select('#target');
target.select('svg').remove();
svg = target
.append('svg')
.attr(size())
.call(fishbone.defaultArrow)
.call(fishbone);
forceLayout();
};
$(window).on('hashchange', drawInContext);
drawInContext();
// handle resizing the window
d3
.select(window)
.on("resize", function () {
forceLayout();
svg.attr(size());
});
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-67431368-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>