Skip to content

Instantly share code, notes, and snippets.

@mygoare
Forked from mbostock/.block
Created April 14, 2014 08:10
Show Gist options
  • Save mygoare/10626617 to your computer and use it in GitHub Desktop.
Save mygoare/10626617 to your computer and use it in GitHub Desktop.
Learn to write d3 plugin

Designed by Stephen Few, a bullet chart “provides a rich display of data in a small space.” A variation on a bar chart, bullet charts compare a given quantitative measure (such as profit or revenue) against qualitative ranges (e.g., poor, satisfactory, good) and related markers (e.g., the same measure a year ago). Layout inspired by Stephen Few. Implementation based on work by Clint Ivy, Jamie Love of N-Squared Software and Jason Davies. The "update" button randomizes the values slightly to demonstrate transitions.

(function() {
// Chart design based on the recommendations of Stephen Few. Implementation
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
// http://projects.instantcognition.com/protovis/bulletchart/
d3.bullet = function() {
var orient = "left", // TODO top & bottom
reverse = false,
duration = 0,
ranges = bulletRanges,
markers = bulletMarkers,
measures = bulletMeasures,
width = 380,
height = 30,
tickFormat = null;
// For each small multiple…
function bullet(g) {
g.each(function(d, i) {
var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
markerz = markers.call(this, d, i).slice().sort(d3.descending),
measurez = measures.call(this, d, i).slice().sort(d3.descending),
g = d3.select(this);
// Compute the new x-scale.
var x1 = d3.scale.linear()
.domain([0, Math.max(rangez[0], markerz[0], measurez[0])])
.range(reverse ? [width, 0] : [0, width]);
// Retrieve the old x-scale, if this is an update.
var x0 = this.__chart__ || d3.scale.linear()
.domain([0, Infinity])
.range(x1.range());
// Stash the new scale.
this.__chart__ = x1;
// Derive width-scales from the x-scales.
var w0 = bulletWidth(x0),
w1 = bulletWidth(x1);
// Update the range rects.
var range = g.selectAll("rect.range")
.data(rangez);
range.enter().append("rect")
.attr("class", function(d, i) { return "range s" + i; })
.attr("width", w0)
.attr("height", height)
.attr("x", reverse ? x0 : 0)
.transition()
.duration(duration)
.attr("width", w1)
.attr("x", reverse ? x1 : 0);
range.transition()
.duration(duration)
.attr("x", reverse ? x1 : 0)
.attr("width", w1)
.attr("height", height);
// Update the measure rects.
var measure = g.selectAll("rect.measure")
.data(measurez);
measure.enter().append("rect")
.attr("class", function(d, i) { return "measure s" + i; })
.attr("width", w0)
.attr("height", height / 3)
.attr("x", reverse ? x0 : 0)
.attr("y", height / 3)
.transition()
.duration(duration)
.attr("width", w1)
.attr("x", reverse ? x1 : 0);
measure.transition()
.duration(duration)
.attr("width", w1)
.attr("height", height / 3)
.attr("x", reverse ? x1 : 0)
.attr("y", height / 3);
// Update the marker lines.
var marker = g.selectAll("line.marker")
.data(markerz);
marker.enter().append("line")
.attr("class", "marker")
.attr("x1", x0)
.attr("x2", x0)
.attr("y1", height / 6)
.attr("y2", height * 5 / 6)
.transition()
.duration(duration)
.attr("x1", x1)
.attr("x2", x1);
marker.transition()
.duration(duration)
.attr("x1", x1)
.attr("x2", x1)
.attr("y1", height / 6)
.attr("y2", height * 5 / 6);
// Compute the tick format.
var format = tickFormat || x1.tickFormat(8);
// Update the tick groups.
var tick = g.selectAll("g.tick")
.data(x1.ticks(8), function(d) {
return this.textContent || format(d);
});
// Initialize the ticks with the old scale, x0.
var tickEnter = tick.enter().append("g")
.attr("class", "tick")
.attr("transform", bulletTranslate(x0))
.style("opacity", 1e-6);
tickEnter.append("line")
.attr("y1", height)
.attr("y2", height * 7 / 6);
tickEnter.append("text")
.attr("text-anchor", "middle")
.attr("dy", "1em")
.attr("y", height * 7 / 6)
.text(format);
// Transition the entering ticks to the new scale, x1.
tickEnter.transition()
.duration(duration)
.attr("transform", bulletTranslate(x1))
.style("opacity", 1);
// Transition the updating ticks to the new scale, x1.
var tickUpdate = tick.transition()
.duration(duration)
.attr("transform", bulletTranslate(x1))
.style("opacity", 1);
tickUpdate.select("line")
.attr("y1", height)
.attr("y2", height * 7 / 6);
tickUpdate.select("text")
.attr("y", height * 7 / 6);
// Transition the exiting ticks to the new scale, x1.
tick.exit().transition()
.duration(duration)
.attr("transform", bulletTranslate(x1))
.style("opacity", 1e-6)
.remove();
});
d3.timer.flush();
}
// left, right, top, bottom
bullet.orient = function(x) {
if (!arguments.length) return orient;
orient = x;
reverse = orient == "right" || orient == "bottom";
return bullet;
};
// ranges (bad, satisfactory, good)
bullet.ranges = function(x) {
if (!arguments.length) return ranges;
ranges = x;
return bullet;
};
// markers (previous, goal)
bullet.markers = function(x) {
if (!arguments.length) return markers;
markers = x;
return bullet;
};
// measures (actual, forecast)
bullet.measures = function(x) {
if (!arguments.length) return measures;
measures = x;
return bullet;
};
bullet.width = function(x) {
if (!arguments.length) return width;
width = x;
return bullet;
};
bullet.height = function(x) {
if (!arguments.length) return height;
height = x;
return bullet;
};
bullet.tickFormat = function(x) {
if (!arguments.length) return tickFormat;
tickFormat = x;
return bullet;
};
bullet.duration = function(x) {
if (!arguments.length) return duration;
duration = x;
return bullet;
};
return bullet;
};
function bulletRanges(d) {
return d.ranges;
}
function bulletMarkers(d) {
return d.markers;
}
function bulletMeasures(d) {
return d.measures;
}
function bulletTranslate(x) {
return function(d) {
return "translate(" + x(d) + ",0)";
};
}
function bulletWidth(x) {
var x0 = x(0);
return function(d) {
return Math.abs(x(d) - x0);
};
}
})();
[
{"title":"Revenue","subtitle":"US$, in thousands","ranges":[150,225,300],"measures":[220,270],"markers":[250]},
{"title":"Profit","subtitle":"%","ranges":[20,25,30],"measures":[21,23],"markers":[26]},
{"title":"Order Size","subtitle":"US$, average","ranges":[350,500,600],"measures":[100,320],"markers":[550]},
{"title":"New Customers","subtitle":"count","ranges":[1400,2000,2500],"measures":[1000,1650],"markers":[2100]},
{"title":"Satisfaction","subtitle":"out of 5","ranges":[3.5,4.25,5],"measures":[3.2,4.7],"markers":[4.4]}
]
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
padding-top: 40px;
position: relative;
width: 960px;
}
button {
position: absolute;
right: 10px;
top: 10px;
}
.bullet { font: 10px sans-serif; }
.bullet .marker { stroke: #000; stroke-width: 2px; }
.bullet .tick line { stroke: #666; stroke-width: .5px; }
.bullet .range.s0 { fill: #eee; }
.bullet .range.s1 { fill: #ddd; }
.bullet .range.s2 { fill: #ccc; }
.bullet .measure.s0 { fill: lightsteelblue; }
.bullet .measure.s1 { fill: steelblue; }
.bullet .title { font-size: 14px; font-weight: bold; }
.bullet .subtitle { fill: #999; }
</style>
<button>Update</button>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="bullet.js"></script>
<script>
var margin = {top: 5, right: 40, bottom: 20, left: 120},
width = 960 - margin.left - margin.right,
height = 50 - margin.top - margin.bottom;
var chart = d3.bullet()
.width(width)
.height(height);
d3.json("bullets.json", function(error, data) {
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg")
.attr("class", "bullet")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
var title = svg.append("g")
.style("text-anchor", "end")
.attr("transform", "translate(-6," + height / 2 + ")");
title.append("text")
.attr("class", "title")
.text(function(d) { return d.title; });
title.append("text")
.attr("class", "subtitle")
.attr("dy", "1em")
.text(function(d) { return d.subtitle; });
d3.selectAll("button").on("click", function() {
svg.datum(randomize).call(chart.duration(1000)); // TODO automatic transition
});
});
function randomize(d) {
if (!d.randomizer) d.randomizer = randomizer(d);
d.ranges = d.ranges.map(d.randomizer);
d.markers = d.markers.map(d.randomizer);
d.measures = d.measures.map(d.randomizer);
return d;
}
function randomizer(d) {
var k = d3.max(d.ranges) * .2;
return function(d) {
return Math.max(0, d + k * (Math.random() - .5));
};
}
</script>
�PNG

IHDR�x��� �iCCPicmHǕ�TSI��$�P�zW�t�(H! $�AŎ,*�TD��.E\ kA,XX{_PQQ�E]l���P�����g眙��;w�ܹoޜ�кXBa*�@� K��Ɍ��c��9@T` �,v��#44�cyw ��5 �-��"��� ����d�A>���� H��/�Jxdet�^ '�r��F�C���
�%J�:��lv�C�@�p���!��y,�%�����%\�8�/v���f„M+i�G�"-2��La*kѿ ��/i���9�`�d���'�m!��Y�:7 xL^%�� ��gDHb�:O�9���)��5 NI���8�����!�� �3��Fm�v9���1�`��2�Eh�(=l\���>.���y��OfJ�7 rK$] �-��I�Յ�_�16W� u��Z�'�"߰1��͔�W:W/��>��7��ML#��0�f����݅��= �b"q�$�����1�X��4��.H�� xﱖ �زA:H�UĔ�!<%tnzw&����p�s\���<�ߡU.�� W�]qg<��Z���x_�@���W��&��c�1ﳡ�/�z������$L���O����ꘆe�e������VL�!z���Dl vk�Nc��X`b��f�;!��fa�EE$]o�� ��7���H<�1&���lA�T?��'f��z���1� �R2� �X�x� atmqO���g����#�o` �^ߏk-@�4��ҵ����Ӳ� �$�+]�H�O�e1=�i�5g�S̙֖V�@r����o�3a\�&�h��
���Xz{
��7������D[,��ᒆ�@�~5xr�c�5�����@"@,���i��` X �A!���2��5�8��qp��A���^�A� #BB�QC�� �FW� FX$IB�Y��B
�b� ٍ�"?#ǐ��E����"�����T�D ѩ����\4 �@s�<tZ�V���F�4z����/�! `r��,0� ��DL�-�
����Z�^���`�G���q&n��?��� |^���5x#~�����W��A0#81�$�B>��PE8J8��>�;"�� ��n�%&��ۉ �Vb7�1q�D"���H.���E�'m#�'�"]%��>���h�X�����dreJd�ɜ��*�LfXVA�@�I6D�#�Hv��^��+�}��dE�مAN&�$��������o���t��f���Vȕ��� �+���D1�xQ�PĔu�jJ+��-�J5��S�Y�u�Z��C��6�@�Ж��i����W������s�K��_�P�U0T�R`),S(W8�pKaH��h����X��O��s%�����G)Oi����t��G�����{���}�De#��d�B�ʝʃ*J*�T�T����P�a` CF#���q�q��i��$�I�Ik'�O�:��dUwU�j�j�� �OjL5���jMj�quS�Y� �w��S��<�y2{r��C��j��a�5�hth iji�i
5�i���bh�k%km�:�կM�v��ko�>������`�2K�g��::�:b��:�:úF����� ���zz�z�������g�/ѯӿk k�`�3�j�n����0�p�a��s#U���:���Tc7� �J��&D���&]����)ϴ��jfg�7�n�mN0w4�W�߲�XxXd[�Y�NaL ��;�iʫ��S�n��>����e��^�{VJV�V�V-Vo�M������m�6�6�m�m^O3�Ɲ�c�m[�� �նm�_���Dv�v���������B�.8=�;w��d��t��g ��}�ϧM�N�;���� �e�K�+�5�u�k���˭�푻�;ǽ�����G��~�W���"ϣ�|��z�zc�~�ޝ>J>�>e>}u}�|�|�l�������7��
� `� �. <D
* zl,
n�����i���33�B@H@Ȧ��F����"�
�U>�i�Uؒ��pz���}��"<#�G܋4�G�E�G͉��z�]�35fi��X�X~ls).*�*nh���-�����ɟss��܅s/�S��:��|������ �����?�BX��������A�{+�%ǝ����u�s�%�$'>OrIڔ��s���^�2��d����SBR�SFR�S�d��ӎ �)���Z� ӻ�f�|aO�SƖ�AQ��*ɜ�ٜ� /�bc���l���� �^��P��c�颵�������_�^ܶDg��%�K=��^�,KXֶ\oy��~+jV�W���5�2�8��UѫZ�4�V�=��|Z�(��j��;��k�k:�ڬݶ�k��R�eaI��"vѥ�~,�qd]���v�wl nl���mcM�bqN��M365nfn.����[.�L+ٹ��U���4��y��� �>���n�{�7ThT��x�������;5w�������n�ݍ���%{�{��<����'��j�ԫ
��T �{j�j�������ط���������z��� ��ƒ�������y(�P�a���G �T�-hD56�z�c���kkqn9�˔_���/?�rb�I�ɼ�#�rN �
[N'�~�6��ޙ�3���:�y.�܅��ϴ{����r��E���.9\j�lw��ö�诶����l�b��˱��{z�ɫnWO_�v�z���7f��y���9�znsn?��z������V�'�/x�������L~k��9�����(�ѽ���/�d>�ܗ����������Ϗ���w��������@�W�2~u��?:c�^�^��)z�����i� �=|��n�}��5>����lx�g���/&_Z�}�?�62"d�Xҫ+����j���»Cd�hn$-�h>'%�O<�?I���D� �Qv�j���k~�;@ml&�X�L���E�����[MH-|�� o�s<���9���=~MB;�V|��)�`k{/ KbKGD������� oFFs=��<� pHYs%%IR$��IDATx���sǝ�?YKWuWoh�HH$EQ�Q�/��-�����y���2���O����˳/�>�/&��2���f�IQ$��Fh��k_���H� �@��5�O��ˮ�oU.����RJ
�P� �
����.�B�ߑ8���!�>i�c*H��I�u˲�~VoL���dY��yh�F�$dY�a��I��H �a��:�ibaE��5�S��3RJ�4%�#����hY��'����e�c�a����U�ر�t?���B���,MXYZ�m�Yo�#���չ9�TRul�8���CؖI�P����B�g4�;B�℧�z�h�6���"�B4���+�L� I�躾u ��K���1a"���B�FjRJ ������)a*C���U(���1��H)����C�$��?���Тi�-c�������m�]W\'���߷0�0�׿�5a���~�q�, !q#�DA�$�J���y����8��q�⅁ޜ�˲(�T/_nn+E�x�X�~HӔ�kט���/q�����[��q�Œ�'N��9q�裏�$ ���H)q���qL�dyy���~�ӟR��pn% ˔0�YȰ�l�Ÿ/F�b�����B��:���������-K�±uL]���{�n�K�4����c�&�Ν�0 �x� �����������a������2ccch�F�)qi=��N�~��b0�>9á���@t����WV<N�u0�ބ)��Ш�����V���!�����1�S�,���_�T*q���X]k2>1R�qmMo9 �)#�K^�}5�T�1���-��
h�t�fw�a7cLS�ئF�60 ����C��~m[�8�Y]���H)��B��٨���L}�F�4q=���2�.��,��|d&q*Y�Л$Xv�����B]�1�e�K�A�Ѕ@]��]�v<� ��'gy�2�i��9mˈ�ۮc�iJ�պ�N�H�M�q�(�1 !��Aw����J)�l�$�c��������R���&�$A��/~��B���@bo~øߩ�/�h�`����HI�;�n�4�z�|��`�.�Ba S�B:۶r��)�N�C�X̽;ٍ/�t\J��@��RJ\��q��<���x�G�Th�^��^�8����'����ɉ�o��}H�$����ǰ �wgv���2K׮��h!�����ùſ�����5֚똦I�V�Ï����3\�_B�u4{�r�ࣹ�?�٧,-�"4��a0>�������O���A�������ۻ���*�0 �:7O�>��0�̸�����E���=��0e��0?�F�E��x��A�<q-�w�$\_Z���Q
������뽱����KW(L�(B��
s��UW��}]�hV!?aJ���:'N|J�R!NR�rWa�ڕ�Y���"��(�R����k��I�$c��4Z^��$��<Z-4M�`[����?�c<ϣ�n��E��F=��� �tW�$�&(9�jn��^E�0� )���T����//-��Y֝���T+ζ�L�
���Y�R("�6ƌ��v�M�Z�e���}�,ömt]' C�ͦZ�|H��}3��wZ���� 1M���� ��w��w�����g�%I������A����},�bqaA �!�.ڔ�w68�a+�l�Bp��Yj�����m
!�f�՝�h6�����E�u �@���"$qL'1��yWY�R #�0�[~'�RA��T���o��>�!j��ٳg���eϞ=�$��}�ׅ:đ#G�,���.^������ߘ,L��c^;�V&v�R�h٢d�(���mV��j˾-� ʍu'�&���b�zff��{�.�\�r�,0��6��ce��|H�����4�[C�]�}]����7M�R)���L�Vc�l����Cu�am�ی1�m���E?�����R��ɲ���岎�eY��:�v{k<:�0�4́��`+���s� M����gVV��YO�?K��{褔LNNa��Hi+im�(9e #�f���S��4͞�[��0 �˲����� A�!���x�ʎ�� Ȳ�(��?Wu����o���'����G�1@��ן��7^:��x#�����;� �x���)���㓏>� |:^ī��z���4�y��i67��M���1s|8&qț��������^~�����(��3�0fjr�o~�hw��۟��$�r�tzSx���@���U�,M���W��*���I��@���[-
)�_�H��V��S.�^�9�2)IeJ��c���<��?$t�Ѿ�c�\��t7���q��fپ�OS�4%M�m'`�{15MChŜ��2��.���iZ�C�ͽ���!��}�ޒ2�+C�@R�� &#�v�,�8�9y�$�J��{ ���u4M�Z�b_]�>}�����*�ƭ�,�벸����eP3��²v�3��-��Y�Փ��ga~���|���a����ap��9fgg�R2>>N�Xd||�_��:t���Q|ߧ\.�n���~��4V���u6�a�3�m�C�%�)�'���y�7�������b]z�Y���7�RJ.�?O�T��<N�:������|�{��zK�9s���)VVV�m��ׯ�O��B`��?�Ē'�dz��G�Pڼ]d� Mܽ^�_����ʿS�4e~~>wP��<v�Y�Q�mʕ2Q��0���^|ϣ�h066�����T*��lll�h4�|�}�����uίE1�gR�ns43�0o�)];�b = �P(�/<����:$I���V�ēG�p��e���� ���P0-�(`�� i���:Q�1S3���9�DN�`�j�{�H41>�a&�e���(�A*3,��a�dY�LS<�#�24!�t -M1 &q�j�HeF��]�s
OL6���Mv}�ǧ�;"�[�{�l�]�݌�M�X���r����}�c����4M ��q���J0!Y&)m�����l�y#���<l�Fӵ\���uݭ�σ����x'�w{��� �����_�R�f������s3~���~��N�M�w�߈[RJZ�#�ƶ�%q�!I���,�����n�q]�$% #�B��z-����u�0&I�0��lj���G�QD�u�}�4I�K�J~�L֛M�4�٣��Pv�]k'�f�I'�q�뺔+����r5x�6��G�z:�,��ׯ�N���=���oG�)����x��qJE�0b��^9�Rn ���׷ޡZ�E1䥗^�'8���s��J�a�ؑ���?>�[���WV(�Q��3O�Ǟ�%�����iv|*�E�<��<��3�~&Wa������j~o�M�# >v��+��ecT�#�&|�$�߿C$�4Fr%t�����t!#���M왙!I!�b@2�ȷ LMM�f�ߘx��s�-�?��F�EӺK�����?��W6�d���)36�_򜄐H��N~��F5Ky�!G~ ��o6s)o��w=������w]�S&v�b��Ğe݃l_ �J�O�^��ވy����7����©S�h4���b��V�h�F����h�}mk�8I��癙��&���Q�al��6�-�J����˗���>ԭ�r��mi+��y8�v�n��ض=����b�'��0��366�eY8�C�X��j��+�`�f�Z��o~�n6��E�4e}}��{��.�j� .��V��!����� �Ŧߵ�)z�h�X�Q$�@3R�>�6ur��B�e��LB���?�� /�o߾ܳ���B2������>}����� VWWq�Z��;#m�[&�b����*���#��DK���#MS>��3FGG�m���i^�u<��JR���T �{D�80�љ�@f"�OWV{뒊5X7�� �~����#����p+텦i��I�$�J%t]�G?��m#�$˲��i�dYF�T*�4%�"�$��*�!��?�<��\_hQ���ӆ �Z���[�����m����6�l��LNN�t�$�YZ\ #4��uݮH�?��W7�����1�}P0��7r�+��q�}c����H))�˻��AߗKd����Frc�'MR�b)S��2a�37we�����)�P�B�i6�ru������8nM�Ri[���O�P�_�P(��-�u�N��a�)ۡian���!M�͙�]ϣ�8[��yV����E�.�mcF���=��)%�b������͌�"��y�$\�x������@�����c�)�С�sO�|; �sT�#4׮��j�ݔ%gz������X����KE.^�r��<�n<~�񁹺�����2��#,,\�Z�I������j��-/%�Ξ�v�dIH� H���16��6u�o&v�����Ѝ��3 |>��o$�F�V���)���@�umqϏ�x�sW�`�&q�r������]��1Z��w��*:D����8�>�{G��ɓ'�M�OO}J��k���S�nf^H)9{� #��D�:�.\�hY�Q�ѧ�n+�\��i�p��y�����*�Q�G.�G�Mě�)��7�[�:.�� tM'MS����(S��{h$Q�Z�I�u����0t������o��6�>?O�V��
�A�u1�`?�b�g�@w����O1,�,M�L޳.�WV�B�o
R�P�{����9oNz��� ��%ƍ�v[%�R܊"w益iw\R�i��^�u����
saa�_��<��3>|� �x�"�z���)�4��7���_�R��$ �N� .P�՘���Z�<���>sW.|j]���.U�����S�Vr�
��ҋ�q������>^����餻wfǸ�����k��F�P�V�Q*��x�"���dYƯ~�+VWW���`zz���9l�&��-k_E�ǿ�;���Ѽ?����|�،�
=�}4��x����(Í�����Z����������A�.�9f���w��8����aH�Tbrr۶�� ��099��˗����4Mfff��jh�F��`t��ƢP(pq#���}C����#%�|x��f+���üK�"��Q���b3�􂇮�]�([:�>j���U�]#������������1n[J)I�V��eY��K�\�fF�t�V����Q��)<��� ����t�z�SW���wc̛�<
��m���yOaX��@���3����~�ͯy!��aii�+V$�n�z����{~1]5��g�j�� �z���u�J%�1��1�?"p�扙�����}[�L�� ��E6��Ea�n�N�%�����}��$IB��w��)Rʾ�:�<9���RJ�8���q7�9������x��mJ��Y�R,��r�{�K�0�x��� ��]���yG�4dYJ����Ƒea� A�P�uC��$I�,k�;9�4% �[R��)�-���H��Hx�-���'}�e[Ki����F]�}��(�HӴ/u�9q�ۺ�n�ؼ���K�g?���v�G<�Ûo���9u�$i&�a&�$�y����Υ���ꄌ�5vt-��g?#�SV��8����;n��s,_�N�|��)��۷��8W�y�)�>����Ob��������7ߦ^���{��߱Q?
�����N�OO��U]�,�ݷ�&A�ʥ���sgXi��X_��������<�.\F}��������O��V-����ݳ.s3�GQ���:���XYY�V���O����:�H{}?H9r��_��t�v��k� ��I��,����Q(XY^"�� ���`m����k��aD������ﱲ�B�ӡ����F���ΎHȲ��V�Ņy��.3)�m��b���˺�z�
,M�.�--r��]�.�0���ղ�S]�֕�Y��Z�X$�}�ryW[����0�IQ(X�w�`�8&IS����,@v#�J���q���Ȏ�خۡ�z8�~2��qw* ��u��2AP��v<n�ҔյU�E� vU�RJ��V1M�$�w]��]�(�a�u�ea�m���2 :��e��ApϺT&v�bQ&v�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�bQ�T(�%L�b��[-�gȵ�IEND�B`�
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment