Created
May 19, 2014 20:41
-
-
Save daveism/fc803799036637049fd2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta name="description" content=""> | |
<meta name="author" content=""> | |
<title>Budget Differences</title> | |
<style> | |
.chart rect { | |
fill: steelblue; | |
} | |
.chart text { | |
fill: white; | |
font: 10px sans-serif; | |
text-anchor: end; | |
} | |
</style> | |
<style> | |
.chart div rect { | |
font: 10px sans-serif; | |
background-color: steelblue; | |
text-align: right; | |
padding: 3px; | |
margin: 1px; | |
color: white; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="budgetdiff.js"></script> | |
<!-- <div class="chart"/>--> | |
<br/> | |
<style> | |
#budgetDiffContainer {background: gray; width: 1000px; height: 200px; overflow: auto;} | |
.interactionPanel {float: left;} | |
.tt {display: block; } | |
</style> | |
<div id="budgetDiffContainer"> | |
<div> <em>See What's Changed</em> </div> | |
<div id="mainFlow" class="interactionPanel" style="background-color:yellow;width:450px;height:175px;border:1px solid #000"> | |
</div> | |
<div class="interactionPanel" style="background-color:pink;width:450px;height: 175px; border:1px solid #000"><em>This is a rectangle!</em><br/> | |
<svg class="chart" id="chart" width="420" height="120"> | |
<circle cx="40" cy="40" r="24" style="stroke:#006600; fill:#00cc00"/> | |
<g transform="translate(100,100)"> | |
<rect width="40" height="19"/> | |
</g> | |
</svg> | |
</div> | |
<script> | |
<!-- Fund,Department,Division,Account,Amount, --> | |
d3.csv("budgetdiffs.csv", forceAmountType, afterRead); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var diffData = [], currentData = [], fundList = []; | |
var currentState = "select"; // select or show | |
var currentLevel = 0, maxLevel = 3, baseChildCount = -1, baseSvgChildCount = -1; | |
var hierarchyNames = ["Fund", "Department", "Division", "Account"]; | |
var accountToggle = false; | |
function getSelection (prune) { | |
var hTag, i; | |
/* | |
* Reset to full dataset or prune data to selected category | |
*/ | |
if (prune == null) { // Reset data | |
currentData = diffData; | |
} | |
else { // Pull out rows associated with the selected item | |
var tmpData = []; | |
var prevLevel = currentLevel - 1; | |
var j=0; | |
hTag = hierarchyNames[currentLevel-1]; | |
for (i=0; i<currentData.length; ++i) { | |
if (currentData[i][hTag] == prune) { | |
tmpData[j++] = currentData[i]; | |
} | |
} | |
currentData = tmpData; | |
} | |
if (currentState == "select") { | |
var ready = false; | |
while (!ready) { | |
var names = {}; | |
hTag = hierarchyNames[currentLevel]; | |
for (i=0; i<currentData.length; ++i) { | |
if (names[currentData[i][hTag]] == undefined) | |
names[currentData[i][hTag]] = 1; | |
else | |
names[currentData[i][hTag]] += 1; | |
} | |
var object = {}; | |
fundList = []; | |
fundList[0] = "- All " + hTag + "s -"; | |
i=1; | |
for (fname in names) { | |
fundList[i++] = fname; | |
} | |
if (fundList.length > 2) { // No point in making the user select if there's only 1 real selection | |
fundList.sort(); | |
ready = true; | |
} | |
else { | |
++currentLevel; | |
if (currentLevel == maxLevel) { | |
currentState = "show"; | |
ready = true; | |
} | |
} | |
} | |
} | |
} | |
function setUpState() { | |
var container = document.getElementById("mainFlow"); | |
if (container != undefined) { | |
if (baseChildCount < 0) baseChildCount = container.childNodes.length; // Initialize count the first time here. | |
// Destroy all the dynamically created elements on this pane | |
while (container.lastChild != null && container.childNodes.length > baseChildCount) { | |
container.removeChild(container.lastChild); | |
} | |
// Now construct the new pane | |
if (currentState == "select") { // User is selecting which data to look at | |
var element = document.createElement("EM"); | |
element.setAttribute("class", "tt"); | |
element.appendChild(document.createTextNode("Select " + hierarchyNames[currentLevel] + ": ")); | |
container.appendChild(element); | |
var selector = document.createElement("SELECT"); | |
selector.setAttribute("id", "pick") | |
selector.setAttribute("class", "tt"); | |
for (var j=0; j<fundList.length; ++j) { | |
var defSelected = false; | |
if (j==0) defSelected = true; | |
selector.add(new Option(fundList[j], fundList[j], defSelected, defSelected)); | |
} | |
container.appendChild(selector); | |
// Next button | |
var button = document.createElement("INPUT"); | |
button.setAttribute("class", "tt"); | |
button.setAttribute("type", "button"); | |
button.setAttribute("value", "Next"); | |
button.setAttribute("onClick", "buttonClick(\"next\")"); | |
container.appendChild(button); | |
// Reset button | |
if (currentLevel > 0) { | |
button = document.createElement("INPUT"); | |
button.setAttribute("class", "tt"); | |
button.setAttribute("type", "button"); | |
button.setAttribute("value", "Start Over"); | |
button.setAttribute("onClick", "buttonClick(\"reset\")"); | |
container.appendChild(button); | |
} | |
} | |
else if (currentState == "show") { | |
// Construct an array | |
var mapTag = null; | |
if (currentLevel == maxLevel || accountToggle) { | |
mapTag="Account"; | |
} | |
else { | |
mapTag = hierarchyNames[currentLevel]; | |
} | |
var names = {}; | |
for (var i=0; i<currentData.length; ++i) { | |
var object = {} | |
if (names[currentData[i][mapTag]] == undefined) { | |
object.name = currentData[i][mapTag]; | |
object.Amount = 0.00; | |
names[currentData[i][mapTag]] = object; | |
} | |
else { | |
object = names[currentData[i][mapTag]]; | |
} | |
object.Amount += currentData[i].Amount; | |
} | |
var dataArray = [], tmpArray = []; | |
var i = 0, j=0; | |
for (var nm in names) { | |
var obj = names[nm]; | |
tmpArray[i++] = obj; | |
} | |
tmpArray.sort(reverseAbsCompare); | |
var minValue = 1.e7, maxValue = -1.e7; | |
for (i=0; i<tmpArray.length && i < 10; ++i) { | |
if (Math.abs(tmpArray[i].Amount > 1.)) { | |
dataArray[j++] = tmpArray[i]; | |
if (tmpArray[i].Amount < minValue) minValue = tmpArray[i].Amount; | |
if (tmpArray[i].Amount > maxValue) maxValue = tmpArray[i].Amount; | |
} | |
} | |
var width = 420.; | |
var delta = 1.1*(maxValue - minValue); | |
minValue = minValue - 0.05 * (maxValue - minValue); | |
var stepArray = []; | |
for (i=0; i<dataArray.length; ++i) { | |
stepArray[i] = Math.round(width*(dataArray[i].Amount - minValue)/delta); | |
} | |
/* | |
* Dave - here is where I'm adding rectangles. The bit of code above is | |
* incomplete ... I'll be computing scale factors, offsets, etc. For now I'm | |
* just trying to add a rectangle inside a <g/> element. | |
*/ | |
var svgChart = document.getElementById("chart"); | |
if (baseSvgChildCount < 0) baseSvgChildCount = svgChart.childNodes.length; // Initialize count the first time here. | |
while (svgChart.lastChild != null && svgChart.childNodes.length > baseSvgChildCount) { | |
svgChart.removeChild(svgChart.lastChild); | |
} | |
for (i=0; i<dataArray.length; ++i) { | |
var g = document.createElementNS("http://www.w3.org/2000/svg", "g"); | |
g.setAttribute("transform", "translate(40," + (i+1)*40 + ")"); | |
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | |
rect.setAttribute("height", "19"); | |
rect.setAttribute("width", "40"); | |
g.appendChild(rect); | |
svgChart.appendChild(g); | |
break; // Just do the one while debugging. | |
} | |
} | |
} | |
} | |
function reverseAbsCompare (a, b) { // reverse sort | |
// var amt1 = (a.Amount>0.0)?a.Amount:(-a.Amount); | |
var amt1 = Math.abs(a.Amount); | |
var amt2 = (b.Amount>0.0)?b.Amount:(-b.Amount); | |
if (amt1 < amt2) | |
return 1; | |
if (amt1 > amt2) | |
return -1; | |
return 0; | |
} | |
function buttonClick(instruction) { | |
var selector = document.getElementById("pick"); | |
if (instruction == "reset") { | |
currentState = "select"; | |
currentLevel = 0; | |
getSelection(null); | |
} | |
else if (instruction == "next") { | |
if (selector.selectedIndex == 0) { | |
currentState = "show"; | |
alert("Setting currentState to show because ALL"); | |
} | |
else if (currentLevel == maxLevel-1) { | |
++currentLevel; | |
alert("Setting currentState to show because MAXLEVEL"); | |
currentState = "show"; | |
getSelection(selector.options[selector.selectedIndex].value) | |
} | |
else { | |
alert("Calling getSelection at normal"); | |
++currentLevel; | |
getSelection(selector.options[selector.selectedIndex].value) | |
} | |
} | |
setUpState(); | |
} | |
function forceAmountType(d) { | |
d.Amount = +d.Amount; // coerce to number | |
return d; | |
} | |
function afterRead (error, data) { | |
diffData = data; | |
currentState = "select"; | |
currentLevel = 0; | |
if (true) { | |
getSelection(null); | |
} | |
else { | |
var fundNames = {}; | |
var i = 0; | |
for (i=0; i<data.length; ++i) { | |
if (fundNames[data[i].Fund] == undefined) fundNames[data[i].Fund] = 1; | |
} | |
var object = {}; | |
fundList[0] = "All Funds"; | |
i=1; | |
for (fname in fundNames) { | |
fundList[i++] = fname; | |
} | |
fundList.sort(); | |
} | |
setUpState(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Fund | Department | Division | Account | Amount | Annotation | |
---|---|---|---|---|---|---|
General Fund | Public Works Department | Streets & Sidewalks Division | Group Insurance Expense | 0.0 |