Skip to content

Commit e577ad1

Browse files
committed
Merge branch 'master' into carpet-plots
Conflicts: src/plots/plots.js
2 parents 7ac5f42 + d3bd87f commit e577ad1

28 files changed

+535
-464
lines changed

src/components/annotations/annotation_defaults.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ module.exports = function handleAnnotationDefaults(annIn, annOut, fullLayout, op
3030
if(!(visible || clickToShow)) return annOut;
3131

3232
coerce('opacity');
33-
coerce('align');
3433
coerce('bgcolor');
3534

3635
var borderColor = coerce('bordercolor'),
@@ -45,6 +44,12 @@ module.exports = function handleAnnotationDefaults(annIn, annOut, fullLayout, op
4544
coerce('textangle');
4645
Lib.coerceFont(coerce, 'font', fullLayout.font);
4746

47+
coerce('width');
48+
coerce('align');
49+
50+
var h = coerce('height');
51+
if(h) coerce('valign');
52+
4853
// positioning
4954
var axLetters = ['x', 'y'],
5055
arrowPosDflt = [-10, -30],

src/components/annotations/attributes.js

+36-4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@ module.exports = {
4949
font: extendFlat({}, fontAttrs, {
5050
description: 'Sets the annotation text font.'
5151
}),
52+
width: {
53+
valType: 'number',
54+
min: 1,
55+
dflt: null,
56+
role: 'style',
57+
description: [
58+
'Sets an explicit width for the text box. null (default) lets the',
59+
'text set the box width. Wider text will be clipped.',
60+
'There is no automatic wrapping; use <br> to start a new line.'
61+
].join(' ')
62+
},
63+
height: {
64+
valType: 'number',
65+
min: 1,
66+
dflt: null,
67+
role: 'style',
68+
description: [
69+
'Sets an explicit height for the text box. null (default) lets the',
70+
'text set the box height. Taller text will be clipped.'
71+
].join(' ')
72+
},
5273
opacity: {
5374
valType: 'number',
5475
min: 0,
@@ -63,10 +84,21 @@ module.exports = {
6384
dflt: 'center',
6485
role: 'style',
6586
description: [
66-
'Sets the vertical alignment of the `text` with',
67-
'respect to the set `x` and `y` position.',
68-
'Has only an effect if `text` spans more two or more lines',
69-
'(i.e. `text` contains one or more <br> HTML tags).'
87+
'Sets the horizontal alignment of the `text` within the box.',
88+
'Has an effect only if `text` spans more two or more lines',
89+
'(i.e. `text` contains one or more <br> HTML tags) or if an',
90+
'explicit width is set to override the text width.'
91+
].join(' ')
92+
},
93+
valign: {
94+
valType: 'enumerated',
95+
values: ['top', 'middle', 'bottom'],
96+
dflt: 'middle',
97+
role: 'style',
98+
description: [
99+
'Sets the vertical alignment of the `text` within the box.',
100+
'Has an effect only if an explicit height is set to override',
101+
'the text height.'
70102
].join(' ')
71103
},
72104
bgcolor: {

src/components/annotations/draw.js

+60-20
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,14 @@ function drawOne(gd, index) {
7272
var optionsIn = (layout.annotations || [])[index],
7373
options = fullLayout.annotations[index];
7474

75+
var annClipID = 'clip' + fullLayout._uid + '_ann' + index;
76+
7577
// this annotation is gone - quit now after deleting it
7678
// TODO: use d3 idioms instead of deleting and redrawing every time
77-
if(!optionsIn || options.visible === false) return;
79+
if(!optionsIn || options.visible === false) {
80+
d3.selectAll('#' + annClipID).remove();
81+
return;
82+
}
7883

7984
var xa = Axes.getFromId(gd, options.xref),
8085
ya = Axes.getFromId(gd, options.yref),
@@ -118,6 +123,18 @@ function drawOne(gd, index) {
118123
.call(Color.stroke, options.bordercolor)
119124
.call(Color.fill, options.bgcolor);
120125

126+
var isSizeConstrained = options.width || options.height;
127+
128+
var annTextClip = fullLayout._defs.select('.clips')
129+
.selectAll('#' + annClipID)
130+
.data(isSizeConstrained ? [0] : []);
131+
132+
annTextClip.enter().append('clipPath')
133+
.classed('annclip', true)
134+
.attr('id', annClipID)
135+
.append('rect');
136+
annTextClip.exit().remove();
137+
121138
var font = options.font;
122139

123140
var annText = annTextGroupInner.append('text')
@@ -144,19 +161,21 @@ function drawOne(gd, index) {
144161
// at the end, even if their position changes
145162
annText.selectAll('tspan.line').attr({y: 0, x: 0});
146163

147-
var mathjaxGroup = annTextGroupInner.select('.annotation-math-group'),
148-
hasMathjax = !mathjaxGroup.empty(),
149-
anntextBB = Drawing.bBox(
150-
(hasMathjax ? mathjaxGroup : annText).node()),
151-
annwidth = anntextBB.width,
152-
annheight = anntextBB.height,
153-
outerwidth = Math.round(annwidth + 2 * borderfull),
154-
outerheight = Math.round(annheight + 2 * borderfull);
164+
var mathjaxGroup = annTextGroupInner.select('.annotation-math-group');
165+
var hasMathjax = !mathjaxGroup.empty();
166+
var anntextBB = Drawing.bBox(
167+
(hasMathjax ? mathjaxGroup : annText).node());
168+
var textWidth = anntextBB.width;
169+
var textHeight = anntextBB.height;
170+
var annWidth = options.width || textWidth;
171+
var annHeight = options.height || textHeight;
172+
var outerWidth = Math.round(annWidth + 2 * borderfull);
173+
var outerHeight = Math.round(annHeight + 2 * borderfull);
155174

156175

157176
// save size in the annotation object for use by autoscale
158-
options._w = annwidth;
159-
options._h = annheight;
177+
options._w = annWidth;
178+
options._h = annHeight;
160179

161180
function shiftFraction(v, anchor) {
162181
if(anchor === 'auto') {
@@ -181,8 +200,8 @@ function drawOne(gd, index) {
181200
ax = Axes.getFromId(gd, axRef),
182201
dimAngle = (textangle + (axLetter === 'x' ? 0 : -90)) * Math.PI / 180,
183202
// note that these two can be either positive or negative
184-
annSizeFromWidth = outerwidth * Math.cos(dimAngle),
185-
annSizeFromHeight = outerheight * Math.sin(dimAngle),
203+
annSizeFromWidth = outerWidth * Math.cos(dimAngle),
204+
annSizeFromHeight = outerHeight * Math.sin(dimAngle),
186205
// but this one is the positive total size
187206
annSize = Math.abs(annSizeFromWidth) + Math.abs(annSizeFromHeight),
188207
anchor = options[axLetter + 'anchor'],
@@ -299,22 +318,43 @@ function drawOne(gd, index) {
299318
return;
300319
}
301320

321+
var xShift = 0;
322+
var yShift = 0;
323+
324+
if(options.align !== 'left') {
325+
xShift = (annWidth - textWidth) * (options.align === 'center' ? 0.5 : 1);
326+
}
327+
if(options.valign !== 'top') {
328+
yShift = (annHeight - textHeight) * (options.valign === 'middle' ? 0.5 : 1);
329+
}
330+
302331
if(hasMathjax) {
303-
mathjaxGroup.select('svg').attr({x: borderfull - 1, y: borderfull});
332+
mathjaxGroup.select('svg').attr({
333+
x: borderfull + xShift - 1,
334+
y: borderfull + yShift
335+
})
336+
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null);
304337
}
305338
else {
306-
var texty = borderfull - anntextBB.top,
307-
textx = borderfull - anntextBB.left;
308-
annText.attr({x: textx, y: texty});
339+
var texty = borderfull + yShift - anntextBB.top,
340+
textx = borderfull + xShift - anntextBB.left;
341+
annText.attr({
342+
x: textx,
343+
y: texty
344+
})
345+
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null);
309346
annText.selectAll('tspan.line').attr({y: texty, x: textx});
310347
}
311348

349+
annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,
350+
annWidth, annHeight);
351+
312352
annTextBG.call(Drawing.setRect, borderwidth / 2, borderwidth / 2,
313-
outerwidth - borderwidth, outerheight - borderwidth);
353+
outerWidth - borderwidth, outerHeight - borderwidth);
314354

315355
annTextGroupInner.call(Drawing.setTranslate,
316-
Math.round(annPosPx.x.text - outerwidth / 2),
317-
Math.round(annPosPx.y.text - outerheight / 2));
356+
Math.round(annPosPx.x.text - outerWidth / 2),
357+
Math.round(annPosPx.y.text - outerHeight / 2));
318358

319359
/*
320360
* rotate text and background

src/lib/geojson_utils.js

+10-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
'use strict';
1111

12+
var BADNUM = require('../constants/numerical').BADNUM;
13+
1214
/**
1315
* Convert calcTrace to GeoJSON 'MultiLineString' coordinate arrays
1416
*
@@ -21,18 +23,19 @@
2123
*
2224
*/
2325
exports.calcTraceToLineCoords = function(calcTrace) {
24-
var trace = calcTrace[0].trace,
25-
connectgaps = trace.connectgaps;
26+
var trace = calcTrace[0].trace;
27+
var connectgaps = trace.connectgaps;
2628

27-
var coords = [],
28-
lineString = [];
29+
var coords = [];
30+
var lineString = [];
2931

3032
for(var i = 0; i < calcTrace.length; i++) {
3133
var calcPt = calcTrace[i];
34+
var lonlat = calcPt.lonlat;
3235

33-
lineString.push(calcPt.lonlat);
34-
35-
if(!connectgaps && calcPt.gapAfter && lineString.length > 0) {
36+
if(lonlat[0] !== BADNUM) {
37+
lineString.push(lonlat);
38+
} else if(!connectgaps && lineString.length > 0) {
3639
coords.push(lineString);
3740
lineString = [];
3841
}

src/plots/mapbox/mapbox.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,14 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
111111
});
112112

113113
// clear navigation container
114-
var className = constants.controlContainerClassName,
115-
controlContainer = self.div.getElementsByClassName(className)[0];
114+
var className = constants.controlContainerClassName;
115+
var controlContainer = self.div.getElementsByClassName(className)[0];
116116
self.div.removeChild(controlContainer);
117117

118+
// make sure canvas does not inherit left and top css
119+
map._canvas.canvas.style.left = '0px';
120+
map._canvas.canvas.style.top = '0px';
121+
118122
self.rejectOnError(reject);
119123

120124
map.once('load', function() {
@@ -176,7 +180,6 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
176180

177181
map.on('dragstart', unhover);
178182
map.on('zoomstart', unhover);
179-
180183
};
181184

182185
proto.updateMap = function(calcData, fullLayout, resolve, reject) {

src/plots/plots.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var Plotly = require('../plotly');
1616
var Registry = require('../registry');
1717
var Lib = require('../lib');
1818
var Color = require('../components/color');
19+
var BADNUM = require('../constants/numerical').BADNUM;
1920

2021
var plots = module.exports = {};
2122

@@ -257,6 +258,9 @@ plots.previousPromises = function(gd) {
257258
* Add source links to your graph inside the 'showSources' config argument.
258259
*/
259260
plots.addLinks = function(gd) {
261+
// Do not do anything if showLink and showSources are not set to true in config
262+
if(!gd._context.showLink && !gd._context.showSources) return;
263+
260264
var fullLayout = gd._fullLayout;
261265

262266
var linkContainer = fullLayout._paper
@@ -847,14 +851,20 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde
847851
coerceSubplotAttr('gl2d', 'xaxis');
848852
coerceSubplotAttr('gl2d', 'yaxis');
849853

850-
supplyTransformDefaults(traceIn, traceOut, layout);
854+
if(plots.traceIs(traceOut, 'showLegend')) {
855+
coerce('showlegend');
856+
coerce('legendgroup');
857+
}
858+
859+
plots.supplyTransformDefaults(traceIn, traceOut, layout);
851860
}
852861

853862
return traceOut;
854863
};
855864

856-
function supplyTransformDefaults(traceIn, traceOut, layout) {
865+
plots.supplyTransformDefaults = function(traceIn, traceOut, layout) {
857866
var globalTransforms = layout._globalTransforms || [];
867+
var transformModules = layout._transformModules || [];
858868

859869
if(!Array.isArray(traceIn.transforms) && globalTransforms.length === 0) return;
860870

@@ -875,15 +885,15 @@ function supplyTransformDefaults(traceIn, traceOut, layout) {
875885
transformOut.type = type;
876886
transformOut._module = _module;
877887

878-
Lib.pushUnique(layout._transformModules, _module);
888+
Lib.pushUnique(transformModules, _module);
879889
}
880890
else {
881891
transformOut = Lib.extendFlat({}, transformIn);
882892
}
883893

884894
containerOut.push(transformOut);
885895
}
886-
}
896+
};
887897

888898
function applyTransforms(fullTrace, fullData, layout, fullLayout) {
889899
var container = fullTrace.transforms,
@@ -2065,11 +2075,8 @@ plots.doCalcdata = function(gd, traces) {
20652075
//
20662076
// This ensures there is a calcdata item for every trace,
20672077
// even if cartesian logic doesn't handle it (for things like legends).
2068-
//
2069-
// Tag this artificial calc point with 'placeholder: true',
2070-
// to make it easier to skip over them in during the plot and hover step.
20712078
if(!Array.isArray(cd) || !cd[0]) {
2072-
cd = [{x: false, y: false, placeholder: true}];
2079+
cd = [{x: BADNUM, y: BADNUM}];
20732080
}
20742081

20752082
// add the trace-wide properties to the first point,

src/traces/scatter/plot.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionOpts, makeOnCo
5151
// Sort the traces, once created, so that the ordering is preserved even when traces
5252
// are shown and hidden. This is needed since we're not just wiping everything out
5353
// and recreating on every update.
54-
for(i = 0, uids = []; i < cdscatter.length; i++) {
55-
uids[i] = cdscatter[i][0].trace.uid;
54+
for(i = 0, uids = {}; i < cdscatter.length; i++) {
55+
uids[cdscatter[i][0].trace.uid] = i;
5656
}
5757

5858
scatterlayer.selectAll('g.trace').sort(function(a, b) {
59-
var idx1 = uids.indexOf(a[0].trace.uid);
60-
var idx2 = uids.indexOf(b[0].trace.uid);
59+
var idx1 = uids[a[0].trace.uid];
60+
var idx2 = uids[b[0].trace.uid];
6161
return idx1 > idx2 ? 1 : -1;
6262
});
6363

0 commit comments

Comments
 (0)