Skip to content
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

Do not add <base> href to SVG clip paths during toImage #3272

Merged
merged 8 commits into from
Nov 26, 2018
4 changes: 2 additions & 2 deletions src/components/annotations/draw.js
Original file line number Diff line number Diff line change
@@ -411,14 +411,14 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
x: borderfull + xShift - 1,
y: borderfull + yShift
})
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null);
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
}
else {
var texty = borderfull + yShift - anntextBB.top;
var textx = borderfull + xShift - anntextBB.left;

annText.call(svgTextUtils.positionText, textx, texty)
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null);
.call(Drawing.setClipUrl, isSizeConstrained ? annClipID : null, gd);
}

annTextClip.select('rect').call(Drawing.setRect, borderfull, borderfull,
34 changes: 15 additions & 19 deletions src/components/drawing/index.js
Original file line number Diff line number Diff line change
@@ -1000,32 +1000,28 @@ function nodeHash(node) {
node.getAttribute('style');
}

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

if(drawing.baseUrl === undefined) {
var base = d3.select('base');

// Stash base url once and for all!
// We may have to stash this elsewhere when
// we'll try to support for child windows
// more info -> https://github.com/plotly/plotly.js/issues/702
if(base.size() && base.attr('href')) {
drawing.baseUrl = window.location.href.split('#')[0];
} else {
drawing.baseUrl = '';
}
}
var context = gd._context;
var baseUrl = context._exportedPlot ? '' : (context._baseUrl || '');

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

drawing.getTranslate = function(element) {
4 changes: 2 additions & 2 deletions src/components/errorbars/plot.js
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ var isNumeric = require('fast-isnumeric');
var Drawing = require('../drawing');
var subTypes = require('../../traces/scatter/subtypes');

module.exports = function plot(traces, plotinfo, transitionOpts) {
module.exports = function plot(gd, traces, plotinfo, transitionOpts) {
var isNew;

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

Drawing.setClipUrl(errorbars, plotinfo.layerClipId);
Drawing.setClipUrl(errorbars, plotinfo.layerClipId, gd);

errorbars.each(function(d) {
var errorbar = d3.select(this);
7 changes: 4 additions & 3 deletions src/components/images/draw.js
Original file line number Diff line number Diff line change
@@ -168,9 +168,10 @@ module.exports = function draw(gd) {
yId = ya ? ya._id : '',
clipAxes = xId + yId;

thisImage.call(Drawing.setClipUrl, clipAxes ?
('clip' + fullLayout._uid + clipAxes) :
null
Drawing.setClipUrl(
thisImage,
clipAxes ? ('clip' + fullLayout._uid + clipAxes) : null,
gd
);
}

4 changes: 2 additions & 2 deletions src/components/legend/draw.js
Original file line number Diff line number Diff line change
@@ -224,7 +224,7 @@ module.exports = function draw(gd) {
y: opts.borderwidth
});

Drawing.setClipUrl(scrollBox, clipId);
Drawing.setClipUrl(scrollBox, clipId, gd);

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

Drawing.setClipUrl(scrollBox, clipId);
Drawing.setClipUrl(scrollBox, clipId, gd);

scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);

2 changes: 1 addition & 1 deletion src/components/rangeslider/draw.js
Original file line number Diff line number Diff line change
@@ -401,7 +401,7 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) {

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

rangePlots.order();

14 changes: 8 additions & 6 deletions src/components/shapes/draw.js
Original file line number Diff line number Diff line change
@@ -120,9 +120,10 @@ function setClipPath(shapePath, gd, shapeOptions) {
// spans two subplots. See https://github.com/plotly/plotly.js/issues/1452
var clipAxes = (shapeOptions.xref + shapeOptions.yref).replace(/paper/g, '');

shapePath.call(Drawing.setClipUrl, clipAxes ?
('clip' + gd._fullLayout._uid + clipAxes) :
null
Drawing.setClipUrl(
shapePath,
clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
gd
);
}

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

shapePath.call(Drawing.setClipUrl, clipAxes ?
'clip' + gd._fullLayout._uid + clipAxes :
null
Drawing.setClipUrl(
shapePath,
clipAxes ? 'clip' + gd._fullLayout._uid + clipAxes : null,
gd
);
}
}
2 changes: 1 addition & 1 deletion src/components/updatemenus/scrollbox.js
Original file line number Diff line number Diff line change
@@ -254,7 +254,7 @@ ScrollBox.prototype.enable = function enable(position, translateX, translateY) {
height: Math.ceil(clipB) - Math.floor(clipT)
});

this.container.call(Drawing.setClipUrl, clipId);
this.container.call(Drawing.setClipUrl, clipId, this.gd);

this.bg.attr({
x: l,
17 changes: 13 additions & 4 deletions src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
@@ -110,9 +110,6 @@ exports.plot = function(gd, data, layout, config) {
// so we can share cached text across tabs
Drawing.makeTester();

// clear stashed base url
delete Drawing.baseUrl;

// collect promises for any async actions during plotting
// any part of the plotting code can push to gd._promises, then
// before we move to the next step, we check that they're all
@@ -419,7 +416,16 @@ function opaqueSetBackground(gd, bgColor) {
}

function setPlotContext(gd, config) {
if(!gd._context) gd._context = Lib.extendDeep({}, defaultConfig);
if(!gd._context) {
gd._context = Lib.extendDeep({}, defaultConfig);

// stash <base> href, used to make robust clipPath URLs
var base = d3.select('base');
gd._context._baseUrl = base.size() && base.attr('href') ?
window.location.href.split('#')[0] :
'';
}

var context = gd._context;

var i, keys, key;
@@ -465,6 +471,9 @@ function setPlotContext(gd, config) {
}
}
}

// not part of the user-facing config options
context._exportedPlot = config._exportedPlot;
}

// staticPlot forces a bunch of others:
2 changes: 1 addition & 1 deletion src/plot_api/subroutines.js
Original file line number Diff line number Diff line change
@@ -224,7 +224,7 @@ function lsInner(gd) {
layerClipId = null;
}

Drawing.setClipUrl(plotinfo.plot, plotClipId);
Drawing.setClipUrl(plotinfo.plot, plotClipId, gd);

// stash layer clipId value (null or same as clipId)
// to DRY up Drawing.setClipUrl calls on trace-module and trace layers
1 change: 1 addition & 0 deletions src/plot_api/to_image.js
Original file line number Diff line number Diff line change
@@ -137,6 +137,7 @@ function toImage(gd, opts) {

// extend config for static plot
var configImage = Lib.extendFlat({}, config, {
_exportedPlot: true,
staticPlot: true,
setBackground: setBackground
});
2 changes: 1 addition & 1 deletion src/plots/cartesian/index.js
Original file line number Diff line number Diff line change
@@ -262,7 +262,7 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback

// layers that allow `cliponaxis: false`
if(className !== 'scatterlayer' && className !== 'barlayer') {
Drawing.setClipUrl(sel, plotinfo.layerClipId);
Drawing.setClipUrl(sel, plotinfo.layerClipId, gd);
}
});

5 changes: 3 additions & 2 deletions src/plots/geo/geo.js
Original file line number Diff line number Diff line change
@@ -470,7 +470,8 @@ proto.updateFx = function(fullLayout, geoLayout) {

proto.makeFramework = function() {
var _this = this;
var fullLayout = _this.graphDiv._fullLayout;
var gd = _this.graphDiv;
var fullLayout = gd._fullLayout;
var clipId = 'clip' + fullLayout._uid + _this.id;

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

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

// sane lonlat to px
_this.project = function(v) {
2 changes: 1 addition & 1 deletion src/plots/polar/polar.js
Original file line number Diff line number Diff line change
@@ -290,7 +290,7 @@ proto.updateLayout = function(fullLayout, polarLayout) {

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

layers.bg
.attr('d', dPath)
8 changes: 5 additions & 3 deletions src/plots/ternary/ternary.js
Original file line number Diff line number Diff line change
@@ -77,6 +77,7 @@ proto.plot = function(ternaryCalcData, fullLayout) {

proto.makeFramework = function(fullLayout) {
var _this = this;
var gd = _this.graphDiv;
var ternaryLayout = fullLayout[_this.id];

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

Drawing.setClipUrl(_this.layers.backplot, clipId);
Drawing.setClipUrl(_this.layers.grids, clipId);
Drawing.setClipUrl(_this.layers.backplot, clipId, gd);
Drawing.setClipUrl(_this.layers.grids, clipId, gd);
};

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

Drawing.setClipUrl(
_this.layers.frontplot,
_this._hasClipOnAxisFalse ? null : _this.clipId
_this._hasClipOnAxisFalse ? null : _this.clipId,
_this.graphDiv
);
};

6 changes: 3 additions & 3 deletions src/traces/bar/plot.js
Original file line number Diff line number Diff line change
@@ -124,7 +124,7 @@ module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
.style('vector-effect', 'non-scaling-stroke')
.attr('d',
'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
.call(Drawing.setClipUrl, plotinfo.layerClipId);
.call(Drawing.setClipUrl, plotinfo.layerClipId, gd);

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

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

// error bars are on the top
Registry.getComponentMethod('errorbars', 'plot')(bartraces, plotinfo);
Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo);
};

function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
6 changes: 5 additions & 1 deletion src/traces/barpolar/plot.js
Original file line number Diff line number Diff line change
@@ -69,7 +69,11 @@ module.exports = function plot(gd, subplot, cdbar) {
});

// clip plotGroup, when trace layer isn't clipped
Drawing.setClipUrl(plotGroup, subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null);
Drawing.setClipUrl(
plotGroup,
subplot._hasClipOnAxisFalse ? subplot.clipIds.forTraces : null,
gd
);
});
};

16 changes: 8 additions & 8 deletions src/traces/contour/plot.js
Original file line number Diff line number Diff line change
@@ -29,7 +29,6 @@ var costConstants = constants.LABELOPTIMIZER;
exports.plot = function plot(gd, plotinfo, cdcontours, contourLayer) {
var xa = plotinfo.xaxis;
var ya = plotinfo.yaxis;
var fullLayout = gd._fullLayout;

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

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

var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels,
gd._fullLayout._clips, cd0.trace.uid);
var lineClip = exports.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid);

var labelGroup = plotgroup.selectAll('g.contourlabels')
.data(showLabels ? [0] : []);
@@ -353,7 +351,8 @@ exports.createLines = function(lineContainer, makeLines, pathinfo) {
return linegroup;
};

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

var lineClip = clips.selectAll('#' + clipId)
@@ -364,7 +363,7 @@ exports.createLineClip = function(lineContainer, clipLinesForLabels, clips, uid)
.classed('contourlineclip', true)
.attr('id', clipId);

Drawing.setClipUrl(lineContainer, clipId);
Drawing.setClipUrl(lineContainer, clipId, gd);

return lineClip;
};
@@ -595,7 +594,8 @@ exports.drawLabels = function(labelGroup, labelData, gd, lineClip, labelClipPath
}
};

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

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

