@decidables/accumulable-elements 0.3.3 → 0.3.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decidables/accumulable-elements",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "accumulable-elements: Web Components for visualizing the Diffusion Decision Model",
5
5
  "keywords": [
6
6
  "web component",
@@ -53,8 +53,8 @@
53
53
  "gulp": "^5.0.1"
54
54
  },
55
55
  "dependencies": {
56
- "@decidables/accumulable-math": "^0.2.3",
57
- "@decidables/decidables-elements": "^0.5.3",
56
+ "@decidables/accumulable-math": "^0.2.4",
57
+ "@decidables/decidables-elements": "^0.5.4",
58
58
  "@lit-labs/motion": "^1.0.9",
59
59
  "@observablehq/plot": "^0.6.17",
60
60
  "bayes.js": "https://github.com/akrawitz/bayes.js.git#commit=c7b091b75f85a86b6a3965a983b31e23d9ef456f",
@@ -62,5 +62,5 @@
62
62
  "lit": "^3.3.1",
63
63
  "regenerator-runtime": "^0.14.1"
64
64
  },
65
- "gitHead": "908a95b3b82c45229f9502d161cf4a2c90f899aa"
65
+ "gitHead": "c0a4df1a6d35d43648d21c14d63cbcee6dd28f9e"
66
66
  }
@@ -546,8 +546,13 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
546
546
  }
547
547
  }
548
548
 
