Skip to content

Commit 8ef1445

Browse files
authored
Merge pull request #3272 from plotly/snapshot-clippath-base
Do not add <base> href to SVG clip paths during toImage
2 parents bd7fefa + 76f5ad0 commit 8ef1445

File tree

25 files changed

+131
-82
lines changed

25 files changed

+131
-82
lines changed

src/components/annotations/draw.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -411,14 +411,14 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
411411
x: borderfull + xShift - 1,
412412
y: borderfull + yShift
413413
})
414-
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null);
414+
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
415415
}
416416
else {
417417
var texty = borderfull + yShift - anntextBB.top;
418418
var textx = borderfull + xShift - anntextBB.left;
419419

420420
annText.call(svgTextUtils.positionText, textx, texty)
421-
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null);
421+
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
422422
}
423423

424424
annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,

src/components/drawing/index.js

+15-19
Original file line numberDiff line numberDiff line change
@@ -1000,32 +1000,28 @@ function nodeHash(node) {
10001000
node.getAttribute('style');
10011001
}
10021002

1003-
/*
1004-
* make a robust clipPath url from a local id
1005-
* note! We'd better not be exporting from a page
1006-
* with a <base> or the svg will not be portable!
1003+
/**
1004+
* Set clipPath URL in a way that work for all situations.
1005+
*
1006+
* In details, graphs on pages with <base> HTML tags need to prepend
1007+
* the clip path ids with the page's base url EXCEPT during toImage exports.
1008+
*
1009+
* @param {d3 selection} s : node to add clip-path attribute
1010+
* @param {string} localId : local clip-path (w/o base url) id
1011+
* @param {DOM element || object} gd
1012+
* - context._baseUrl {string}
1013+
* - context._exportedPlot {boolean}
10071014
*/
1008-
drawing.setClipUrl = function(s, localId) {
1015+
drawing.setClipUrl = function(s, localId, gd) {
10091016
if(!localId) {
10101017
s.attr('clip-path', null);
10111018
return;
10121019
}
10131020

1014-
if(drawing.baseUrl === undefined) {
1015-
var base = d3.select('base');
1016-
1017-
// Stash base url once and for all!
1018-
// We may have to stash this elsewhere when
1019-
// we'll try to support for child windows
1020-
// more info -> https://github.com/plotly/plotly.js/issues/702
1021-
if(base.size() && base.attr('href')) {
1022-
drawing.baseUrl = window.location.href.split('#')[0];
1023-
} else {
1024-
drawing.baseUrl = '';
1025-
}
1026-
}
1021+
var context = gd._context;
1022+
var baseUrl = context._exportedPlot ? '' : (context._baseUrl || '');
10271023

1028-
s.attr('clip-path', 'url(' + drawing.baseUrl + '#' + localId + ')');
1024+
s.attr('clip-path', 'url(' + baseUrl + '#' + localId + ')');
10291025
};
10301026

10311027
drawing.getTranslate = function(element) {

src/components/errorbars/plot.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var isNumeric = require('fast-isnumeric');
1515
var Drawing = require('../drawing');
1616
var subTypes = require('../../traces/scatter/subtypes');
1717

18-
module.exports = function plot(traces, plotinfo, transitionOpts) {
18+
module.exports = function plot(gd, traces, plotinfo, transitionOpts) {
1919
var isNew;
2020

2121
var xa = plotinfo.xaxis;
@@ -66,7 +66,7 @@ module.exports = function plot(traces, plotinfo, transitionOpts) {
6666
.style('opacity', 1);
6767
}
6868

69-
Drawing.setClipUrl(errorbars, plotinfo.layerClipId);
69+
Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd);
7070

7171
errorbars.each(function(d) {
7272
var errorbar = d3.select(this);

src/components/images/draw.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,10 @@ module.exports = function draw(gd) {
168168
yId = ya ? ya._id : '',
169169
clipAxes = xId + yId;
170170

171-
thisImage.call(Drawing.setClipUrl, clipAxes ?
172-
('clip' + fullLayout._uid + clipAxes) :
173-
null
171+
Drawing.setClipUrl(
172+
thisImage,
173+
clipAxes ? ('clip' + fullLayout._uid + clipAxes) : null,
174+
gd
174175
);
175176
}
176177

src/components/legend/draw.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ module.exports = function draw(gd) {
224224
y: opts.borderwidth
225225
});
226226

227-
Drawing.setClipUrl(scrollBox, clipId);
227+
Drawing.setClipUrl(scrollBox, clipId, gd);
228228

229229
Drawing.setRect(scrollBar, 0, 0, 0, 0);
230230
delete opts._scrollY;
@@ -262,7 +262,7 @@ module.exports = function draw(gd) {
262262
y: opts.borderwidth + scrollBoxY
263263
});
264264

265-
Drawing.setClipUrl(scrollBox, clipId);
265+
Drawing.setClipUrl(scrollBox, clipId, gd);
266266

267267
scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
268268

src/components/rangeslider/draw.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
401401

402402
rangePlots.enter().append('g')
403403
.attr('class', function(id) { return constants.rangePlotClassName + ' ' + id; })
404-
.call(Drawing.setClipUrl, opts._clipId);
404+
.call(Drawing.setClipUrl, opts._clipId, gd);
405405

406406
rangePlots.order();
407407

src/components/shapes/draw.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ function setClipPath(shapePath, gd, shapeOptions) {
120120
// spans two subplots. See https://github.com/plotly/plotly.js/issues/1452
121121
var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, '');
122122

123-
shapePath.call(Drawing.setClipUrl, clipAxes ?
124-
('clip' + gd._fullLayout._uid + clipAxes) :
125-
null
123+
Drawing.setClipUrl(
124+
shapePath,
125+
clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
126+
gd
126127
);
127128
}
128129

@@ -493,9 +494,10 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer) {
493494
if(xref !== 'paper' && !xa.autorange) clipAxes += xref;
494495
if(yref !== 'paper' && !ya.autorange) clipAxes += yref;
495496

496-
shapePath.call(Drawing.setClipUrl, clipAxes ?
497-
'clip' + gd._fullLayout._uid + clipAxes :
498-
null
497+
Drawing.setClipUrl(
498+
shapePath,
499+
clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
500+
gd
499501
);
500502
}
501503
}

src/components/updatemenus/scrollbox.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ ScrollBox.prototype.enable = function enable(position, translateX, translateY) {
254254
height: Math.ceil(clipB) - Math.floor(clipT)
255255
});
256256

257-
this.container.call(Drawing.setClipUrl, clipId);
257+
this.container.call(Drawing.setClipUrl, clipId, this.gd);
258258

259259
this.bg.attr({
260260
x: l,

src/plot_api/plot_api.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,6 @@ exports.plot = function(gd, data, layout, config) {
110110
// so we can share cached text across tabs
111111
Drawing.makeTester();
112112

113-
// clear stashed base url
114-
delete Drawing.baseUrl;
115-
116113
// collect promises for any async actions during plotting
117114
// any part of the plotting code can push to gd._promises, then
118115
// before we move to the next step, we check that they're all
@@ -419,7 +416,16 @@ function opaqueSetBackground(gd, bgColor) {
419416
}
420417

421418
function setPlotContext(gd, config) {
422-
if(!gd._context) gd._context = Lib.extendDeep({}, defaultConfig);
419+
if(!gd._context) {
420+
gd._context = Lib.extendDeep({}, defaultConfig);
421+
422+
// stash <base> href, used to make robust clipPath URLs
423+
var base = d3.select('base');
424+
gd._context._baseUrl = base.size() && base.attr('href') ?
425+
window.location.href.split('#')[0] :
426+
'';
427+
}
428+
423429
var context = gd._context;
424430

425431
var i, keys, key;
@@ -465,6 +471,9 @@ function setPlotContext(gd, config) {
465471
}
466472
}
467473
}
474+
475+
// not part of the user-facing config options
476+
context._exportedPlot = config._exportedPlot;
468477
}
469478

470479
// staticPlot forces a bunch of others:

src/plot_api/subroutines.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ function lsInner(gd) {
224224
layerClipId = null;
225225
}
226226

227-
Drawing.setClipUrl(plotinfo.plot, plotClipId);
227+
Drawing.setClipUrl(plotinfo.plot, plotClipId, gd);
228228

229229
// stash layer clipId value (null or same as clipId)
230230
// to DRY up Drawing.setClipUrl calls on trace-module and trace layers

src/plot_api/to_image.js

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ function toImage(gd, opts) {
137137

138138
// extend config for static plot
139139
var configImage = Lib.extendFlat({}, config, {
140+
_exportedPlot: true,
140141
staticPlot: true,
141142
setBackground: setBackground
142143
});

src/plots/cartesian/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback
262262

263263
// layers that allow `cliponaxis: false`
264264
if(className !== 'scatterlayer' && className !== 'barlayer') {
265-
Drawing.setClipUrl(sel, plotinfo.layerClipId);
265+
Drawing.setClipUrl(sel, plotinfo.layerClipId, gd);
266266
}
267267
});
268268

src/plots/geo/geo.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,8 @@ proto.updateFx = function(fullLayout, geoLayout) {
470470

471471
proto.makeFramework = function() {
472472
var _this = this;
473-
var fullLayout = _this.graphDiv._fullLayout;
473+
var gd = _this.graphDiv;
474+
var fullLayout = gd._fullLayout;
474475
var clipId = 'clip' + fullLayout._uid + _this.id;
475476

476477
_this.clipDef = fullLayout._clips.append('clipPath')
@@ -480,7 +481,7 @@ proto.makeFramework = function() {
480481

481482
_this.framework = d3.select(_this.container).append('g')
482483
.attr('class', 'geo ' + _this.id)
483-
.call(Drawing.setClipUrl, clipId);
484+
.call(Drawing.setClipUrl, clipId, gd);
484485

485486
// sane lonlat to px
486487
_this.project = function(v) {

src/plots/polar/polar.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ proto.updateLayout = function(fullLayout, polarLayout) {
290290

291291
layers.frontplot
292292
.attr('transform', strTranslate(xOffset2, yOffset2))
293-
.call(Drawing.setClipUrl, _this._hasClipOnAxisFalse ? null : _this.clipIds.forTraces);
293+
.call(Drawing.setClipUrl, _this._hasClipOnAxisFalse ? null : _this.clipIds.forTraces, _this.gd);
294294

295295
layers.bg
296296
.attr('d', dPath)

src/plots/ternary/ternary.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ proto.plot = function(ternaryCalcData, fullLayout) {
7777

7878
proto.makeFramework = function(fullLayout) {
7979
var _this = this;
80+
var gd = _this.graphDiv;
8081
var ternaryLayout = fullLayout[_this.id];
8182

8283
var clipId = _this.clipId = 'clip' + _this.layoutId + _this.id;
@@ -96,8 +97,8 @@ proto.makeFramework = function(fullLayout) {
9697
_this.plotContainer = Lib.ensureSingle(_this.container, 'g', _this.id);
9798
_this.updateLayers(ternaryLayout);
9899

99-
Drawing.setClipUrl(_this.layers.backplot, clipId);
100-
Drawing.setClipUrl(_this.layers.grids, clipId);
100+
Drawing.setClipUrl(_this.layers.backplot, clipId, gd);
101+
Drawing.setClipUrl(_this.layers.grids, clipId, gd);
101102
};
102103

103104
proto.updateLayers = function(ternaryLayout) {
@@ -345,7 +346,8 @@ proto.adjustLayout = function(ternaryLayout, graphSize) {
345346

346347
Drawing.setClipUrl(
347348
_this.layers.frontplot,
348-
_this._hasClipOnAxisFalse ? null : _this.clipId
349+
_this._hasClipOnAxisFalse ? null : _this.clipId,
350+
_this.graphDiv
349351
);
350352
};
351353

src/traces/bar/plot.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
124124
.style('vector-effect', 'non-scaling-stroke')
125125
.attr('d',
126126
'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
127-
.call(Drawing.setClipUrl, plotinfo.layerClipId);
127+
.call(Drawing.setClipUrl, plotinfo.layerClipId, gd);
128128

129129
appendBarText(gd, bar, cd, i, x0, x1, y0, y1);
130130

@@ -136,11 +136,11 @@ module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
136136
// lastly, clip points groups of `cliponaxis !== false` traces
137137
// on `plotinfo._hasClipOnAxisFalse === true` subplots
138138
var hasClipOnAxisFalse = cd0.trace.cliponaxis === false;
139-
Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId);
139+
Drawing.setClipUrl(plotGroup, hasClipOnAxisFalse ? null : plotinfo.layerClipId, gd);
140140
});
141141

142142
// error bars are on the top
143-
Registry.getComponentMethod('errorbars', 'plot')(bartraces, plotinfo);
143+
Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo);
144144
};
145145

146146
function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {

src/traces/barpolar/plot.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ module.exports = function plot(gd, subplot, cdbar) {
6969
});
7070

7171
// clip plotGroup, when trace layer isn't clipped
72-
Drawing.setClipUrl(plotGroup, subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null);
72+
Drawing.setClipUrl(
73+
plotGroup,
74+
subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null,
75+
gd
76+
);
7377
});
7478
};
7579

src/traces/contour/plot.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ var costConstants = constants.LABELOPTIMIZER;
2929
exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) {
3030
var xa = plotinfo.xaxis;
3131
var ya = plotinfo.yaxis;
32-
var fullLayout = gd._fullLayout;
3332

3433
Lib.makeTraceGroups(contourLayer, cdcontours, 'contour').each(function(cd) {
3534
var plotGroup = d3.select(this);
@@ -78,7 +77,7 @@ exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) {
7877
makeBackground(plotGroup, perimeter, contours);
7978
makeFills(plotGroup, fillPathinfo, perimeter, contours);
8079
makeLinesAndLabels(plotGroup, pathinfo, gd, cd0, contours, perimeter);
81-
clipGaps(plotGroup, plotinfo, fullLayout._clips, cd0, perimeter);
80+
clipGaps(plotGroup, plotinfo, gd, cd0, perimeter);
8281
});
8382
};
8483

@@ -230,8 +229,7 @@ function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours, perimeter) {
230229
// In this case we'll remove the lines after making the labels.
231230
var linegroup = exports.createLines(lineContainer, showLines || showLabels, pathinfo);
232231

233-
var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels,
234-
gd._fullLayout._clips, cd0.trace.uid);
232+
var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid);
235233

