-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
add Chart2Music keyboard and sound accessibility features #6680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 65 commits
6fb9c65
87b4c5c
64ae505
d37d24f
d0467bb
3a73776
b4df959
97b608f
6205d62
7e40cde
6d9185b
935cf54
44deae2
c38ab67
c6a27be
cac174c
159ca1b
7467a47
08b820b
41b0fb9
94e25c3
61ea8ce
c9a0b91
cc92639
7baa0d3
3127dee
2429e55
00572e8
30f4e11
247b301
d08adfb
dbce5da
e1f9563
3f4ab54
92d97b7
6b2dcc9
b5bb517
25ca44a
7328324
00b2750
b04bb17
d4bd71b
af2ff80
80b4cf3
ee99f35
3841640
d84d108
0c43407
4c75035
3c49ed6
f7be666
4a260af
f67c513
41baab9
26754b7
ed18907
1e4bcef
2c1abf4
614fb8b
5f5e599
fe9736e
59ad5c6
d18c584
043d408
129eb3b
acc8517
c5ad240
0113d90
771029e
bfbbbf1
cadfd7f
56e582d
8e55f98
86d75d8
15671b0
057e067
44f7ad3
8e3b62b
aed1e02
1777fba
3a0384f
dba0b53
56f2ebc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# accessibility | ||
|
||
While anticipating more accessibility libraries in the future, as of today this is a trivial wrapper over the `sonification` directory. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# `plotly.js` wrapper for `chart2music`, sonified charts | ||
|
||
This wrapper attaches a context object to `gd._context._c2m` with the following properties: | ||
|
||
* `.options`: the options object as required by c2m | ||
* `.info`: the info object as required by c2m | ||
* `.ccOptions`: information about where to place the closed caption div | ||
* `.c2mHandler`: the object returned by calling the c2m library's initializer | ||
|
||
The first three have most of their values set by the defaults in *src/plot_api/plot_config.js*. | ||
|
||
|
||
### index.js | ||
|
||
**index.js** exposes the following api: | ||
|
||
* `initC2M(gd, defaultConfig)` full resets the `c2mChart` object. | ||
* `defaultConfig` is equal to `gd._context.sonification`, as defined in *src/plot_api/plot_config.js*. | ||
|
||
|
||
* `initClosedCaptionDiv(gd, config)` finds or creates the closed caption div, depending on `config`. | ||
* `config` is equal to `defaultConfig.closedCaptions`. | ||
|
||
### all_codecs.js | ||
**all_codecs.js** agregates all the individual **_codec.js* files, all are expect to export exactly two functions: `test` and `process`. | ||
|
||
I chose to aggregate all codecs in a separate file because it will ultimately be a 1-1 conversion if `plotly.js` adopts ES modules. | ||
|
||
## Writing a Codec | ||
|
||
This section is TODO | ||
|
||
chart2music supports N types of graphs. We should figure out how how to convert as many plotly types to chart2music types. I will write descriptions for each type in the folder, if not the actual code, along with what `test` must return and what `process` must return. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use strict'; | ||
|
||
var scatterXY = require('./xy_scatter_codec'); | ||
|
||
exports.codecs = [ scatterXY ]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
'use strict'; | ||
|
||
var c2mPlotly = require('.'); | ||
|
||
function enable_sonification(gd) { | ||
// Collecting defaults | ||
var defaultConfig = gd._context.sonification; | ||
c2mPlotly.initC2M(gd, defaultConfig); | ||
} | ||
exports.enable_sonification = enable_sonification; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
'use strict'; | ||
|
||
var c2m = require('chart2music'); | ||
var Fx = require('../../components/fx'); | ||
var Lib = require('../../lib'); | ||
|
||
var codecs = require('./all_codecs').codecs; | ||
|
||
function initC2M(gd, defaultConfig) { | ||
// TODO: should this be Lib.getGraphDiv()? | ||
// TODO what is there besides a fullReset? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you elaborate on this? @ayjayt There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi sorry! I just fixed my github notifications today Line 10: there is a function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
// TODO Do we need the capacity to add data (live listen?) | ||
// TODO Do we need the capacity to reset all data | ||
var c2mContext = gd._context._c2m = {}; | ||
c2mContext.options = Lib.extendDeepAll({}, defaultConfig.options); | ||
c2mContext.info = Lib.extendDeepAll({}, defaultConfig.info); | ||
c2mContext.ccOptions = Lib.extendDeepAll({}, defaultConfig.closedCaptions); | ||
|
||
var labels = []; // TODO this probably needs to be stored in context- | ||
// when data is updated this specific instance needs to be updated | ||
// or the below function eneds to be reset to use the new instance of labels. | ||
c2mContext.options.onFocusCallback = function(dataInfo) { | ||
Fx.hover(gd, [{ | ||
curveNumber: labels.indexOf(dataInfo.slice), | ||
pointNumber: dataInfo.index | ||
}]); | ||
}; | ||
// I generally don't like using closures like this. | ||
// In this case it works out, we effectively treat 'labels' | ||
// like a global, but it's a local. | ||
// I'd rather the callback be given its c2m handler which | ||
// could store extra data. Or bind c2mContext as `this`. | ||
|
||
var titleText = gd._fullLayout.title?.text ?? 'Chart'; | ||
marthacryan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var ccElement = initClosedCaptionDiv(gd, c2mContext.ccOptions); | ||
|
||
// TODO: | ||
// I believe that title OR title.text could contain the information needed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @archmoj Is that accurate that title or title.text could contain the information needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The preferred way of setting a title is via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should drop support for that! |
||
// So we should stop if title is a type string | ||
// Furthermore, I think that the traces would have to point to their axis, | ||
// since it might not be x1, could be x2, etc | ||
// So this really needs to be part of process() | ||
var xAxisText = gd._fullLayout.xaxis?.title?.text ?? 'X Axis'; | ||
var yAxisText = gd._fullLayout.yaxis?.title?.text ?? 'Y Axis'; | ||
|
||
var c2mData = {}; | ||
var types = []; | ||
var fullData = gd._fullData; | ||
// TODO: We're looping through all traces, and that's fine, but it might be helpful to discern how things are organized | ||
for(var i = 0; i < fullData.length; i++) { | ||
var trace = fullData[i]; | ||
// TODO: what happens if this doesn't run, weird c2m errors | ||
for(var codecI = 0; codecI < codecs.length; codecI++) { | ||
var test = codecs[codecI].test(trace); | ||
if (!test) continue; | ||
var label = test.name ? test.name : i.toString() + ' '; | ||
|
||
var labelCount = 0; | ||
var originalLabel = label; | ||
while (label in c2mData) { | ||
labelCount++; | ||
label = originalLabel + labelCount.toString(); | ||
} | ||
|
||
labels.push(label); | ||
types.push(test.type); | ||
c2mData[label] = codecs[codecI].process(trace); | ||
} | ||
} // TODO add unsupported codec | ||
|
||
c2mContext.c2mHandler = c2m.c2mChart({ | ||
title: titleText, | ||
type: types, | ||
axes: { | ||
x: { // needs to be generated | ||
label: xAxisText | ||
}, | ||
y: { | ||
label: yAxisText | ||
}, | ||
}, | ||
element: gd, | ||
cc: ccElement, | ||
data: c2mData, | ||
options: c2mContext.options, | ||
info: c2mContext.info | ||
}); | ||
// TODO: We need to handle the possible error that c2mChart returns | ||
} | ||
exports.initC2M = initC2M; | ||
|
||
function initClosedCaptionDiv(gd, config) { | ||
if(config.generate) { | ||
var closedCaptions = document.createElement('div'); | ||
closedCaptions.id = config.elId; | ||
closedCaptions.className = config.elClassname; | ||
gd.parentNode.insertBefore(closedCaptions, gd.nextSibling); // this really might not work | ||
return closedCaptions; | ||
} else { | ||
return document.getElementById(config.elId); | ||
} | ||
} | ||
|
||
exports.initClosedCaptionDiv = initClosedCaptionDiv; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
'use strict'; | ||
// This codec serves for one x axis and one y axis | ||
|
||
function test(trace) { | ||
if(!trace) return null; | ||
if(!trace.type || trace.type !== 'scatter') return null; | ||
// TODO: I think we can have an x OR a y | ||
if((trace.y === undefined || trace.y.length === 0) && (trace.x === undefined || trace.x.length === 0)) return null; | ||
return {type: 'scatter', name: trace.name}; | ||
} | ||
exports.test = test; | ||
|
||
function process(trace) { | ||
var traceData = []; | ||
var x = trace.x && trace.x.length !== 0 ? trace.x : []; | ||
var y = trace.y && trace.y.length !== 0 ? trace.y : []; | ||
|
||
for(var p = 0; p < Math.max(x.length, y.length); p++) { | ||
traceData.push({ // TODO I think we're copying the data here, bad. | ||
x: x[p] ? x[p] : p, | ||
y: y[p] ? y[p] : p, | ||
label: trace.text[p] ? trace.text[p] : p | ||
}); | ||
} | ||
return traceData; | ||
} | ||
exports.process = process; |
Uh oh!
There was an error while loading. Please reload this page.