Commit | Line | Data |
---|---|---|
dc61ee93 RLAR |
1 | {* |
2 | +--------------------------------------------------------------------+ | |
3 | | CiviCRM version 5 | | |
4 | +--------------------------------------------------------------------+ | |
f299f7db | 5 | | Copyright CiviCRM LLC (c) 2004-2020 | |
dc61ee93 RLAR |
6 | +--------------------------------------------------------------------+ |
7 | | This file is a part of CiviCRM. | | |
8 | | | | |
9 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
10 | | under the terms of the GNU Affero General Public License | | |
11 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
12 | | | | |
13 | | CiviCRM is distributed in the hope that it will be useful, but | | |
14 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
16 | | See the GNU Affero General Public License for more details. | | |
17 | | | | |
18 | | You should have received a copy of the GNU Affero General Public | | |
19 | | License and the CiviCRM Licensing Exception along | | |
20 | | with this program; if not, contact CiviCRM LLC | | |
21 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
22 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
23 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
24 | +--------------------------------------------------------------------+ | |
25 | *} | |
26 | <script src="{$config->resourceBase}/bower_components/d3/d3.min.js"></script> | |
27 | <script src="{$config->resourceBase}/bower_components/crossfilter2/crossfilter.min.js"></script> | |
28 | <script src="{$config->resourceBase}/bower_components/dc-2.1.x/dc.min.js"></script> | |
29 | <style src="{$config->resourceBase}/bower_components/dc-2.1.x/dc.min.css"></style> | |
30 | {literal} | |
31 | <style> | |
32 | .dc-chart path.domain { | |
33 | fill: none; | |
34 | stroke: black; | |
35 | } | |
36 | </style> | |
37 | <script type="text/javascript"> | |
38 | function createChart( chartID, divName, xSize, ySize, data ) { | |
39 | ||
40 | var div = document.getElementById(divName); | |
41 | if (!div) { | |
42 | console.log("no element found for chart id ", divName); | |
43 | return; | |
44 | } | |
45 | ||
0cf05243 | 46 | // Figure out suitable size based on container size. |
f648602a RLAR |
47 | // In some cases the containing element has no size. We should insist on a minimum size. |
48 | var w = Math.max(Math.min(div.clientWidth - 32, 800), 316); | |
0cf05243 | 49 | var h = Math.min(400, parseInt(w / 2)); |
dc61ee93 RLAR |
50 | |
51 | var chartNode = document.createElement('div'); | |
52 | var heading = document.createElement('h2'); | |
53 | heading.textContent = data.title; | |
54 | heading.style.marginBottom = '1rem'; | |
55 | heading.style.textAlign = 'center'; | |
56 | div.style.width = w + 'px'; | |
57 | div.style.marginLeft = 'auto'; | |
58 | div.style.marginRight = 'auto'; | |
59 | ||
60 | var links = document.createElement('div'); | |
0cf05243 RLAR |
61 | links.style.textAlign = 'center'; |
62 | links.style.marginBottom = '1rem'; | |
dc61ee93 RLAR |
63 | var linkSVG = document.createElement('a'); |
64 | linkSVG.href = '#'; | |
65 | linkSVG.textContent = 'Download chart (SVG)'; | |
66 | linkSVG.addEventListener('click', e => { | |
67 | e.preventDefault(); | |
68 | e.stopPropagation(); | |
69 | // Create an image. | |
70 | var svg = div.querySelector('svg'); | |
71 | var xml = new XMLSerializer().serializeToString(svg); | |
72 | var image64 = 'data:image/svg+xml;base64,' + btoa(xml); | |
73 | ||
74 | downloadImageUrl('image/svg+xml', image64, data.title.replace(/[^a-zA-Z0-9-]+/g, '') + '.svg'); | |
75 | }); | |
76 | function downloadImageUrl(mime, url, filename) { | |
77 | var downloadLink = document.createElement('a'); | |
78 | downloadLink.download = filename; | |
79 | downloadLink.href = url; | |
80 | downloadLink.downloadurl = [mime, downloadLink.download, url].join(':'); | |
81 | document.body.append(downloadLink); | |
82 | downloadLink.click(); | |
83 | document.body.removeChild(downloadLink); | |
84 | } | |
85 | var linkPNG = document.createElement('a'); | |
86 | linkPNG.href = '#'; | |
87 | linkPNG.textContent = 'Download chart (PNG)'; | |
88 | linkPNG.addEventListener('click', e => { | |
89 | e.preventDefault(); | |
90 | e.stopPropagation(); | |
91 | // Create an image. | |
92 | ||
93 | var canvas = document.createElement('canvas'); | |
94 | canvas.width = w; | |
95 | canvas.height = h; | |
96 | div.appendChild(canvas); | |
97 | ||
98 | var svg = div.querySelector('svg'); | |
99 | var xml = new XMLSerializer().serializeToString(svg); | |
100 | var svg64 = btoa(xml); | |
101 | var b64Start = 'data:image/svg+xml;base64,'; | |
102 | var image64 = b64Start + svg64; | |
103 | ||
104 | var img = document.createElement('img'); | |
105 | img.onload = function() { | |
106 | canvas.getContext('2d').drawImage(img, 0, 0); | |
107 | // canvas.style.display = 'block'; | |
108 | var imgURL = canvas.toDataURL('image/png'); | |
109 | downloadImageUrl('image/png', imgURL, data.title.replace(/[^a-zA-Z0-9-]+/g, '') + '.png'); | |
110 | div.removeChild(canvas); | |
111 | }; | |
112 | img.src = image64; | |
113 | }); | |
114 | ||
115 | links.appendChild(linkSVG); | |
116 | links.appendChild(document.createTextNode(' | ')); | |
117 | links.appendChild(linkPNG); | |
dc61ee93 RLAR |
118 | |
119 | var crossfilterData, ndx, dataDimension, dataGroup, chart; | |
120 | ndx = crossfilter(data.values[0]); | |
121 | dataDimension = ndx.dimension(d => d.label); | |
122 | dataGroup = dataDimension.group().reduceSum(d => d.value); | |
123 | var ordinals = data.values[0].map(d => d.label); | |
124 | ||
125 | if (data.type === 'barchart') { | |
126 | chart = dc.barChart(chartNode) | |
127 | .width(w) | |
128 | .height(h) | |
129 | .dimension(dataDimension) | |
130 | .group(dataGroup) | |
131 | .gap(4) // px | |
132 | .x(d3.scale.ordinal(ordinals).domain(ordinals)) | |
133 | .xUnits(dc.units.ordinal) | |
134 | .margins({top: 10, right: 30, bottom: 30, left: 90}) | |
135 | .elasticY(true) | |
136 | .renderLabel(false) | |
137 | .renderHorizontalGridLines(true) | |
138 | .title(item=> item.key + ': ' + item.value) | |
139 | //.turnOnControls(true) | |
140 | .renderTitle(true); | |
141 | } | |
142 | else if (data.type === 'piechart') { | |
143 | chart = dc.pieChart(chartNode) | |
144 | .width(w) | |
145 | .height(h) | |
146 | .radius(parseInt(h / 2) - 5) // define pie radius | |
147 | .innerRadius(parseInt(h / 4) - 5) // optional | |
148 | .externalRadiusPadding(5) | |
149 | .legend(dc.legend().legendText(d => d.name).y(5)) | |
150 | .dimension(dataDimension) | |
151 | .group(dataGroup) | |
152 | .renderLabel(false) | |
153 | .title(item=> item.key + ': ' + item.value) | |
154 | .turnOnControls(true) | |
155 | .renderTitle(true); | |
156 | } | |
157 | // Delay rendering so that animation looks good. | |
0cf05243 RLAR |
158 | window.setTimeout(() => { |
159 | div.appendChild(heading); | |
160 | div.appendChild(chartNode); | |
161 | div.appendChild(links); | |
162 | ||
163 | dc.renderAll(); | |
164 | }, 1500); | |
dc61ee93 RLAR |
165 | } |
166 | </script> | |
167 | {/literal} |