236234
var labelGroup = plotgroup.selectAll('g.contourlabels')
237235
.data(showLabels ? [0] : []);
@@ -373,7 +371,8 @@ exports.createLines = function(lineContainer, makeLines, pathinfo) {
373371
return linegroup;
374372
};
375373

376-
exports.createLineClip = function(lineContainer, clipLinesForLabels, clips, uid) {
374+
exports.createLineClip = function(lineContainer, clipLinesForLabels, gd, uid) {
375+
var clips = gd._fullLayout._clips;
377376
var clipId = clipLinesForLabels ? ('clipline' + uid) : null;
378377

379378
var lineClip = clips.selectAll('#' + clipId)
@@ -384,7 +383,7 @@ exports.createLineClip = function(lineContainer, clipLinesForLabels, clips, uid)
384383
.classed('contourlineclip', true)
385384
.attr('id', clipId);
386385

387-
Drawing.setClipUrl(lineContainer, clipId);
386+
Drawing.setClipUrl(lineContainer, clipId, gd);
388387

389388
return lineClip;
390389
};
@@ -615,7 +614,8 @@ exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPath
615614
}
616615
};
617616

618-
function clipGaps(plotGroup, plotinfo, clips, cd0, perimeter) {
617+
function clipGaps(plotGroup, plotinfo, gd, cd0, perimeter) {
618+
var clips = gd._fullLayout._clips;
619619
var clipId = 'clip' + cd0.trace.uid;
620620

621621
var clipPath = clips.selectAll('#' + clipId)
@@ -654,7 +654,7 @@ function clipGaps(plotGroup, plotinfo, clips, cd0, perimeter) {
654654
}
655655
else clipId = null;
656656

657-
plotGroup.call(Drawing.setClipUrl, clipId);
657+
Drawing.setClipUrl(plotGroup, clipId, gd);
658658
}
659659

660660
function makeClipMask(cd0) {

0 commit comments

Comments
 (0)