549
- .measure {
550
- stroke-width: 2;
549
+ .measure .line.short {
550
+ stroke-width: 0;
551
+ }
552
+
553
+ .measure .markers {
554
+ fill: none;
555
+ stroke-width: 0;
551
556
  }
552
557
 
553
558
  .measure .label {
@@ -569,7 +574,9 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
569
574
  stroke: var(---color-z);
570
575
  }
571
576
 
572
- .measure.z .label {
577
+ .measure.z .label,
578
+ /* Hack to avoid Safari weirdness */
579
+ .measure.z .label tspan {
573
580
  dominant-baseline: hanging;
574
581
  text-anchor: start;
575
582
  }
@@ -592,6 +599,43 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
592
599
  text-anchor: middle;
593
600
  }
594
601
 
602
+ .measure-arrow {
603
+ fill: context-stroke;
604
+ stroke: context-stroke;
605
+ }
606
+
607
+ .measure-arrow .arrow {
608
+ stroke-width: 1;
609
+ }
610
+
611
+ .measure-arrow.capped .cap {
612
+ stroke-width: 2;
613
+ }
614
+
615
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
616
+ .measure-arrow.a {
617
+ fill: var(---color-a);
618
+ stroke: var(---color-a);
619
+ }
620
+
621
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
622
+ .measure-arrow.z {
623
+ fill: var(---color-z);
624
+ stroke: var(---color-z);
625
+ }
626
+
627
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
628
+ .measure-arrow.v {
629
+ fill: var(---color-v);
630
+ stroke: var(---color-v);
631
+ }
632
+
633
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
634
+ .measure-arrow.t0 {
635
+ fill: var(---color-t0);
636
+ stroke: var(---color-t0);
637
+ }
638
+
595
639
  .sd .indicator,
596
640
  .mean .indicator {
597
641
  stroke-width: 2;
@@ -617,6 +661,20 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
617
661
  stroke: var(---color-error-dark);
618
662
  }
619
663
 
664
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
665
+ .cap.correct {
666
+ fill: var(---color-correct-dark);
667
+ stroke: var(---color-correct-dark);
668
+ stroke-dasharray: 100%;
669
+ }
670
+
671
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
672
+ .cap.error {
673
+ fill: var(---color-error-dark);
674
+ stroke: var(---color-error-dark);
675
+ stroke-dasharray: 100%;
676
+ }
677
+
620
678
  .rt-label rect {
621
679
  filter: url("#shadow-2");
622
680
 
@@ -883,48 +941,84 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
883
941
  .html(AccumulableElement.svgDefs);
884
942
  const svgDefs = svgEnter.append('defs');
885
943
  // Arrowhead marker for measures
886
- svgDefs.append('marker')
887
- .attr('id', 'measure-arrow')
888
- .attr('orient', 'auto-start-reverse')
889
- .attr('markerUnits', 'userSpaceOnUse')
890
- .attr('viewBox', '-5 -5 10 10')
891
- .attr('refX', '2')
892
- .attr('refY', '0')
893
- .attr('markerWidth', '10')
894
- .attr('markerHeight', '10')
895
- .append('path')
896
- .attr('stroke', 'context-stroke')
897
- .attr('fill', 'context-stroke')
898
- .attr('d', 'M -3 -3 l 6 3 l -6 3 z');
944
+ const measureArrow = (parameter) => {
945
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
946
+ svgDefs.append('marker')
947
+ .attr('id', `measure-arrow-${parameter}`)
948
+ .attr('class', `measure-arrow ${parameter}`)
949
+ .attr('orient', 'auto-start-reverse')
950
+ .attr('markerUnits', 'userSpaceOnUse')
951
+ .attr('viewBox', '-10 -6 12 12')
952
+ .attr('refX', '0')
953
+ .attr('refY', '0')
954
+ .attr('markerWidth', '12')
955
+ .attr('markerHeight', '12')
956
+ .append('path')
957
+ .attr('class', 'arrow')
958
+ .attr('d', 'M -7 -3 l 6 3 l -6 3 z');
959
+ };
960
+ const measureCappedArrow = (parameter) => {
961
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
962
+ const marker = svgDefs.append('marker')
963
+ .attr('id', `measure-capped-arrow-${parameter}`)
964
+ .attr('class', `measure-arrow capped ${parameter}`)
965
+ .attr('orient', 'auto-start-reverse')
966
+ .attr('markerUnits', 'userSpaceOnUse')
967
+ .attr('viewBox', '-10 -6 12 12')
968
+ .attr('refX', '0')
969
+ .attr('refY', '0')
970
+ .attr('markerWidth', '12')
971
+ .attr('markerHeight', '12');
972
+ marker
973
+ .append('path')
974
+ .attr('class', 'arrow')
975
+ .attr('d', 'M -7 -3 l 6 3 l -6 3 z');
976
+ marker
977
+ .append('path')
978
+ .attr('class', 'cap')
979
+ .attr('d', 'M 0 -4 l 0 8');
980
+ };
981
+ measureArrow('a');
982
+ measureArrow('z');
983
+ measureCappedArrow('v');
984
+ measureArrow('t0');
985
+ measureCappedArrow('t0');
899
986
  // Flat markers for SDs
900
- svgDefs.append('marker')
901
- .attr('id', 'model-sd-cap')
902
- .attr('orient', 'auto-start-reverse')
903
- .attr('markerUnits', 'userSpaceOnUse')
904
- .attr('viewBox', '-5 -5 10 10')
905
- .attr('refX', '0')
906
- .attr('refY', '0')
907
- .attr('markerWidth', '10')
908
- .attr('markerHeight', '10')
909
- .append('path')
910
- .attr('stroke', 'context-stroke')
911
- .attr('fill', 'context-stroke')
912
- .attr('stroke-width', '2')
913
- .attr('d', 'M 0 -4 l 0 8');
914
- svgDefs.append('marker')
915
- .attr('id', 'data-sd-cap')
916
- .attr('orient', 'auto-start-reverse')
917
- .attr('markerUnits', 'userSpaceOnUse')
918
- .attr('viewBox', '-5 -5 10 10')
919
- .attr('refX', '0')
920
- .attr('refY', '0')
921
- .attr('markerWidth', '10')
922
- .attr('markerHeight', '10')
923
- .append('path')
924
- .attr('stroke', 'context-stroke')
925
- .attr('fill', 'context-stroke')
926
- .attr('stroke-width', '2')
927
- .attr('d', 'M 0 -3 l 0 6');
987
+ const sdCap = (outcome) => {
988
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
989
+ svgDefs.append('marker')
990
+ .attr('id', `model-sd-cap-${outcome}`)
991
+ .attr('class', `model-sd cap ${outcome}`)
992
+ .attr('orient', 'auto-start-reverse')
993
+ .attr('markerUnits', 'userSpaceOnUse')
994
+ .attr('viewBox', '-5 -5 10 10')
995
+ .attr('refX', '0')
996
+ .attr('refY', '0')
997
+ .attr('markerWidth', '10')
998
+ .attr('markerHeight', '10')
999
+ .append('path')
1000
+ .attr('stroke', 'context-stroke')
1001
+ .attr('fill', 'context-stroke')
1002
+ .attr('stroke-width', '2')
1003
+ .attr('d', 'M 0 -4 l 0 8');
1004
+ svgDefs.append('marker')
1005
+ .attr('id', `data-sd-cap-${outcome}`)
1006
+ .attr('class', `data-sd cap ${outcome}`)
1007
+ .attr('orient', 'auto-start-reverse')
1008
+ .attr('markerUnits', 'userSpaceOnUse')
1009
+ .attr('viewBox', '-5 -5 10 10')
1010
+ .attr('refX', '0')
1011
+ .attr('refY', '0')
1012
+ .attr('markerWidth', '10')
1013
+ .attr('markerHeight', '10')
1014
+ .append('path')
1015
+ .attr('stroke', 'context-stroke')
1016
+ .attr('fill', 'context-stroke')
1017
+ .attr('stroke-width', '2')
1018
+ .attr('d', 'M 0 -3 l 0 6');
1019
+ };
1020
+ sdCap('error');
1021
+ sdCap('correct');
928
1022
  const gradient = svgDefs.append('linearGradient')
929
1023
  .attr('id', 'path-animate')
930
1024
  .attr('gradientUnits', 'userSpaceOnUse')
@@ -1842,6 +1936,9 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1842
1936
  // EXIT
1843
1937
  t0zUpdate.exit().remove();
1844
1938
 
1939
+ // Measures
1940
+ const markerCorrection = 2;
1941
+
1845
1942
  // a Measure
1846
1943
  // DATA-JOIN
1847
1944
  const aUpdate = evidenceOverlayerMerge.selectAll('.measure.a')
@@ -1850,9 +1947,12 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1850
1947
  const aEnter = aUpdate.enter().append('g')
1851
1948
  .classed('measure a', true);
1852
1949
  aEnter.append('line')
1853
- .classed('line', true)
1854
- .attr('marker-start', 'url(#measure-arrow)')
1855
- .attr('marker-end', 'url(#measure-arrow)');
1950
+ .classed('line', true);
1951
+ aEnter.append('line')
1952
+ .classed('markers', true)
1953
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
1954
+ .attr('marker-start', 'url(#measure-arrow-a)')
1955
+ .attr('marker-end', 'url(#measure-arrow-a)');
1856
1956
  const aLabel = aEnter.append('text')
1857
1957
  .classed('label', true);
1858
1958
  aLabel.append('tspan')
@@ -1864,15 +1964,26 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1864
1964
  aLabel.append('tspan')
1865
1965
  .classed('value', true);
1866
1966
  // MERGE
1967
+ const aLength = Math.abs(evidenceScale(this.bounds.upper) - evidenceScale(this.bounds.lower));
1968
+ const aShort = aLength <= (markerCorrection * 2);
1867
1969
  const aMerge = aEnter.merge(aUpdate);
1868
1970
  aMerge.select('.line')
1971
+ .classed('short', aShort)
1869
1972
  .transition()
1870
1973
  .duration(this.drag ? 0 : transitionDuration)
1871
1974
  .ease(d3.easeCubicOut)
1872
1975
  .attr('x1', timeScale(this.scale.time.max) - this.rem * 0.75)
1873
- .attr('y1', evidenceScale(this.bounds.upper) + 2)
1976
+ .attr('y1', evidenceScale(this.bounds.upper) + markerCorrection)
1977
+ .attr('x2', timeScale(this.scale.time.max) - this.rem * 0.75)
1978
+ .attr('y2', evidenceScale(this.bounds.lower) - markerCorrection);
1979
+ aMerge.select('.markers')
1980
+ .transition()
1981
+ .duration(this.drag ? 0 : transitionDuration)
1982
+ .ease(d3.easeCubicOut)
1983
+ .attr('x1', timeScale(this.scale.time.max) - this.rem * 0.75)
1984
+ .attr('y1', evidenceScale(this.bounds.upper))
1874
1985
  .attr('x2', timeScale(this.scale.time.max) - this.rem * 0.75)
1875
- .attr('y2', evidenceScale(this.bounds.lower) - 2);
1986
+ .attr('y2', evidenceScale(this.bounds.lower));
1876
1987
  const aLabelMerge = aMerge.select('.label')
1877
1988
  .transition()
1878
1989
  .duration(this.drag ? 0 : transitionDuration)
@@ -1892,9 +2003,12 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1892
2003
  const zEnter = zUpdate.enter().append('g')
1893
2004
  .classed('measure z', true);
1894
2005
  zEnter.append('line')
1895
- .classed('line', true)
1896
- .attr('marker-start', 'url(#measure-arrow)')
1897
- .attr('marker-end', 'url(#measure-arrow)');
2006
+ .classed('line', true);
2007
+ zEnter.append('line')
2008
+ .classed('markers', true)
2009
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
2010
+ .attr('marker-start', 'url(#measure-arrow-z)')
2011
+ .attr('marker-end', 'url(#measure-arrow-z)');
1898
2012
  const zLabel = zEnter.append('text')
1899
2013
  .classed('label', true);
1900
2014
  zLabel.append('tspan')
@@ -1906,15 +2020,26 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1906
2020
  zLabel.append('tspan')
1907
2021
  .classed('value', true);
1908
2022
  // MERGE
2023
+ const zLength = Math.abs(evidenceScale(this.startingPoint) - evidenceScale(this.bounds.lower));
2024
+ const zShort = zLength <= (markerCorrection * 2);
1909
2025
  const zMerge = zEnter.merge(zUpdate);
1910
2026
  zMerge.select('.line')
2027
+ .classed('short', zShort)
1911
2028
  .transition()
1912
2029
  .duration(this.drag ? 0 : transitionDuration)
1913
2030
  .ease(d3.easeCubicOut)
1914
2031
  .attr('x1', timeScale(this.scale.time.min) + this.rem * 0.75)
1915
- .attr('y1', evidenceScale(this.startingPoint) + 2)
2032
+ .attr('y1', evidenceScale(this.startingPoint) + markerCorrection)
1916
2033
  .attr('x2', timeScale(this.scale.time.min) + this.rem * 0.75)
1917
- .attr('y2', evidenceScale(this.bounds.lower) - 2);
2034
+ .attr('y2', evidenceScale(this.bounds.lower) - markerCorrection);
2035
+ zMerge.select('.markers')
2036
+ .transition()
2037
+ .duration(this.drag ? 0 : transitionDuration)
2038
+ .ease(d3.easeCubicOut)
2039
+ .attr('x1', timeScale(this.scale.time.min) + this.rem * 0.75)
2040
+ .attr('y1', evidenceScale(this.startingPoint))
2041
+ .attr('x2', timeScale(this.scale.time.min) + this.rem * 0.75)
2042
+ .attr('y2', evidenceScale(this.bounds.lower));
1918
2043
  const zLabelMerge = zMerge.select('.label')
1919
2044
  .transition()
1920
2045
  .duration(this.drag ? 0 : transitionDuration)
@@ -1934,9 +2059,12 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1934
2059
  const vEnter = vUpdate.enter().append('g')
1935
2060
  .classed('measure v', true);
1936
2061
  vEnter.append('path')
1937
- .classed('line', true)
1938
- .attr('marker-start', 'url(#measure-arrow)')
1939
- .attr('marker-end', 'url(#measure-arrow)');
2062
+ .classed('line', true);
2063
+ vEnter.append('path')
2064
+ .classed('markers', true)
2065
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
2066
+ .attr('marker-start', 'url(#measure-capped-arrow-v)')
2067
+ .attr('marker-end', 'url(#measure-capped-arrow-v)');
1940
2068
  const vLabel = vEnter.append('text')
1941
2069
  .classed('label', true);
1942
2070
  vLabel.append('tspan')
@@ -1948,18 +2076,38 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1948
2076
  vLabel.append('tspan')
1949
2077
  .classed('value', true);
1950
2078
  // MERGE
1951
- const driftAngle = Math.atan((this.v / 1000) * scaleRatio);
2079
+ // Full path
1952
2080
  const driftHypotenuse = timeScale(200) - timeScale(0) + this.rem * 0.75;
2081
+ const driftAngle = Math.atan((this.v / 1000) * scaleRatio);
1953
2082
  const driftX = Math.cos(driftAngle) * driftHypotenuse;
1954
2083
  const driftY = Math.sin(driftAngle) * driftHypotenuse;
2084
+ // Corrected path
2085
+ const driftCorrection = markerCorrection / driftHypotenuse;
2086
+ const driftAngleCorrected = Math.atan((this.v / 1000) * scaleRatio) - driftCorrection;
2087
+ const driftStartX = Math.cos(driftCorrection) * driftHypotenuse;
2088
+ const driftStartY = Math.sin(driftCorrection) * driftHypotenuse;
2089
+ const driftEndX = Math.cos(driftAngleCorrected) * driftHypotenuse;
2090
+ const driftEndY = Math.sin(driftAngleCorrected) * driftHypotenuse;
2091
+ // Short path?
2092
+ const vLength = driftAngleCorrected * driftHypotenuse;
2093
+ const vShort = vLength <= (markerCorrection * 2);
1955
2094
  const vMerge = vEnter.merge(vUpdate);
1956
2095
  vMerge.select('.line')
2096
+ .classed('short', vShort)
2097
+ .transition()
2098
+ .duration(this.drag ? 0 : transitionDuration)
2099
+ .ease(d3.easeCubicOut)
2100
+ .attr('d', `
2101
+ M ${timeScale(this.t0) + driftStartX}, ${evidenceScale(this.startingPoint) - driftStartY}
2102
+ A ${timeScale(200) - timeScale(0) + this.rem * 0.75} ${timeScale(200) - timeScale(0) + this.rem * 0.75} 0 0 0 ${timeScale(this.t0) + driftEndX} ${evidenceScale(this.startingPoint) - driftEndY}
2103
+ `);
2104
+ vMerge.select('.markers')
1957
2105
  .transition()
1958
2106
  .duration(this.drag ? 0 : transitionDuration)
1959
2107
  .ease(d3.easeCubicOut)
1960
2108
  .attr('d', `
1961
2109
  M ${timeScale(this.t0 + 200) + this.rem * 0.75}, ${evidenceScale(this.startingPoint)}
1962
- A ${timeScale(200) - timeScale(0)} ${timeScale(200) - timeScale(0)} 0 0 0 ${timeScale(this.t0) + driftX} ${evidenceScale(this.startingPoint) - driftY}
2110
+ A ${timeScale(200) - timeScale(0) + this.rem * 0.75} ${timeScale(200) - timeScale(0) + this.rem * 0.75} 0 0 0 ${timeScale(this.t0) + driftX} ${evidenceScale(this.startingPoint) - driftY}
1963
2111
  `);
1964
2112
  const vLabelMerge = vMerge.select('.label')
1965
2113
  .transition()
@@ -1980,9 +2128,12 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1980
2128
  const t0Enter = t0Update.enter().append('g')
1981
2129
  .classed('measure t0', true);
1982
2130
  t0Enter.append('line')
1983
- .classed('line', true)
1984
- .attr('marker-start', 'url(#measure-arrow)')
1985
- .attr('marker-end', 'url(#measure-arrow)');
2131
+ .classed('line', true);
2132
+ t0Enter.append('line')
2133
+ .classed('markers', true)
2134
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
2135
+ .attr('marker-start', 'url(#measure-arrow-t0)')
2136
+ .attr('marker-end', 'url(#measure-capped-arrow-t0)');
1986
2137
  const t0Label = t0Enter.append('text')
1987
2138
  .classed('label', true);
1988
2139
  t0Label.append('tspan')
@@ -1994,14 +2145,25 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
1994
2145
  t0Label.append('tspan')
1995
2146
  .classed('value', true);
1996
2147
  // MERGE
2148
+ const t0Length = Math.abs(timeScale(0) - timeScale(this.t0));
2149
+ const t0Short = t0Length <= (markerCorrection * 2);
1997
2150
  const t0Merge = t0Enter.merge(t0Update);
1998
2151
  t0Merge.select('.line')
2152
+ .classed('short', t0Short)
2153
+ .transition()
2154
+ .duration(this.drag ? 0 : transitionDuration)
2155
+ .ease(d3.easeCubicOut)
2156
+ .attr('x1', timeScale(0) + markerCorrection)
2157
+ .attr('y1', evidenceScale(this.startingPoint) - this.rem * 0.75)
2158
+ .attr('x2', timeScale(this.t0) - markerCorrection)
2159
+ .attr('y2', evidenceScale(this.startingPoint) - this.rem * 0.75);
2160
+ t0Merge.select('.markers')
1999
2161
  .transition()
2000
2162
  .duration(this.drag ? 0 : transitionDuration)
2001
2163
  .ease(d3.easeCubicOut)
2002
- .attr('x1', timeScale(0) + 2)
2164
+ .attr('x1', timeScale(0))
2003
2165
  .attr('y1', evidenceScale(this.startingPoint) - this.rem * 0.75)
2004
- .attr('x2', timeScale(this.t0) - 2)
2166
+ .attr('x2', timeScale(this.t0))
2005
2167
  .attr('y2', evidenceScale(this.startingPoint) - this.rem * 0.75);
2006
2168
  const t0LabelMerge = t0Merge.select('.label')
2007
2169
  .transition()
@@ -2091,8 +2253,9 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
2091
2253
  .attr('class', (datum) => { return `model sd ${datum.outcome}`; });
2092
2254
  sdEnter.append('line')
2093
2255
  .classed('indicator', true)
2094
- .attr('marker-start', 'url(#model-sd-cap)')
2095
- .attr('marker-end', 'url(#model-sd-cap)');
2256
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
2257
+ .attr('marker-start', (datum) => { return `url(#model-sd-cap-${datum.outcome})`; })
2258
+ .attr('marker-end', (datum) => { return `url(#model-sd-cap-${datum.outcome})`; });
2096
2259
  // MERGE
2097
2260
  const sdMerge = sdEnter.merge(sdUpdate);
2098
2261
  sdMerge.select('.indicator')
@@ -2119,8 +2282,9 @@ export default class DDMModel extends DecidablesMixinResizeable(AccumulableEleme
2119
2282
  .attr('class', (datum) => { return `data sd ${datum.outcome}`; });
2120
2283
  dataSDEnter.append('line')
2121
2284
  .classed('indicator', true)
2122
- .attr('marker-start', 'url(#data-sd-cap)')
2123
- .attr('marker-end', 'url(#data-sd-cap)')
2285
+ /* Hack to avoid lack of context-stroke and context-fill in Safari */
2286
+ .attr('marker-start', (datum) => { return `url(#data-sd-cap-${datum.outcome})`; })
2287
+ .attr('marker-end', (datum) => { return `url(#data-sd-cap-${datum.outcome})`; })
2124
2288
  .attr('y1', (datum) => {
2125
2289
  return datum.densityScale(0) + ((datum.outcome === 'correct') ? 0.375 : -0.375) * this.rem;
2126
2290
  })