chartjs-plugin-chart2music 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js ADDED
@@ -0,0 +1,199 @@
1
+ var chartjs2music = (function (c2mChart) {
2
+ 'use strict';
3
+
4
+ // let lastDataObj = "";
5
+ const chartjs_c2m_converter = {
6
+ bar: "bar",
7
+ line: "line",
8
+ pie: "pie",
9
+ polarArea: "bar",
10
+ doughnut: "pie"
11
+ };
12
+ const processChartType = (chart) => {
13
+ const topLevelType = chart.config.type;
14
+ const panelTypes = chart.data.datasets.map(({ type }) => type ?? topLevelType);
15
+ const invalid = panelTypes.find((t) => !(t in chartjs_c2m_converter));
16
+ if (invalid) {
17
+ return {
18
+ valid: false,
19
+ invalidType: invalid
20
+ };
21
+ }
22
+ if ([...new Set(panelTypes)].length === 1) {
23
+ return {
24
+ valid: true,
25
+ c2m_types: chartjs_c2m_converter[panelTypes[0]]
26
+ };
27
+ }
28
+ return {
29
+ valid: true,
30
+ c2m_types: panelTypes.map((t) => chartjs_c2m_converter[t])
31
+ };
32
+ };
33
+ const generateAxisInfo = (chartAxisInfo, chart) => {
34
+ const axis = {};
35
+ if (chartAxisInfo?.min) {
36
+ if (typeof chartAxisInfo.min === "string") {
37
+ axis.minimum = chart.data.labels.indexOf(chartAxisInfo.min);
38
+ }
39
+ else {
40
+ axis.minimum = chartAxisInfo.min;
41
+ }
42
+ }
43
+ if (chartAxisInfo?.max) {
44
+ if (typeof chartAxisInfo.max === "string") {
45
+ axis.maximum = chart.data.labels.indexOf(chartAxisInfo.max);
46
+ }
47
+ else {
48
+ axis.maximum = chartAxisInfo.max;
49
+ }
50
+ }
51
+ const label = chartAxisInfo?.title?.text;
52
+ if (label) {
53
+ axis.label = label;
54
+ }
55
+ if (chartAxisInfo?.type === "logarithmic") {
56
+ axis.type = "log10";
57
+ }
58
+ return axis;
59
+ };
60
+ const generateAxes = (chart) => {
61
+ const axes = {
62
+ x: {
63
+ ...generateAxisInfo(chart.options?.scales?.x, chart),
64
+ valueLabels: chart.data.labels.slice(0)
65
+ },
66
+ y: {
67
+ ...generateAxisInfo(chart.options?.scales?.y, chart),
68
+ format: (value) => value.toLocaleString()
69
+ }
70
+ };
71
+ return axes;
72
+ };
73
+ const whichDataStructure = (data) => {
74
+ if (Array.isArray(data[0])) {
75
+ return data.map((arr, x) => {
76
+ let [low, high] = arr.sort();
77
+ return { x, low, high };
78
+ });
79
+ }
80
+ return data;
81
+ };
82
+ const scrubX = (data) => {
83
+ const blackboard = JSON.parse(JSON.stringify(data));
84
+ let labels = [];
85
+ if (Array.isArray(data)) {
86
+ // console.log("not grouped");
87
+ // Not grouped
88
+ blackboard.forEach((item, x) => {
89
+ if (typeof item === "object" && "x" in item) {
90
+ labels.push(item.x);
91
+ item.x = x;
92
+ }
93
+ });
94
+ return { labels, data: blackboard };
95
+ }
96
+ };
97
+ const processData = (data) => {
98
+ let groups = [];
99
+ if (data.datasets.length === 1) {
100
+ return {
101
+ data: whichDataStructure(data.datasets[0].data)
102
+ };
103
+ }
104
+ const result = {};
105
+ data.datasets.forEach((obj, index) => {
106
+ const groupName = obj.label ?? `Group ${index + 1}`;
107
+ groups.push(groupName);
108
+ result[groupName] = whichDataStructure(obj.data);
109
+ });
110
+ return { groups, data: result };
111
+ };
112
+ const determineChartTitle = (options) => {
113
+ if (options.plugins?.title?.text) {
114
+ if (Array.isArray(options.plugins.title.text)) {
115
+ return options.plugins.title.text.join(", ");
116
+ }
117
+ return options.plugins.title.text;
118
+ }
119
+ return "";
120
+ };
121
+ const determineCCElement = (canvas, provided) => {
122
+ if (provided) {
123
+ return provided;
124
+ }
125
+ const cc = document.createElement("div");
126
+ canvas.insertAdjacentElement("afterend", cc);
127
+ return cc;
128
+ };
129
+ const plugin = {
130
+ id: "chartjs2music",
131
+ afterInit: (chart, args, options) => {
132
+ const { valid, c2m_types, invalidType } = processChartType(chart);
133
+ if (!valid) {
134
+ options.errorCallback?.(`Unable to connect chart2music to chart. The chart is of type "${invalidType}", which is not one of the supported chart types for this plugin. This plugin supports: ${Object.keys(chartjs_c2m_converter).join(", ")}`);
135
+ return;
136
+ }
137
+ const axes = generateAxes(chart);
138
+ // Generate CC element
139
+ const cc = determineCCElement(chart.canvas, options.cc);
140
+ const { data, groups } = processData(chart.data);
141
+ // lastDataObj = JSON.stringify(data);
142
+ let scrub = scrubX(data);
143
+ if (scrub?.labels && scrub?.labels?.length > 0) { // Something was scrubbed
144
+ if (!chart.data.labels || chart.data.labels.length === 0) {
145
+ axes.x.valueLabels = scrub.labels.slice(0);
146
+ }
147
+ }
148
+ const c2mOptions = {
149
+ cc,
150
+ element: chart.canvas,
151
+ type: c2m_types,
152
+ data: scrub?.data ?? data,
153
+ title: determineChartTitle(chart.options),
154
+ axes,
155
+ options: {
156
+ // @ts-ignore
157
+ onFocusCallback: ({ slice, index }) => {
158
+ try {
159
+ const highlightElements = [{
160
+ datasetIndex: groups?.indexOf(slice) ?? 0,
161
+ index
162
+ }];
163
+ chart?.setActiveElements(highlightElements);
164
+ chart?.tooltip?.setActiveElements(highlightElements, {});
165
+ chart?.update();
166
+ }
167
+ catch (e) {
168
+ // console.warn(e);
169
+ }
170
+ }
171
+ }
172
+ };
173
+ if (options.audioEngine) {
174
+ // @ts-ignore
175
+ c2mOptions.audioEngine = options.audioEngine;
176
+ }
177
+ const { err, data: c2m } = c2mChart(c2mOptions);
178
+ // /* istanbul-ignore-next */
179
+ if (err) {
180
+ options.errorCallback?.(err);
181
+ }
182
+ },
183
+ // afterUpdate: (chart: Chart) => {
184
+ // const {data} = processData(chart.data);
185
+ // if(JSON.stringify(data) !== lastDataObj){
186
+ // c2mChartRef?.setData(data);
187
+ // lastDataObj = JSON.stringify(data);
188
+ // }
189
+ // },
190
+ defaults: {
191
+ cc: null,
192
+ audioEngine: null,
193
+ errorCallback: null
194
+ }
195
+ };
196
+
197
+ return plugin;
198
+
199
+ })(c2mChart);
@@ -0,0 +1,196 @@
1
+ import c2mChart from 'chart2music';
2
+
3
+ // let lastDataObj = "";
4
+ const chartjs_c2m_converter = {
5
+ bar: "bar",
6
+ line: "line",
7
+ pie: "pie",
8
+ polarArea: "bar",
9
+ doughnut: "pie"
10
+ };
11
+ const processChartType = (chart) => {
12
+ const topLevelType = chart.config.type;
13
+ const panelTypes = chart.data.datasets.map(({ type }) => type ?? topLevelType);
14
+ const invalid = panelTypes.find((t) => !(t in chartjs_c2m_converter));
15
+ if (invalid) {
16
+ return {
17
+ valid: false,
18
+ invalidType: invalid
19
+ };
20
+ }
21
+ if ([...new Set(panelTypes)].length === 1) {
22
+ return {
23
+ valid: true,
24
+ c2m_types: chartjs_c2m_converter[panelTypes[0]]
25
+ };
26
+ }
27
+ return {
28
+ valid: true,
29
+ c2m_types: panelTypes.map((t) => chartjs_c2m_converter[t])
30
+ };
31
+ };
32
+ const generateAxisInfo = (chartAxisInfo, chart) => {
33
+ const axis = {};
34
+ if (chartAxisInfo?.min) {
35
+ if (typeof chartAxisInfo.min === "string") {
36
+ axis.minimum = chart.data.labels.indexOf(chartAxisInfo.min);
37
+ }
38
+ else {
39
+ axis.minimum = chartAxisInfo.min;
40
+ }
41
+ }
42
+ if (chartAxisInfo?.max) {
43
+ if (typeof chartAxisInfo.max === "string") {
44
+ axis.maximum = chart.data.labels.indexOf(chartAxisInfo.max);
45
+ }
46
+ else {
47
+ axis.maximum = chartAxisInfo.max;
48
+ }
49
+ }
50
+ const label = chartAxisInfo?.title?.text;
51
+ if (label) {
52
+ axis.label = label;
53
+ }
54
+ if (chartAxisInfo?.type === "logarithmic") {
55
+ axis.type = "log10";
56
+ }
57
+ return axis;
58
+ };
59
+ const generateAxes = (chart) => {
60
+ const axes = {
61
+ x: {
62
+ ...generateAxisInfo(chart.options?.scales?.x, chart),
63
+ valueLabels: chart.data.labels.slice(0)
64
+ },
65
+ y: {
66
+ ...generateAxisInfo(chart.options?.scales?.y, chart),
67
+ format: (value) => value.toLocaleString()
68
+ }
69
+ };
70
+ return axes;
71
+ };
72
+ const whichDataStructure = (data) => {
73
+ if (Array.isArray(data[0])) {
74
+ return data.map((arr, x) => {
75
+ let [low, high] = arr.sort();
76
+ return { x, low, high };
77
+ });
78
+ }
79
+ return data;
80
+ };
81
+ const scrubX = (data) => {
82
+ const blackboard = JSON.parse(JSON.stringify(data));
83
+ let labels = [];
84
+ if (Array.isArray(data)) {
85
+ // console.log("not grouped");
86
+ // Not grouped
87
+ blackboard.forEach((item, x) => {
88
+ if (typeof item === "object" && "x" in item) {
89
+ labels.push(item.x);
90
+ item.x = x;
91
+ }
92
+ });
93
+ return { labels, data: blackboard };
94
+ }
95
+ };
96
+ const processData = (data) => {
97
+ let groups = [];
98
+ if (data.datasets.length === 1) {
99
+ return {
100
+ data: whichDataStructure(data.datasets[0].data)
101
+ };
102
+ }
103
+ const result = {};
104
+ data.datasets.forEach((obj, index) => {
105
+ const groupName = obj.label ?? `Group ${index + 1}`;
106
+ groups.push(groupName);
107
+ result[groupName] = whichDataStructure(obj.data);
108
+ });
109
+ return { groups, data: result };
110
+ };
111
+ const determineChartTitle = (options) => {
112
+ if (options.plugins?.title?.text) {
113
+ if (Array.isArray(options.plugins.title.text)) {
114
+ return options.plugins.title.text.join(", ");
115
+ }
116
+ return options.plugins.title.text;
117
+ }
118
+ return "";
119
+ };
120
+ const determineCCElement = (canvas, provided) => {
121
+ if (provided) {
122
+ return provided;
123
+ }
124
+ const cc = document.createElement("div");
125
+ canvas.insertAdjacentElement("afterend", cc);
126
+ return cc;
127
+ };
128
+ const plugin = {
129
+ id: "chartjs2music",
130
+ afterInit: (chart, args, options) => {
131
+ const { valid, c2m_types, invalidType } = processChartType(chart);
132
+ if (!valid) {
133
+ options.errorCallback?.(`Unable to connect chart2music to chart. The chart is of type "${invalidType}", which is not one of the supported chart types for this plugin. This plugin supports: ${Object.keys(chartjs_c2m_converter).join(", ")}`);
134
+ return;
135
+ }
136
+ const axes = generateAxes(chart);
137
+ // Generate CC element
138
+ const cc = determineCCElement(chart.canvas, options.cc);
139
+ const { data, groups } = processData(chart.data);
140
+ // lastDataObj = JSON.stringify(data);
141
+ let scrub = scrubX(data);
142
+ if (scrub?.labels && scrub?.labels?.length > 0) { // Something was scrubbed
143
+ if (!chart.data.labels || chart.data.labels.length === 0) {
144
+ axes.x.valueLabels = scrub.labels.slice(0);
145
+ }
146
+ }
147
+ const c2mOptions = {
148
+ cc,
149
+ element: chart.canvas,
150
+ type: c2m_types,
151
+ data: scrub?.data ?? data,
152
+ title: determineChartTitle(chart.options),
153
+ axes,
154
+ options: {
155
+ // @ts-ignore
156
+ onFocusCallback: ({ slice, index }) => {
157
+ try {
158
+ const highlightElements = [{
159
+ datasetIndex: groups?.indexOf(slice) ?? 0,
160
+ index
161
+ }];
162
+ chart?.setActiveElements(highlightElements);
163
+ chart?.tooltip?.setActiveElements(highlightElements, {});
164
+ chart?.update();
165
+ }
166
+ catch (e) {
167
+ // console.warn(e);
168
+ }
169
+ }
170
+ }
171
+ };
172
+ if (options.audioEngine) {
173
+ // @ts-ignore
174
+ c2mOptions.audioEngine = options.audioEngine;
175
+ }
176
+ const { err, data: c2m } = c2mChart(c2mOptions);
177
+ // /* istanbul-ignore-next */
178
+ if (err) {
179
+ options.errorCallback?.(err);
180
+ }
181
+ },
182
+ // afterUpdate: (chart: Chart) => {
183
+ // const {data} = processData(chart.data);
184
+ // if(JSON.stringify(data) !== lastDataObj){
185
+ // c2mChartRef?.setData(data);
186
+ // lastDataObj = JSON.stringify(data);
187
+ // }
188
+ // },
189
+ defaults: {
190
+ cc: null,
191
+ audioEngine: null,
192
+ errorCallback: null
193
+ }
194
+ };
195
+
196
+ export { plugin as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chartjs-plugin-chart2music",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "Chart.js plugin for Chart2Music. Turns chart.js charts into music so the blind can hear data.",
6
6
  "main": "dist/plugin.js",