forked from cool-Blue's block: stacked bar chart with dynamic axes and labels
forked from Thanaporn-sk's block: stacked bar chart with dynamic axes and labels
license: mit |
forked from cool-Blue's block: stacked bar chart with dynamic axes and labels
forked from Thanaporn-sk's block: stacked bar chart with dynamic axes and labels
<!DOCTYPE html> | |
<html> | |
<head lang="en"> | |
<meta charset="UTF-8"> | |
<title>http://stackoverflow.com/questions/32057842/d3-js-highlighting-stacked-bar-and-getting-selected-values/32079517#32079517</title> | |
<style> | |
body { | |
position: relative; | |
} | |
#vis { | |
margin: 100px; | |
position: relative; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.fly-in { | |
font-size: 8px; | |
} | |
.axis .tick line { | |
stroke: #ccc; | |
/*opacity: 0.5;*/ | |
pointer-events: none; | |
} | |
/*.axis .minor line{*/ | |
/*stroke: red;*/ | |
/*}*/ | |
.highlight { | |
font-weight: bold ; | |
} | |
svg { | |
overflow: visible; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="UTF-8"></script> | |
<!--<script src="https://rawgit.com/cool-Blue/d3-lib/516508b6aa8d9ae724ceb194257226aa29d48fb7/inputs/select/select.js"></script>--> | |
<script src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/inputs/select/select.js" charset="UTF-8"></script> | |
<script src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/plot/plot-transform-2.0.0/plot-transform.js" charset="UTF-8"></script> | |
<script src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/transitions/end-all/endAll.js" charset="UTF-8"></script> | |
<script src="script.js"></script> | |
</body> | |
</html> |
var width = 760, | |
height = 300, | |
padding = {left: 50, right: 200, top: 20, bottom: 30}, | |
xRangeWidth = width - padding.left - padding.right, | |
yRangeHeight = height - padding.top - padding.bottom; | |
var vis = d3.select("body").append("div").attr({ | |
margin: "auto", | |
id: "vis" | |
}), | |
svg = vis | |
.append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.append("g") | |
.attr("transform", "translate(" + [padding.left, padding.top] + ")"); | |
var dataSet1 = [ | |
{ | |
name: "PC", | |
sales: [{year: 2005, profit: 3000}, | |
{year: 2006, profit: 1300}, | |
{year: 2007, profit: 3700}, | |
{year: 2008, profit: 4900}, | |
{year: 2009, profit: 700}] | |
}, | |
{ | |
name: "SmartPhone", | |
sales: [{year: 2005, profit: 2000}, | |
{year: 2006, profit: 4000}, | |
{year: 2007, profit: 1810}, | |
{year: 2008, profit: 6540}, | |
{year: 2009, profit: 2820}] | |
}, | |
{ | |
name: "Software", | |
sales: [{year: 2005, profit: 1100}, | |
{year: 2006, profit: 1700}, | |
{year: 2007, profit: 1680}, | |
{year: 2008, profit: 4000}, | |
{year: 2009, profit: 4900}] | |
} | |
]; | |
var offsetSelect = d3.ui.select({ | |
base: vis, | |
before: "svg", | |
style: {position: "absolute", left: width - padding.right + 15 + "px", top: yRangeHeight + "px"}, | |
onchange: function() { | |
update(dataSet1) | |
}, | |
data: ["wiggle", "zero", "expand", "silhouette"] | |
}), | |
orderSelect = d3.ui.select({ | |
base: vis, | |
before: "svg", | |
style: {position: "absolute", left: width - padding.right + 15 + "px", top: yRangeHeight - 20 + "px"}, | |
onchange: function() { | |
update(dataSet1) | |
}, | |
data: ["inside-out", "default", "reverse"] | |
}), | |
stack = d3.layout.stack() | |
.values(function(d) { return d.sales; }) | |
.x(function(d) { return d.year; }) | |
.y(function(d) { return d.profit; }) | |
.out(function out(d, y0, y) { | |
d.p0 = y0; | |
d.y = y; | |
} | |
); | |
// x Axis | |
var xPadding = {inner: 0.1, outer: 0.3}, | |
xScale = d3.scale.ordinal() | |
.rangeBands([0, xRangeWidth], xPadding.inner, xPadding.outer), | |
xAxis = d3.cbPlot.d3Axis() | |
.scale(xScale) | |
.orient("bottom"), | |
gX = svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + yRangeHeight + ")"); | |
// y Axis | |
var yAxisScale = d3.scale.linear() | |
.range([yRangeHeight, 0]), | |
yAxis = d3.cbPlot.d3Axis() | |
.scale(yAxisScale) | |
.orient("left") | |
.tickSubdivide(2), | |
gY = svg.append("g") | |
.attr("class", "y axis") | |
.style({"pointer-events": "none", "font-size": "12px"}), | |
yAxisTransition = 1000; | |
var yPlotScale = d3.scale.linear() | |
.range([0, yRangeHeight]); | |
var color = d3.scale.category10(); | |
function update(dataSet) { | |
// create an array of normalised layers and | |
// add the normalised values onto the data | |
var normData = stack.offset("expand")(dataSet) | |
.map(stack.values()) | |
.map(function(s) { | |
return s.map(function(p) {return p.yNorm = p.y}) | |
}), | |
stackedData = stack.offset(offsetSelect.value()) | |
.order(orderSelect.value())(dataSet), | |
maxY = d3.max(stackedData, function(d) { | |
return d3.max(d.sales, function(s) { | |
return s.profit + s.p0 | |
}) | |
}), | |
years = stackedData[0].sales.map(stack.x()), | |
yearlyTotals = years.reduce(function(t, y) { | |
return (t[y] = d3.sum(stackedData, function(o) { | |
return o.sales.filter(function(s) { | |
return s.year == y | |
})[0].profit | |
}), t) | |
}, {}); | |
xScale.domain(years); | |
yAxisScale.reset = function(){ | |
this.domain([0, offsetSelect.value() == "expand" ? 1 : maxY]) | |
.range([yRangeHeight, 0]) | |
.ticks(10) | |
}; | |
yAxisScale.reset(); | |
yPlotScale.domain(yAxisScale.domain()); | |
// plotArea | |
// (svg) -> (g.plotArea)[stackedData] | |
// apply a transform to map screen space to cartesian space | |
// this removes all confusion and mess when plotting data! | |
var plotArea = svg.selectAll(".plotArea") | |
.data([stackedData]); | |
plotArea.enter().insert("g", ".axis") | |
.attr(d3.cbPlot.transplot(yRangeHeight)) | |
.attr("class", "plotArea"); | |
/* | |
plotArea.series | |
(g.plotArea)[stackedData] xF transPlot | |
?data d | |
\ | |
`+-> (g.plotArea.series)[stackedData[0]] | |
: | |
: | |
\ | |
`+-> (g.plotArea.series)[stackedData[m]] | |
*/ | |
plotArea.series = plotArea.selectAll(".series") | |
.data(ID); | |
plotArea.series.enter() | |
.append("g") | |
.attr("class", "series"); | |
plotArea.series.style("fill", function(d, i) { | |
return color(i); | |
}); | |
plotArea.series.exit().remove(); | |
Object.defineProperties(plotArea.series, d3._CB_selection_destructure); | |
/* | |
plotArea.series.components | |
(g.series)[stackedData[0]] | |
?data d3.entries(d) | |
\ | |
`+-> (g.name)[stackedData[0].name] | |
+-> (g.sales)[stackedData[0].sales] | |
: | |
: | |
(g.series)[stackedData[m]] | |
?data d3.entries(d) | |
\ | |
`+-> (g.name)[stackedData[m].name] | |
+-> (g.sales)[stackedData[m].sales] | |
*/ | |
plotArea.series.components = plotArea.series.selectAll(".components") | |
.data(function(d) { | |
return d3.entries(d); | |
}); | |
plotArea.series.components.enter().append("g") | |
.attr("class", function(d){return d.key}) | |
.classed("components", true); | |
plotArea.series.components.exit().remove(); | |
/* | |
plotArea.series.components.values | |
(g.series)[stackedData[0]] | |
\ | |
`+-> (g.sales)[stackedData[0].sales] | |
: | |
: | |
(g.series)[stackedData[m]] | |
\ | |
`+-> (g.sales)[stackedData[m].sales] | |
*/ | |
plotArea.series.components.values = plotArea.series.components.filter(function(d){ | |
return d.key == "sales" | |
}); | |
Object.defineProperties(plotArea.series.components.values, d3._CB_selection_destructure); | |
/* | |
plotArea.series.components.labels | |
(g.series)[stackedData[0]] | |
\ | |
`+-> (g.name)[stackedData[0].name] xF transPlot | |
: | |
: | |
(g.series)[stackedData[m]] | |
\ | |
`+-> (g.name)[stackedData[m].name] xF transPlot | |
*/ | |
plotArea.series.components.labels = plotArea.series.components.filter(function(d){ | |
return d.key == "name" | |
}) | |
// reverse the plotArea transform (it is it's own inverse) | |
.attr(d3.cbPlot.transplot(yRangeHeight)); | |
Object.defineProperties(plotArea.series.components.labels, d3._CB_selection_destructure); | |
var s = xScale.rangeBand(), | |
w = s - xPadding.inner, | |
drag = d3.behavior.drag() | |
.on("dragstart", mouseOver), | |
/* | |
plotArea.series.components.values.points | |
(g.sales)[stackedData[0].sales] * on mouseover; * on mouseout; * bH drag | |
?data d.value | |
\ | |
`+-> (rect.point)[stackedData[0].sales.value[0] | |
: | |
+-> (rect.point)[stackedData[0].sales.value[n] | |
: | |
: | |
(g.sales)[stackedData[m].sales] * on mouseover; * on mouseout; * bH drag | |
?data d.value | |
\ | |
`+-> (rect.point)[stackedData[m].sales.value[0] | |
: | |
+-> (rect.point)[stackedData[m].sales.value[n] | |
*/ | |
points = plotArea.series.components.values.points = plotArea.series.components.values.selectAll("rect") | |
.data(function(d){ | |
return d.value | |
}); | |
points.enter() | |
.append("rect") | |
.attr({width: w, class: "point"}) | |
.on("mouseover", mouseOver) | |
.on("mouseout", mouseOut) | |
.call(drag); | |
points.transition() | |
.attr("x", function(d) { | |
return xScale(d.year); | |
}) | |
.attr("y", function(d) { | |
return yPlotScale(d.p0); | |
}) | |
.attr("height", function(d) { | |
return yPlotScale(d.y); | |
}) | |
.attr("stroke", "white"); | |
points.exit().remove(); | |
Object.defineProperties(plotArea.series.components.values.points, d3._CB_selection_destructure); | |
gX.transition().call(xAxis); | |
gY.transition().call(yAxis); | |
function mouseOver(pointData, pointIndex, groupIndex) { | |
console.log(["in", pointIndex].join("\t")); | |
var selectedYear = pointData.year, | |
// wrap the node in a selection with the proper parent | |
plotData = plotArea.series.components.values.data, | |
seriesData = plotData[groupIndex], | |
currentYear = d3.transpose(plotData)[pointIndex], | |
point = plotArea.series.components.values.points.nodes[groupIndex][pointIndex]; | |
// if the plot is not normalised, fly-in the axis on the selected year | |
if(offsetSelect.value() != "expand") { | |
yAxisScale.reset(); | |
// get the zero offset for the fly-in axis | |
var pMin = d3.min(currentYear, function(s) { | |
return s.p0 | |
}), | |
refP0 = seriesData[pointIndex].p0, | |
selectedGroupHeight = d3.sum(currentYear, function(d) {return d.y}), | |
// set the range and domain height for the selected year | |
localDomain = [0, selectedGroupHeight].map(function(d){return d + pMin - refP0}), | |
localRange = [0, selectedGroupHeight].map(function(d) {return yAxisScale(d + pMin)}); | |
console.log(yAxisScale(pMin)); | |
yAxisScale | |
.domain(localDomain) | |
.range(localRange); | |
// apply the changes to the y axis and manage the ticks | |
gY.transition("axis") | |
.duration(yAxisTransition) | |
.call(yAxis.ticks(+(Math.abs(localRange[0] - localRange[1]) / 15).toFixed())) | |
.attr("transform", "translate(" + point.attr("x") + ",0)") | |
.style({"font-size": "8px"}) | |
.call(function(t) {d3.select(t.node()).classed("fly-in", true)}); | |
// align the selected series across all years | |
points.transition("points") | |
.attr("y", alignY(seriesData[pointIndex].p0, groupIndex)) | |
.call(endAll, toolTip) | |
} else window.setTimeout(toolTip, 0); // if not expand | |
// manage the highlighting | |
// points highlighting | |
plotArea.series.transition("fade") | |
.attr("opacity", function(d, i) { | |
return i == groupIndex ? 1 : 0.5; | |
}); | |
// x axis highlighting | |
d3.selectAll(".x.axis .tick") | |
.filter(function(d) { | |
return d == selectedYear | |
}) | |
.classed("highlight", true); | |
// move the selected element to the front | |
d3.select(this.parentNode) | |
.moveToFront(); | |
gX.moveToFront(); | |
legendText(groupIndex); | |
// Tooltip | |
function toolTip() { | |
plotArea.series | |
.append("g") | |
.attr("class", "tooltip") | |
.attr("transform", "translate(" + [point.attr("x"), point.attr("y")] + ")") | |
.append("text") | |
.attr(d3.cbPlot.transflip()) | |
.text(d3.format(">8.0%")(pointData.yNorm)) | |
.attr({x: "1em", y: -point.attr("height") / 2, dy: ".35em", opacity: 0}) | |
.transition("tooltip").attr("opacity", 1) | |
.style({fill: "black", "pointer-events": "none"}) | |
} | |
} | |
function mouseOut(d, nodeIndex, groupIndex) { | |
console.log(["out", nodeIndex].join("\t")); | |
var year = d.year; | |
d3.selectAll(".x.axis .tick") | |
.filter(function(d) { | |
return d == year | |
}) | |
.classed("highlight", false); | |
plotArea.series.transition("fade") | |
.attr({opacity: 1}); | |
var g = plotArea.series.components.labels.nodes[groupIndex][0].select("text"); | |
g.classed("highlight", false); | |
g.text(g.text().split(":")[0]) | |
yAxisScale.reset(); | |
gY.selectAll(".minor").remove(); | |
gY.transition("axis").call(yAxis) | |
.attr("transform", "translate(0,0)") | |
.style({"font-size": "12px"}) | |
.call(function(t) {d3.select(t.node()).classed("fly-in", false)}); | |
plotArea.series.selectAll(".tooltip") | |
.transition("tooltip") | |
.attr({opacity: 0}) | |
.remove(); | |
points.transition("points").attr("y", function(d) { | |
return yPlotScale(d.p0); | |
}) | |
}; | |
/* | |
plotArea.series.components.labels | |
(g.name)[stackedData[0].name] | |
?data | |
\ | |
`+-> (g.label)[stackedData[0].name.value xF transPlot | |
: | |
: | |
(g.name)[stackedData[m].name] | |
?data | |
\ | |
`+-> (g.label)[stackedData[m].name.value xF transPlot | |
*/ | |
// Add the legend inside the series containers | |
// The series legend is wrapped in another g so that the | |
// plot transform can be reversed. Otherwise the text would be mirrored | |
var labHeight = 40, | |
labRadius = 10; | |
/* | |
plotArea.series.components.labels.circles | |
(g.name)[stackedData[0].name] xF transPlot | |
?data [d.value] | |
\ | |
`+-> (circle)[stackedData[0].name.value] | |
: | |
: | |
(g.name)[stackedData[m].name] xF transPlot | |
?data [d.value] | |
\ | |
`+-> (circle)[stackedData[0].name.value] | |
*/ | |
// add the marker and the legend text to the normalised container | |
// push the stackedData (name) down to them | |
var labelCircle = plotArea.series.components.labels.selectAll("circle") | |
.data(function(d){return [d.value]}), | |
// take a moment to get the series order delivered by stack | |
orders = stackedData.map(function(d) { // simplify the form | |
return {name: d.name, base: d.sales[0].p0} | |
}).sort(function(a, b) { // get a copy, sorted by p0 | |
return a.base - b.base | |
}).map(function(d) { // convert to index permutations | |
return stackedData.map(function(p) { | |
return p.name | |
}).indexOf(d.name) | |
}).reverse(); // convert to screen y ordinate | |
labelCircle.enter().append("circle") | |
.on("mouseover", function(pointData, pointIndex, groupIndex) { | |
var node = this, | |
typicalP0 = d3.median(plotArea.series.components.values.data[groupIndex], | |
function(d){return d.p0}); | |
plotArea.series.components.values.points.transition("points") | |
.attr("y", alignY(typicalP0, groupIndex)); | |
plotArea.series.transition("fade") | |
.attr("opacity", function(d) { | |
return d === d3.select(node.parentNode.parentNode).datum() ? 1 : 0.5; | |
}); | |
legendText(groupIndex); | |
}) | |
.on("mouseout", function(pointData, pointIndex, groupIndex) { | |
plotArea.series.transition("fade") | |
.attr({opacity: 1}); | |
plotArea.series.components.values.points.transition("points").attr("y", function(d) { | |
return yPlotScale(d.p0); | |
}) | |
}); | |
labelCircle.attr("cx", xRangeWidth + 20) | |
.attr("cy", function(d, i, j) { | |
return labHeight * orders[j]; | |
}) | |
.attr("r", labRadius); | |
/* | |
plotArea.series.components.labels.text | |
(g.name)[stackedData[0].name] xF transPlot | |
?data [d.value] | |
\ | |
`+-> (text)[stackedData[0].name.value] | |
: | |
: | |
(g.name)[stackedData[m].name] xF transPlot | |
?data [d.value] | |
\ | |
`+-> (text)[stackedData[0].name.value] | |
*/ | |
var labelText = plotArea.series.components.labels.selectAll("text") | |
.data(function(d){return [d.value]}); | |
labelText.enter().append("text"); | |
labelText.attr("x", xRangeWidth + 40) | |
.attr("y", function(d, i, j) { | |
return labHeight * orders[j]; | |
}) | |
.attr("dy", labRadius / 2) | |
.text(function(d) { | |
return d; | |
}); | |
function legendText(groupIndex){ | |
// Legend text | |
// add the value for the moused over item to the legend text and | |
// highlight it | |
var labelText = plotArea.series.components.labels.nodes[groupIndex][0].select("text"), | |
seriesData = plotArea.series.components.values.data[groupIndex], | |
fmt = [">8,.0f", ">8.0%"][(offsetSelect.value() == "expand") * 1]; | |
labelText.classed("highlight", true); | |
labelText.text(labelText.datum().value + ": " + d3.format(fmt)( | |
offsetSelect.value() != "expand" ? | |
d3.sum(seriesData, stack.y()) : | |
d3.sum(seriesData, function(s) { | |
var totalSales = d3.sum(d3.values(yearlyTotals)); | |
return s.y * yearlyTotals[s.year] / totalSales | |
}) | |
)); | |
} | |
function alignY(p0, series) { | |
var offsets = plotArea.series.components.values.data[series].map(function(d) { | |
return p0 - d.p0; | |
}); | |
return function(d, i) { | |
return yPlotScale(d.p0 + offsets[i]); | |
} | |
} | |
function aID(d) { | |
return [d]; | |
} | |
function ID(d) { | |
return d; | |
} | |
} | |
d3.selection.prototype.moveToFront = function() { | |
return this.each(function() { | |
this.parentNode.appendChild(this); | |
}); | |
}; | |
d3._CB_selection_destructure = { | |
"nodes": { | |
get: function() { | |
return this.map(function(g) { | |
return g.map(function(n) { | |
return d3.select(n) | |
}) | |
}) | |
} | |
}, | |
data: { | |
get: function() { | |
return this.map(function(g) { | |
return d3.select(g[0]).datum().value | |
}) | |
} | |
} | |
}; | |
update(dataSet1); | |
window.setTimeout(function(){ | |
update(dataSet1.map(function(d) { | |
return { | |
name: d.name, sales: d.sales.map(function(y) { | |
return {year: y.year, profit: y.profit / 2} | |
}) | |
} | |
}) | |
) | |
},1000) | |
PK ! b�h^ � [Content_Types].xml �(� ���N�0E�H�C�-Jܲ@5��*Q>�ēƪc[�ii����B�j7���{2��h�nm���ƻR����U^7/���%��rZY�@1__�f� �q��R4D�AJ�h>����V�ƹ�Z�9����NV�8ʩ����ji){^��-I�"{�v^�P!XS)bR�r��K�s(�3�`c�0��������7M4�����ZƐk+�|\|z�(���P��6h_-[�@�!��� Pk���2n�}�?�L��� ��%���d����dN"m,�ǞDO97*�~��ɸ8�O�c|n���E������B��!$}�����;{���[����2� �� PK ! �U0#� L _rels/.rels �(� ��MO�0��H�����ݐBKwAH�!T~�I����$ݿ'T�G�~����<���!��4��;#�w����qu*&r�Fq���v�����GJy(v��*����K��#F��D��.W ��=��Z�MY�b���BS�����7��ϛז�� ?�9L�ҙ�sbgٮ|�l!��USh9i�b�r:"y_dl��D���|-N��R"4�2�G�%��Z�4�˝y�7 ë��ɂ��� �� PK ! �>��� � xl/_rels/workbook.xml.rels �(� �RMK�0���0w�v�t/"�U�ɴ)�&!3~��*�]X�K/o�y���v�5���+��zl�;o���b���G�����s�>��,�8��(%���"D��҆4j�0u2js��MY�˴���S쭂��� �)f���C����y�� I< y ���!+��E���fMy�k�����K�5=|�t ��G)�s墙�U��tB��)���,���f����� �� PK ! �V | |
h � xl/workbook.xml���n�0��H���}��$tMR��ATBP�i����83��a�B�;�I�n�`ߒ����9Y��@_��\��GFLQ]s�-���m0��:�j"�b~`���_-���6Z� �-p�\����-���)8i�����lC�Fj�2��(zJ� �y C7 ��B�^2�F�a�8߶��Mҗ�$1���� 6\p�0@1�4��*m�F@ڇ8��0}���mu� �A>�7��8S. �n����@��E`$�u��;V� ,����a���N�4M"�?��2�c#k�r{��F5kH/�̙�@6K�� �3�Qı�V�}t�_u��V�k�}�aP,^�rOBs��Wĵ�7��˼�� KuVK���h[�q��n\_s��(�UWFf�٪�U7�O=�ˠ�ЖWA��]��9�,��;B�X!�5f4��T�\����loy��p�U���.{x2����vm���<��q���ցU��$.�Z �F�����C����W F&�01�u<��(ԗËY������[W.`Wx���itv��A��eA:?I�y:K�ez�������<���#��1>J��ڀ��g�f�9��0cB'81EN_�? �� PK ! N�P�� � xl/sharedStrings.xmld��N�0�;��!�ubi%�a�@� Qk�H�SjwloO�.�9����`{��A�q�T�A�k@!u�Otr���v��%P�B������̢�.��(2�h�]�xWF�e�U�d��I�8a�9"J�i���!���$���fJ�3o�>�����n�x��� ��B��djz���ESMU|]_�Yö��������a� �� PK ! ��nX� � xl/theme/theme1.xml�Yϋ7��?sw�kfl/�{lg��&!�䨵e����ɻ1!P�c�P��^ | |
��P��%�k�MiSȿ�'��#��n�n -Y�2�����7ߓ4/ݍ�s�SNX�v�*����d�vo���p��1�,�mw��{i���.�-�;�?�[��FB̶�e>�f�/�N�ل�1p�N���ݘ�k�JP�I\'A1��6��v�Ҥ��4ާp�.F4ݗ���CaLJU����9B���8cv<�w��P�<h��疷/��Vމ� }�~����;�kj�tz���|/��+ �~��=@��4�����n�ϱ(����5z�������8w|�3� | |
������A^4� | |
��}�O��3� | |
��5|���y �@%�����p9�d����A��/P� ��CLX"6�Z��t �H�����dq�(9H��K�$�%�Cs�VT��_�<u�<��0�zK^���5I>�d&��`�� /�}������'��<����Ó?f���;(��_|�ٟ_�������㹎���O~��s;&[x����{���W����#���>$1��U|��`1�My�d���c!b�@ض��� ^] j�u��[)� xy~���sA,#_�b������+r,���y2���u� ��lc�(1B۟�@Y��da��u���8���!Ɩ��&�������pn����%Cr`$R�i����� �����-�˨m�=|d"�@�B~�����h.Pl39D1���Dd#��HG:��Dz�)s�c̹�ϵ�� | |
��=�{t��T�C��]Ę���0B��ʙ$����B�"�:6�3�yq@��p�"���BptU�T$�|2O-�����>.�a�2 ����$9S�O���NԳ�tZ�;)��Z;��|�?(�=4O�cxg��;�~����^�7���څP���u�v�7.�'��}��x���;��4@��V���j+7��2�(�i�T'e�#"���`�_U�)�MO�3cV��Ym��)�j�0���8۱V�rw��G�h���v�m�4�]�ʼ��N�nyI@��'$��Lu�Ʋ��w$��΅E�¢)�/C����@mX?9��j������ | |
Q<�q��ѕ�9�Hor&�3 �("ݒ\7NO�.K�W��ABK7�����<;����u��AO�b�64�7k)"���&�R��9n�A݇ӱ��� ���2�A�p��Et | |
�g#�f/��(�,墇x�9\�N�18u(�ۮ��*h�4Dq��@�Zr-�����2�L�H�a�Z���[P�L+�OU��˞l�ޏ�����7��ߨJ� ��j��1�͕��w�0岫)����E(�(��gp%�+:�n��.�38t݅SY`�u�=�TK�i�Y�LCUdմ��+�����2�V�^h]k�u���*qF�}���Q+3�I��2,5;o5���@�D��o�a���V~�w:ke�X�+U�O�� vpģ��s*� | |
%|{H,����L6��+�5"\9��{��5?,U�~��սJ��wꥎ�}�Z�uk����(���g��G�E��E��}���GnF,.3������0���0ѹ��z��Z�Π����R+��^6z�^�7[���s��^�zA�Y | |
�aX�l�^����f���ϗ10�L>r_�{��� �� PK ! ^@� 4 xl/styles.xml�U�o�0~�����;��%P5M�*uS�v�^0Ī0�td���� I�ZmY�0^�����}>�����)���,���\LV �|�y3�CdA��4�;�������1;N�6��I�Ƙz��M���4g��fJ�1`��ojMI��M��� ���0�;���OD?lk/W�&��gf�0�⺒J�5�m8!����˵jTi� �We�r���ܟ���ƥ��A��J�h{��A�o2�S�٭J��;z$<!��8W\id 3@�y$�[qI8[kf��D0���#�p��� �Y�oy��61��Ɩ 8��c�����j8^B!;��/�+Mv�(:}C�8+,�����u�3���Y�L��E���> l�s��b\+]�H��Ҙ�� �f��������h�TJn�ց �S���O��ɭȄ�zp%l��C��;�ΰ�C�{ ;���;,j��v#R�|w�Y%�ě� ��D��w��5�y�ɬ-�D}�*Co5-Yۋ��F �zH@�U��XP���~c���j�����@h5?��A�ȶ�� �C�ꕅ�[� �/�0��x3��5���;s8�\Вl��?L&�8�H�P�~�-{T�A$�8��8��V@[s�@��?�j��W���U6�f�r�M�4���r�E���j�̓Qp�s������'���{��`{�wG_�FG�52�=�>M��(�l��dJf�l:��, | |
G��dyeр{�:�a��a��Z���0A9��Z�+4�B���C����O �� PK ! ��%�� � xl/worksheets/sheet1.xml�Y�r�8�O�����`lCp f�{Cd�l���)�y����R˒�lB�>���QK�������I�4�#��tM��x�F�O�ŵi������@F�I�_��~��{�#$3��!��,;,+ v$��N|$�l�$�3xLެ��Ô���t��V�3�0Hαo�a@���#"�,7�������.<���(8�\�'�Nj ��`b���5�(��q�������6{��Ga��i��:`���c��n,�4�<,�M�~s�h:m�7!DGKb$d;2o���{EL�9$_)�����ٓ #��i�ڬ���g0ԥ����ڀ��?��2����o���!V�`�sG� r f:N�Z | |
�=L�(Ҹ�+���~��l72{��ӿ��n�I�y!5i�G���Kb�VFz��-�8g��,́�J��9/Л��o1�۱{�K��ݛB ~��6��MN�)͜�� ʵ� �sf� wa����sݷ��ح��Ew~揇I�e����ӣO[�;p�l=o�0|Kь@����;�>��A��Ɉ� | |
a����0W��)�N\3�].��VN9�S�R��pE�'#z"b*#�(t���s$��i�i�⾇�� �o�N��h��ɈK�2q�R&#�Q�JDMY�2>eH�O���U�O | |
��l�8�.y�S��в�4����^&@���W0E��#_�r�n=�F�z�x=���&���х�f#�B�E������X��f��<�sᩍ���X��k� ����'(Z� d�2w��Yf%���Fj�#f/r_<-��F/�%�$�J�����F��9�gJ�K�Ь����`�I�%\��!�t:�Tgv��+a��id!�<J#Kid%�<�#B��G��8gp�ē�� ?��0���RYI#O���3P�M�)�a���W��v�����*ᩉ��A��g�}a��������C4{��sP<��jf�]�0��8P*_��� | |
��j��`�� 5m|�z������@�f͐y�O�h��"0A,����ʙ���f�V5�%nh��\��@��vZ�������+��>93[��;O'� | |
B� �s��nu��ؚú'QC�*��3p R�6|���Ħhe�x!WCvΝV���G6�z���T�1S | |
��PA9��~�|�t]bF'O� | |
�3�P(��o�����fJU{A(վ��՞�c�~���Asy��40�͠b� �K^!��/�f�}�C@��8�~ %6i�f�f��b4mzƭ�V� *� �IU�jHUo]�M��u�;p�� ��2Ʃ ���k*:�۪CQ��Z̖�Q BL-�pV ����W���̈�S B�T�TM*-D��ۦkMZ鵦wMM�!�Pt���mlO�pʼn/��Tu_� B�C��ٙjR1�Vm�-�!Wkt)6�05<��L���˅�1�V�4���n�:�TJ\���~�/̿��7r�'o�!5�d˾�n�,�v���+(�:��3_���o�nF���6���~���"�� �� PK ! ���bC ] docProps/core.xml �(� ��_K�0���C�{�f�RBہʞ�V�br��?$�n��je>y�='�{�%�r/�����*IR��Z�j[��jg(r�)���@phY^^��P�-<Xm��\H�Qn | |
���P�߁d. č���p�[l�`[��4��<�3�c3�)��4����ch@������+ݟze┵?�0�1�-� �Gc۶I;�c������Q�Zu���\p�-0�m���Êr<)u�k����M ��0�Ε��@QC��'�e~{W�P9K�U�f�TiF)]\�u���� yl�bV�9]�3%� e��>D� �� PK ! �Aي docProps/app.xml �(� ��Ao�0����9�:��bH7��b�vgM�c��$�������i{ڍ�{x�DI]:_����P��k�������* | |
$j�c�Jŵ��AmrL���-QZI��������Jsg�ۼ��i���h�:$/��A���H�@1%�z���:ځv���Z}K�;k�o���cC�����\TL���u��U[k<�9X7�#(�2P�`��m�˨UO�,�\���k���0�T�7ٙ@�5ئf�}B��w̏�*Ɇi8�s�v��r4pqn&�w�<�fc2�C�����3�|���W���%�,���.<�}��C���ڶ&C�/pZ�i�ny��!�ք=�Ϟ������˫E���w�͔|��� �� PK- ! b�h^ � [Content_Types].xmlPK- ! �U0#� L � _rels/.relsPK- ! �>��� � � xl/_rels/workbook.xml.relsPK- ! �V | |
h � � xl/workbook.xmlPK- ! N�P�� � � xl/sharedStrings.xmlPK- ! ��nX� � ~ xl/theme/theme1.xmlPK- ! ^@� 4 B xl/styles.xmlPK- ! ��%�� � t xl/worksheets/sheet1.xmlPK- ! ���bC ] � docProps/core.xmlPK- ! �Aي docProps/app.xmlPK | |