plotGroup.call(Drawing.setClipUrl, clipId);
Drawing.setClipUrl(plotGroup, clipId, gd);
}

function makeClipMask(cd0) {
5 changes: 2 additions & 3 deletions src/traces/contourcarpet/plot.js
Original file line number Diff line number Diff line change
@@ -113,7 +113,7 @@ module.exports = function plot(gd, plotinfo, cdcontours, contourcarpetLayer) {
makeLinesAndLabels(plotGroup, pathinfo, gd, cd0, contours, plotinfo, carpet);

// Clip the boundary of the plot
Drawing.setClipUrl(plotGroup, carpet._clipPathId);
Drawing.setClipUrl(plotGroup, carpet._clipPathId, gd);
});
};

@@ -129,8 +129,7 @@ function makeLinesAndLabels(plotgroup, pathinfo, gd, cd0, contours, plotinfo, ca
// In this case we'll remove the lines after making the labels.
var linegroup = contourPlot.createLines(lineContainer, showLines || showLabels, pathinfo);

var lineClip = contourPlot.createLineClip(lineContainer, clipLinesForLabels,
gd._fullLayout._defs, cd0.trace.uid);
var lineClip = contourPlot.createLineClip(lineContainer, clipLinesForLabels, gd, cd0.trace.uid);

var labelGroup = plotgroup.selectAll('g.contourlabels')
.data(showLabels ? [0] : []);
10 changes: 5 additions & 5 deletions src/traces/scatter/plot.js
Original file line number Diff line number Diff line change
@@ -88,7 +88,7 @@ module.exports = function plot(gd, plotinfo, cdscatter, scatterLayer, transition
function createFills(gd, traceJoin, plotinfo) {
traceJoin.each(function(d) {
var fills = ensureSingle(d3.select(this), 'g', 'fills');
Drawing.setClipUrl(fills, plotinfo.layerClipId);
Drawing.setClipUrl(fills, plotinfo.layerClipId, gd);

var trace = d[0].trace;

@@ -140,7 +140,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
var text = ensureSingle(tr, 'g', 'text');

// error bars are at the bottom
Registry.getComponentMethod('errorbars', 'plot')(errorBarGroup, plotinfo, transitionOpts);
Registry.getComponentMethod('errorbars', 'plot')(gd, errorBarGroup, plotinfo, transitionOpts);

if(trace.visible !== true) return;

@@ -295,7 +295,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
.call(Drawing.lineGroupStyle)
.each(makeUpdate(true));

Drawing.setClipUrl(lineJoin, plotinfo.layerClipId);
Drawing.setClipUrl(lineJoin, plotinfo.layerClipId, gd);

function clearFill(selection) {
transition(selection).attr('d', 'M0,0Z');
@@ -523,8 +523,8 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
// on `plotinfo._hasClipOnAxisFalse === true` subplots
var hasClipOnAxisFalse = trace.cliponaxis === false;
var clipUrl = hasClipOnAxisFalse ? null : plotinfo.layerClipId;
Drawing.setClipUrl(points, clipUrl);
Drawing.setClipUrl(text, clipUrl);
Drawing.setClipUrl(points, clipUrl, gd);
Drawing.setClipUrl(text, clipUrl, gd);
}

function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) {
2 changes: 1 addition & 1 deletion src/traces/scattercarpet/plot.js
Original file line number Diff line number Diff line change
@@ -37,6 +37,6 @@ module.exports = function plot(gd, plotinfoproxy, data, layer) {
// separately to all scattercarpet traces, but that would require
// lots of reorganization of scatter traces that is otherwise not
// necessary. That makes this a potential optimization.
Drawing.setClipUrl(node, carpet._clipPathId);
Drawing.setClipUrl(node, carpet._clipPathId, gd);
}
};
9 changes: 6 additions & 3 deletions src/traces/table/plot.js
Original file line number Diff line number Diff line change
@@ -83,8 +83,9 @@ module.exports = function plot(gd, wrappedTraceHolders) {
.attr('width', function(d) {return d.width;})
.attr('height', function(d) {return d.height;});

tableControlView
.each(function(d) {Drawing.setClipUrl(d3.select(this), scrollAreaBottomClipKey(gd, d));});
tableControlView.each(function(d) {
Drawing.setClipUrl(d3.select(this), scrollAreaBottomClipKey(gd, d), gd);
});

var yColumn = tableControlView.selectAll('.' + c.cn.yColumn)
.data(function(vm) {return vm.columns;}, gup.keyFun);
@@ -137,7 +138,9 @@ module.exports = function plot(gd, wrappedTraceHolders) {
})
);

yColumn.each(function(d) {Drawing.setClipUrl(d3.select(this), columnBoundaryClipKey(gd, d));});
yColumn.each(function(d) {
Drawing.setClipUrl(d3.select(this), columnBoundaryClipKey(gd, d), gd);
});

var columnBlock = yColumn.selectAll('.' + c.cn.columnBlock)
.data(splitData.splitToPanels, gup.keyFun);
13 changes: 6 additions & 7 deletions test/jasmine/tests/drawing_test.js
Original file line number Diff line number Diff line change
@@ -19,15 +19,12 @@ describe('Drawing', function() {
afterEach(function() {
this.svg.remove();
this.g.remove();

// unstash base url from Drawing module object
delete Drawing.baseUrl;
});

it('should set the clip-path attribute', function() {
expect(this.g.attr('clip-path')).toBe(null);

Drawing.setClipUrl(this.g, 'id1');
Drawing.setClipUrl(this.g, 'id1', {_context: {}});

expect(this.g.attr('clip-path')).toEqual('url(#id1)');
});
@@ -49,7 +46,7 @@ describe('Drawing', function() {
// grab window URL
var href = window.location.href;

Drawing.setClipUrl(this.g, 'id3');
Drawing.setClipUrl(this.g, 'id3', {_context: {_baseUrl: href}});

expect(this.g.attr('clip-path'))
.toEqual('url(' + href + '#id3)');
@@ -63,10 +60,12 @@ describe('Drawing', function() {
.attr('href', 'https://plot.ly/#hash');

window.location.hash = 'hash';
var href = window.location.href;
var href2 = href.split('#')[0];

Drawing.setClipUrl(this.g, 'id4');
Drawing.setClipUrl(this.g, 'id4', {_context: {_baseUrl: href2}});

var expected = 'url(' + window.location.href.split('#')[0] + '#id4)';
var expected = 'url(' + href2 + '#id4)';

expect(this.g.attr('clip-path')).toEqual(expected);

3 changes: 0 additions & 3 deletions test/jasmine/tests/plot_interact_test.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ var d3 = require('d3');

var Plotly = require('@lib/index');
var Lib = require('@src/lib');
var Drawing = require('@src/components/drawing');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
@@ -552,7 +551,6 @@ describe('plot svg clip paths', function() {
d3.selectAll('[clip-path]').each(function() {
var cp = d3.select(this).attr('clip-path');

expect(Drawing.baseUrl).toBe('');
expect(cp.substring(0, 5)).toEqual('url(#');
expect(cp.substring(cp.length - 1)).toEqual(')');
});
@@ -578,7 +576,6 @@ describe('plot svg clip paths', function() {
d3.selectAll('[clip-path]').each(function() {
var cp = d3.select(this).attr('clip-path');

expect(Drawing.baseUrl).toBe(href);
expect(cp.substring(0, 5 + href.length)).toEqual('url(' + href + '#');
expect(cp.substring(cp.length - 1)).toEqual(')');
});
35 changes: 35 additions & 0 deletions test/jasmine/tests/toimage_test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var Plotly = require('@lib');
var Lib = require('@src/lib');

var d3 = require('d3');
var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var failTest = require('../assets/fail_test');
@@ -210,4 +211,38 @@ describe('Plotly.toImage', function() {
.catch(failTest)
.then(done);
});

it('should work on pages with <base>', function(done) {
var parser = new DOMParser();

var base = d3.select('body')
.append('base')
.attr('href', 'https://plot.ly');

Plotly.plot(gd, [{ y: [1, 2, 1] }])
.then(function() {
return Plotly.toImage(gd, {format: 'svg', imageDataOnly: true});
})
.then(function(svg) {
var svgDOM = parser.parseFromString(svg, 'image/svg+xml');
var gSubplot = svgDOM.getElementsByClassName('plot')[0];
var clipPath = gSubplot.getAttribute('clip-path');
var len = clipPath.length;

var head = clipPath.substr(0, 4);
var tail = clipPath.substr(len - 7, len);
expect(head).toBe('url(', 'subplot clipPath head');
expect(tail).toBe('xyplot)', 'subplot clipPath tail');

var middle = clipPath.substr(4, 10);
expect(middle.length).toBe(10, 'subplot clipPath uid length');
expect(middle.indexOf('http://')).toBe(-1, 'no <base> URL in subplot clipPath!');
expect(middle.indexOf('https://')).toBe(-1, 'no <base> URL in subplot clipPath!');
})
.catch(failTest)
.then(function() {
base.remove();
done();
});
});
});