function Geo(options) {
this.id = options.id;
this.graphDiv = options.graphDiv;
this.container = options.container;
this.topojsonURL = options.topojsonURL;
this.topojsonName = null;
this.topojson = null;
this.projectionType = null;
this.projection = null;
this.clipAngle = null;
this.setScale = null;
this.path = null;
this.zoom = null;
this.zoomReset = null;
this.makeFramework();
this.traceHash = {};
}n/a
function ScrollBox(gd, container, id) {
this.gd = gd;
this.container = container;
this.id = id;
// See ScrollBox.prototype.enable for further definition
this.position = null; // scrollbox position
this.translateX = null; // scrollbox horizontal translation
this.translateY = null; // scrollbox vertical translation
this.hbar = null; // horizontal scrollbar D3 selection
this.vbar = null; // vertical scrollbar D3 selection
// <rect> element to capture pointer events
this.bg = this.container.selectAll('rect.scrollbox-bg').data([0]);
this.bg.exit()
.on('.drag', null)
.on('wheel', null)
.remove();
this.bg.enter().append('rect')
.classed('scrollbox-bg', true)
.style('pointer-events', 'all')
.attr({
opacity: 0,
x: 0,
y: 0,
width: 0,
height: 0
});
}n/a
function Sieve(traces, separateNegativeValues, dontMergeOverlappingData) {
this.traces = traces;
this.separateNegativeValues = separateNegativeValues;
this.dontMergeOverlappingData = dontMergeOverlappingData;
var positions = [];
for(var i = 0; i < traces.length; i++) {
var trace = traces[i];
for(var j = 0; j < trace.length; j++) {
var bar = trace[j];
if(bar.p !== BADNUM) positions.push(bar.p);
}
}
this.positions = positions;
var dv = Lib.distinctVals(this.positions);
this.distinctPositions = dv.vals;
this.minDiff = dv.minDiff;
this.binWidth = this.minDiff;
this.bins = {};
}n/a
function Ternary(options, fullLayout) {
this.id = options.id;
this.graphDiv = options.graphDiv;
this.init(fullLayout);
this.makeFramework();
}n/a
function isBottomAnchor(opts) {
return (
opts.yanchor === 'bottom' ||
(opts.yanchor === 'auto' && opts.y <= 1 / 3)
);
}n/a
function isCenterAnchor(opts) {
return (
opts.xanchor === 'center' ||
(opts.xanchor === 'auto' && opts.x > 1 / 3 && opts.x < 2 / 3)
);
}n/a
function isMiddleAnchor(opts) {
return (
opts.yanchor === 'middle' ||
(opts.yanchor === 'auto' && opts.y > 1 / 3 && opts.y < 2 / 3)
);
}n/a
function isRightAnchor(opts) {
return (
opts.xanchor === 'right' ||
(opts.xanchor === 'auto' && opts.x >= 2 / 3)
);
}n/a
autoBin = function (data, ax, nbins, is2d, calendar) {
var dataMin = Lib.aggNums(Math.min, null, data),
dataMax = Lib.aggNums(Math.max, null, data);
if(!calendar) calendar = ax.calendar;
if(ax.type === 'category') {
return {
start: dataMin - 0.5,
end: dataMax + 0.5,
size: 1
};
}
var size0;
if(nbins) size0 = ((dataMax - dataMin) / nbins);
else {
// totally auto: scale off std deviation so the highest bin is
// somewhat taller than the total number of bins, but don't let
// the size get smaller than the 'nice' rounded down minimum
// difference between values
var distinctData = Lib.distinctVals(data),
msexp = Math.pow(10, Math.floor(
Math.log(distinctData.minDiff) / Math.LN10)),
minSize = msexp * Lib.roundUp(
distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
size0 = Math.max(minSize, 2 * Lib.stdev(data) /
Math.pow(data.length, is2d ? 0.25 : 0.4));
// fallback if ax.d2c output BADNUMs
// e.g. when user try to plot categorical bins
// on a layout.xaxis.type: 'linear'
if(!isNumeric(size0)) size0 = 1;
}
// piggyback off autotick code to make "nice" bin sizes
var dummyAx;
if(ax.type === 'log') {
dummyAx = {
type: 'linear',
range: [dataMin, dataMax]
};
}
else {
dummyAx = {
type: ax.type,
range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
calendar: calendar
};
}
axes.setConvert(dummyAx);
axes.autoTicks(dummyAx, size0);
var binStart = axes.tickIncrement(
axes.tickFirst(dummyAx), dummyAx.dtick, 'reverse', calendar),
binEnd;
// check for too many data points right at the edges of bins
// (>50% within 1% of bin edges) or all data points integral
// and offset the bins accordingly
if(typeof dummyAx.dtick === 'number') {
binStart = autoShiftNumericBins(binStart, data, dummyAx, dataMin, dataMax);
var bincount = 1 + Math.floor((dataMax - binStart) / dummyAx.dtick);
binEnd = binStart + bincount * dummyAx.dtick;
}
else {
// month ticks - should be the only nonlinear kind we have at this point.
// dtick (as supplied by axes.autoTick) only has nonlinear values on
// date and log axes, but even if you display a histogram on a log axis
// we bin it on a linear axis (which one could argue against, but that's
// a separate issue)
if(dummyAx.dtick.charAt(0) === 'M') {
binStart = autoShiftMonthBins(binStart, data, dummyAx.dtick, dataMin, calendar);
}
// calculate the endpoint for nonlinear ticks - you have to
// just increment until you're done
binEnd = binStart;
while(binEnd <= dataMax) {
binEnd = axes.tickIncrement(binEnd, dummyAx.dtick, false, calendar);
}
}
return {
start: ax.c2r(binStart, 0, calendar),
end: ax.c2r(binEnd, 0, calendar),
size: dummyAx.dtick
};
}n/a
autoTicks = function (ax, roughDTick) {
var base;
if(ax.type === 'date') {
ax.tick0 = Lib.dateTick0(ax.calendar);
// the criteria below are all based on the rough spacing we calculate
// being > half of the final unit - so precalculate twice the rough val
var roughX2 = 2 * roughDTick;
if(roughX2 > ONEAVGYEAR) {
roughDTick /= ONEAVGYEAR;
base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10));
ax.dtick = 'M' + (12 * roundDTick(roughDTick, base, roundBase10));
}
else if(roughX2 > ONEAVGMONTH) {
roughDTick /= ONEAVGMONTH;
ax.dtick = 'M' + roundDTick(roughDTick, 1, roundBase24);
}
else if(roughX2 > ONEDAY) {
ax.dtick = roundDTick(roughDTick, ONEDAY, roundDays);
// get week ticks on sunday
// this will also move the base tick off 2000-01-01 if dtick is
// 2 or 3 days... but that's a weird enough case that we'll ignore it.
ax.tick0 = Lib.dateTick0(ax.calendar, true);
}
else if(roughX2 > ONEHOUR) {
ax.dtick = roundDTick(roughDTick, ONEHOUR, roundBase24);
}
else if(roughX2 > ONEMIN) {
ax.dtick = roundDTick(roughDTick, ONEMIN, roundBase60);
}
else if(roughX2 > ONESEC) {
ax.dtick = roundDTick(roughDTick, ONESEC, roundBase60);
}
else {
// milliseconds
base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10));
ax.dtick = roundDTick(roughDTick, base, roundBase10);
}
}
else if(ax.type === 'log') {
ax.tick0 = 0;
var rng = Lib.simpleMap(ax.range, ax.r2l);
if(roughDTick > 0.7) {
// only show powers of 10
ax.dtick = Math.ceil(roughDTick);
}
else if(Math.abs(rng[1] - rng[0]) < 1) {
// span is less than one power of 10
var nt = 1.5 * Math.abs((rng[1] - rng[0]) / roughDTick);
// ticks on a linear scale, labeled fully
roughDTick = Math.abs(Math.pow(10, rng[1]) -
Math.pow(10, rng[0])) / nt;
base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10));
ax.dtick = 'L' + roundDTick(roughDTick, base, roundBase10);
}
else {
// include intermediates between powers of 10,
// labeled with small digits
// ax.dtick = "D2" (show 2 and 5) or "D1" (show all digits)
ax.dtick = (roughDTick > 0.3) ? 'D2' : 'D1';
}
}
else if(ax.type === 'category') {
ax.tick0 = 0;
ax.dtick = Math.ceil(Math.max(roughDTick, 1));
}
else {
// auto ticks always start at 0
ax.tick0 = 0;
base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10));
ax.dtick = roundDTick(roughDTick, base, roundBase10);
}
// prevent infinite loops
if(ax.dtick === 0) ax.dtick = 1;
// TODO: this is from log axis histograms with autorange off
if(!isNumeric(ax.dtick) && typeof ax.dtick !== 'string') {
var olddtick = ax.dtick;
ax.dtick = 1;
throw 'ax.dtick error: ' + String(olddtick);
}
}...
type: ax.type,
range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
calendar: calendar
};
}
axes.setConvert(dummyAx);
axes.autoTicks(dummyAx, size0);
var binStart = axes.tickIncrement(
axes.tickFirst(dummyAx), dummyAx.dtick, 'reverse', calendar),
binEnd;
// check for too many data points right at the edges of bins
// (>50% within 1% of bin edges) or all data points integral
// and offset the bins accordingly
...function calcTicks(ax) {
var rng = Lib.simpleMap(ax.range, ax.r2l);
// calculate max number of (auto) ticks to display based on plot size
if(ax.tickmode === 'auto' || !ax.dtick) {
var nt = ax.nticks,
minPx;
if(!nt) {
if(ax.type === 'category') {
minPx = ax.tickfont ? (ax.tickfont.size || 12) * 1.2 : 15;
nt = ax._length / minPx;
}
else {
minPx = ax._id.charAt(0) === 'y' ? 40 : 80;
nt = Lib.constrain(ax._length / minPx, 4, 9) + 1;
}
}
// add a couple of extra digits for filling in ticks when we
// have explicit tickvals without tick text
if(ax.tickmode === 'array') nt *= 100;
axes.autoTicks(ax, Math.abs(rng[1] - rng[0]) / nt);
// check for a forced minimum dtick
if(ax._minDtick > 0 && ax.dtick < ax._minDtick * 2) {
ax.dtick = ax._minDtick;
ax.tick0 = ax.l2r(ax._forceTick0);
}
}
// check for missing tick0
if(!ax.tick0) {
ax.tick0 = (ax.type === 'date') ? '2000-01-01' : 0;
}
// now figure out rounding of tick values
autoTickRound(ax);
// now that we've figured out the auto values for formatting
// in case we're missing some ticktext, we can break out for array ticks
if(ax.tickmode === 'array') return arrayTicks(ax);
// find the first tick
ax._tmin = axes.tickFirst(ax);
// check for reversed axis
var axrev = (rng[1] < rng[0]);
// return the full set of tick vals
var vals = [],
// add a tiny bit so we get ticks which may have rounded out
endtick = rng[1] * 1.0001 - rng[0] * 0.0001;
if(ax.type === 'category') {
endtick = (axrev) ? Math.max(-0.5, endtick) :
Math.min(ax._categories.length - 0.5, endtick);
}
for(var x = ax._tmin;
(axrev) ? (x >= endtick) : (x <= endtick);
x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)) {
vals.push(x);
// prevent infinite loops
if(vals.length > 1000) break;
}
// save the last tick as well as first, so we can
// show the exponent only on the last one
ax._tmax = vals[vals.length - 1];
// for showing the rest of a date when the main tick label is only the
// latter part: ax._prevDateHead holds what we showed most recently.
// Start with it cleared and mark that we're in calcTicks (ie calculating a
// whole string of these so we should care what the previous date head was!)
ax._prevDateHead = '';
ax._inCalcTicks = true;
var ticksOut = new Array(vals.length);
for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]);
ax._inCalcTicks = false;
return ticksOut;
}...
}
// set scaling to pixels
ax.setScale();
var axLetter = axid.charAt(0),
counterLetter = axes.counterLetter(axid),
vals = axes.calcTicks(ax),
datafn = function(d) { return [d.text, d.x, ax.mirror].join('_'); },
tcls = axid + 'tick',
gcls = axid + 'grid',
zcls = axid + 'zl',
pad = (ax.linewidth || 1) / 2,
labelStandoff =
(ax.ticks === 'outside' ? ax.ticklen : 1) + (ax.linewidth || 0),
...function cleanId(id, axLetter) {
if(!id.match(constants.AX_ID_PATTERN)) return;
if(axLetter && id.charAt(0) !== axLetter) return;
var axNum = id.substr(1).replace(/^0+/, '');
if(axNum === '1') axNum = '';
return id.charAt(0) + axNum;
}...
delete layout.yaxis1;
}
var axList = Axes.list({_fullLayout: layout});
for(i = 0; i < axList.length; i++) {
var ax = axList[i];
if(ax.anchor && ax.anchor !== 'free') {
ax.anchor = Axes.cleanId(ax.anchor);
}
if(ax.overlaying) ax.overlaying = Axes.cleanId(ax.overlaying);
// old method of axis type - isdate and islog (before category existed)
if(!ax.type) {
if(ax.isdate) ax.type = 'date';
else if(ax.islog) ax.type = 'log';
...clearTypes = function (gd, traces) {
if(!Array.isArray(traces) || !traces.length) {
traces = (gd._fullData).map(function(d, i) { return i; });
}
traces.forEach(function(tracenum) {
var trace = gd.data[tracenum];
delete (axes.getFromId(gd, trace.xaxis) || {}).type;
delete (axes.getFromId(gd, trace.yaxis) || {}).type;
});
}...
} else if(hovermode.get() === 'y') {
hovermode.set('x');
}
}
// check if we need to call axis type
if((traces.indexOf(0) !== -1) && (axtypeAttrs.indexOf(ai) !== -1)) {
Plotly.Axes.clearTypes(gd, traces);
flags.docalc = true;
}
// switching from auto to manual binning or z scaling doesn't
// actually do anything but change what you see in the styling
// box. everything else at least needs to apply styles
if((['autobinx', 'autobiny', 'zauto'].indexOf(ai) === -1) ||
...coercePosition = function (containerOut, gd, coerce, axRef, attr, dflt) {
var pos,
newPos;
if(axRef === 'paper' || axRef === 'pixel') {
pos = coerce(attr, dflt);
}
else {
var ax = axes.getFromId(gd, axRef);
dflt = ax.fraction2r(dflt);
pos = coerce(attr, dflt);
if(ax.type === 'category') {
// if position is given as a category name, convert it to a number
if(typeof pos === 'string' && (ax._categories || []).length) {
newPos = ax._categories.indexOf(pos);
containerOut[attr] = (newPos === -1) ? dflt : newPos;
return;
}
}
else if(ax.type === 'date') {
containerOut[attr] = Lib.cleanDate(pos, BADNUM, ax.calendar);
return;
}
}
// finally make sure we have a number (unless date type already returned a string)
containerOut[attr] = isNumeric(pos) ? Number(pos) : dflt;
}...
for(var i = 0; i < 2; i++) {
var axLetter = axLetters[i];
// xref, yref
var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper');
// x, y
Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5);
if(showArrow) {
var arrowPosAttr = 'a' + axLetter,
// axref, ayref
aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel');
// for now the arrow can only be on the same axis or specified as pixels
...coerceRef = function (containerIn, containerOut, gd, attr, dflt, extraOption) {
var axLetter = attr.charAt(attr.length - 1),
axlist = axes.listIds(gd, axLetter),
refAttr = attr + 'ref',
attrDef = {};
if(!dflt) dflt = axlist[0] || extraOption;
if(!extraOption) extraOption = dflt;
// data-ref annotations are not supported in gl2d yet
attrDef[refAttr] = {
valType: 'enumerated',
values: axlist.concat(extraOption ? [extraOption] : []),
dflt: dflt
};
// xref, yref
return Lib.coerce(containerIn, containerOut, attrDef, refAttr);
}...
var axLetters = ['x', 'y'],
arrowPosDflt = [-10, -30],
gdMock = {_fullLayout: fullLayout};
for(var i = 0; i < 2; i++) {
var axLetter = axLetters[i];
// xref, yref
var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper
');
// x, y
Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5);
if(showArrow) {
var arrowPosAttr = 'a' + axLetter,
// axref, ayref
...counterLetter = function (id) {
var axLetter = id.charAt(0);
if(axLetter === 'x') return 'y';
if(axLetter === 'y') return 'x';
}...
}
}
// set scaling to pixels
ax.setScale();
var axLetter = axid.charAt(0),
counterLetter = axes.counterLetter(axid),
vals = axes.calcTicks(ax),
datafn = function(d) { return [d.text, d.x, ax.mirror].join('_'); },
tcls = axid + 'tick',
gcls = axid + 'grid',
zcls = axid + 'zl',
pad = (ax.linewidth || 1) / 2,
labelStandoff =
...doAutoRange = function (ax) {
if(!ax._length) ax.setScale();
// TODO do we really need this?
var hasDeps = (ax._min && ax._max && ax._min.length && ax._max.length);
if(ax.autorange && hasDeps) {
ax.range = axes.getAutoRange(ax);
// doAutoRange will get called on fullLayout,
// but we want to report its results back to layout
var axIn = ax._input;
axIn.range = ax.range.slice();
axIn.autorange = ax.autorange;
}
}...
}
function doAutoRangeAndConstraints() {
if(gd._transitioning) return;
var axList = Plotly.Axes.list(gd, '', true);
for(var i = 0; i < axList.length; i++) {
Plotly.Axes.doAutoRange(axList[i]);
}
enforceAxisConstraints(gd);
// store initial ranges *after* enforcing constraints, otherwise
// we will never look like we're at the initial ranges
if(graphWasEmpty) Plotly.Axes.saveRangeInitial(gd);
...doTicks = function (gd, axid, skipTitle) {
var fullLayout = gd._fullLayout,
ax,
independent = false;
// allow passing an independent axis object instead of id
if(typeof axid === 'object') {
ax = axid;
axid = ax._id;
independent = true;
}
else {
ax = axes.getFromId(gd, axid);
if(axid === 'redraw') {
fullLayout._paper.selectAll('g.subplot').each(function(subplot) {
var plotinfo = fullLayout._plots[subplot],
xa = plotinfo.xaxis,
ya = plotinfo.yaxis;
plotinfo.xaxislayer
.selectAll('.' + xa._id + 'tick').remove();
plotinfo.yaxislayer
.selectAll('.' + ya._id + 'tick').remove();
plotinfo.gridlayer
.selectAll('path').remove();
plotinfo.zerolinelayer
.selectAll('path').remove();
});
}
if(!axid || axid === 'redraw') {
return Lib.syncOrAsync(axes.list(gd, '', true).map(function(ax) {
return function() {
if(!ax._id) return;
var axDone = axes.doTicks(gd, ax._id);
if(axid === 'redraw') {
ax._r = ax.range.slice();
ax._rl = Lib.simpleMap(ax._r, ax.r2l);
}
return axDone;
};
}));
}
}
// make sure we only have allowed options for exponents
// (others can make confusing errors)
if(!ax.tickformat) {
if(['none', 'e', 'E', 'power', 'SI', 'B'].indexOf(ax.exponentformat) === -1) {
ax.exponentformat = 'e';
}
if(['all', 'first', 'last', 'none'].indexOf(ax.showexponent) === -1) {
ax.showexponent = 'all';
}
}
// set scaling to pixels
ax.setScale();
var axLetter = axid.charAt(0),
counterLetter = axes.counterLetter(axid),
vals = axes.calcTicks(ax),
datafn = function(d) { return [d.text, d.x, ax.mirror].join('_'); },
tcls = axid + 'tick',
gcls = axid + 'grid',
zcls = axid + 'zl',
pad = (ax.linewidth || 1) / 2,
labelStandoff =
(ax.ticks === 'outside' ? ax.ticklen : 1) + (ax.linewidth || 0),
labelShift = 0,
gridWidth = Drawing.crispRound(gd, ax.gridwidth, 1),
zeroLineWidth = Drawing.crispRound(gd, ax.zerolinewidth, gridWidth),
tickWidth = Drawing.crispRound(gd, ax.tickwidth, 1),
sides, transfn, tickpathfn, subplots,
i;
if(ax._counterangle && ax.ticks === 'outside') {
var caRad = ax._counterangle * Math.PI / 180;
labelStandoff = ax.ticklen * Math.cos(caRad) + (ax.linewidth || 0);
labelShift = ax.ticklen * Math.sin(caRad);
}
// positioning arguments for x vs y axes
if(axLetter === 'x') {
sides = ['bottom', 'top'];
transfn = function(d) {
return 'translate(' + ax.l2p(d.x) + ',0)';
};
tickpathfn = function(shift, len) {
if(ax._counterangle) {
var caRad = ax._counterangle * Math.PI / 180;
return 'M0,' + shift + 'l' + (Math.sin(caRad) * len) + ',' + (Math.cos(caRad) * len);
}
else return 'M0,' + shift + 'v' + len;
};
}
else if(axLetter === 'y') {
sides = ['left', 'right'];
transfn = function(d) {
return 'translate(0,' + ax.l2p(d.x) + ')';
};
tickpathfn = function(shift, len) {
if(ax._counterangle) {
var caRad = ax._counterangle * Math.PI / 180;
return 'M' + shift + ',0l' + (Math.cos(caRad) * len) + ',' + (-Math.sin(caRad) * len);
}
else return 'M' + shift + ',0h' + len;
};
}
else {
Lib.warn('Unrecognized doTicks axis:', axid);
return;
}
var axside = ax.side || sides[0],
// which direction do the side[0], side[1], and ......
// store initial ranges *after* enforcing constraints, otherwise
// we will never look like we're at the initial ranges
if(graphWasEmpty) Plotly.Axes.saveRangeInitial(gd);
}
// draw ticks, titles, and calculate axis scaling (._b, ._m)
function drawAxes() {
return Plotly.Axes.doTicks(gd, 'redraw');
}
// Now plot the data
function drawData() {
var calcdata = gd.calcdata,
i;
...expand = function (ax, data, options) {
var needsAutorange = (
ax.autorange ||
!!Lib.nestedProperty(ax, 'rangeslider.autorange').get()
);
if(!needsAutorange || !data) return;
if(!ax._min) ax._min = [];
if(!ax._max) ax._max = [];
if(!options) options = {};
if(!ax._m) ax.setScale();
var len = data.length,
extrappad = options.padded ? ax._length * 0.05 : 0,
tozero = options.tozero && (ax.type === 'linear' || ax.type === '-'),
i, j, v, di, dmin, dmax,
ppadiplus, ppadiminus, includeThis, vmin, vmax;
function getPad(item) {
if(Array.isArray(item)) {
return function(i) { return Math.max(Number(item[i]||0), 0); };
}
else {
var v = Math.max(Number(item||0), 0);
return function() { return v; };
}
}
var ppadplus = getPad((ax._m > 0 ?
options.ppadplus : options.ppadminus) || options.ppad || 0),
ppadminus = getPad((ax._m > 0 ?
options.ppadminus : options.ppadplus) || options.ppad || 0),
vpadplus = getPad(options.vpadplus || options.vpad),
vpadminus = getPad(options.vpadminus || options.vpad);
function addItem(i) {
di = data[i];
if(!isNumeric(di)) return;
ppadiplus = ppadplus(i) + extrappad;
ppadiminus = ppadminus(i) + extrappad;
vmin = di - vpadminus(i);
vmax = di + vpadplus(i);
// special case for log axes: if vpad makes this object span
// more than an order of mag, clip it to one order. This is so
// we don't have non-positive errors or absurdly large lower
// range due to rounding errors
if(ax.type === 'log' && vmin < vmax / 10) { vmin = vmax / 10; }
dmin = ax.c2l(vmin);
dmax = ax.c2l(vmax);
if(tozero) {
dmin = Math.min(0, dmin);
dmax = Math.max(0, dmax);
}
// In order to stop overflow errors, don't consider points
// too close to the limits of js floating point
function goodNumber(v) {
return isNumeric(v) && Math.abs(v) < FP_SAFE;
}
if(goodNumber(dmin)) {
includeThis = true;
// take items v from ax._min and compare them to the
// presently active point:
// - if the item supercedes the new point, set includethis false
// - if the new pt supercedes the item, delete it from ax._min
for(j = 0; j < ax._min.length && includeThis; j++) {
v = ax._min[j];
if(v.val <= dmin && v.pad >= ppadiminus) {
includeThis = false;
}
else if(v.val >= dmin && v.pad <= ppadiminus) {
ax._min.splice(j, 1);
j--;
}
}
if(includeThis) {
ax._min.push({
val: dmin,
pad: (tozero && dmin === 0) ? 0 : ppadiminus
});
}
}
if(goodNumber(dmax)) {
includeThis = true;
for(j = 0; j < ax._max.length && includeThis; j++) {
v = ax._max[j];
if(v.val >= dmax && v.pad >= ppadiplus) {
includeThis = false;
}
else if(v.val <= dmax && v.pad <= ppadiplus) {
ax._max.splice(j, 1);
j--;
}
}
if(includeThis) {
ax._max.push({
val: dmax,
pad: (tozero && dmax === 0) ? 0 : ppadiplus
});
}
}
}
// For efficiency covering monotonic or near-monotonic data,
// check a few points at both ends first and then sweep
// through the middle
for(i = 0; i < 6; i++) addItem(i);
for(i = len - 1; i > 5; i--) addItem(i);
}...
if(xa && xa.autorange) {
headPlus = headSize + ann.xshift;
headMinus = headSize - ann.xshift;
if(ann.axref === ann.xref) {
// expand for the arrowhead (padded by arrowhead)
Axes.expand(xa, [xa.r2c(ann.x)], {
ppadplus: headPlus,
ppadminus: headMinus
});
// again for the textbox (padded by textbox)
Axes.expand(xa, [xa.r2c(ann.ax)], {
ppadplus: ann._xpadplus,
ppadminus: ann._xpadminus
...findSubplotsWithAxis = function (subplots, ax) {
var axMatch = new RegExp(
(ax._id.charAt(0) === 'x') ? ('^' + ax._id + 'y') : (ax._id + '$')
);
var subplotsWithAxis = [];
for(var i = 0; i < subplots.length; i++) {
var sp = subplots[i];
if(axMatch.test(sp)) subplotsWithAxis.push(sp);
}
return subplotsWithAxis;
}...
if(aMatch[1] === bMatch[1]) {
return +(aMatch[2] || 1) - (bMatch[2] || 1);
}
return +(aMatch[1]||0) - (bMatch[1]||0);
});
if(ax) return axes.findSubplotsWithAxis(allSubplots, ax);
return allSubplots;
};
// find all subplots with axis 'ax'
axes.findSubplotsWithAxis = function(subplots, ax) {
var axMatch = new RegExp(
(ax._id.charAt(0) === 'x') ? ('^' + ax._id + 'y') : (ax._id + '$')
...getAutoRange = function (ax) {
var newRange = [];
var minmin = ax._min[0].val,
maxmax = ax._max[0].val,
i;
for(i = 1; i < ax._min.length; i++) {
if(minmin !== maxmax) break;
minmin = Math.min(minmin, ax._min[i].val);
}
for(i = 1; i < ax._max.length; i++) {
if(minmin !== maxmax) break;
maxmax = Math.max(maxmax, ax._max[i].val);
}
var j, minpt, maxpt, minbest, maxbest, dp, dv,
mbest = 0,
axReverse = false;
if(ax.range) {
var rng = Lib.simpleMap(ax.range, ax.r2l);
axReverse = rng[1] < rng[0];
}
// one-time setting to easily reverse the axis
// when plotting from code
if(ax.autorange === 'reversed') {
axReverse = true;
ax.autorange = true;
}
for(i = 0; i < ax._min.length; i++) {
minpt = ax._min[i];
for(j = 0; j < ax._max.length; j++) {
maxpt = ax._max[j];
dv = maxpt.val - minpt.val;
dp = ax._length - minpt.pad - maxpt.pad;
if(dv > 0 && dp > 0 && dv / dp > mbest) {
minbest = minpt;
maxbest = maxpt;
mbest = dv / dp;
}
}
}
if(minmin === maxmax) {
var lower = minmin - 1;
var upper = minmin + 1;
if(ax.rangemode === 'tozero') {
newRange = minmin < 0 ? [lower, 0] : [0, upper];
}
else if(ax.rangemode === 'nonnegative') {
newRange = [Math.max(0, lower), Math.max(0, upper)];
}
else {
newRange = [lower, upper];
}
}
else if(mbest) {
if(ax.type === 'linear' || ax.type === '-') {
if(ax.rangemode === 'tozero') {
if(minbest.val >= 0) {
minbest = {val: 0, pad: 0};
}
if(maxbest.val <= 0) {
maxbest = {val: 0, pad: 0};
}
}
else if(ax.rangemode === 'nonnegative') {
if(minbest.val - mbest * minbest.pad < 0) {
minbest = {val: 0, pad: 0};
}
if(maxbest.val < 0) {
maxbest = {val: 1, pad: 0};
}
}
// in case it changed again...
mbest = (maxbest.val - minbest.val) /
(ax._length - minbest.pad - maxbest.pad);
}
newRange = [
minbest.val - mbest * minbest.pad,
maxbest.val + mbest * maxbest.pad
];
}
// don't let axis have zero size, while still respecting tozero and nonnegative
if(newRange[0] === newRange[1]) {
if(ax.rangemode === 'tozero') {
if(newRange[0] < 0) {
newRange = [newRange[0], 0];
}
else if(newRange[0] > 0) {
newRange = [0, newRange[0]];
}
else {
newRange = [0, 1];
}
}
else {
newRange = [newRange[0] - 1, newRange[0] + 1];
if(ax.rangemode === 'nonnegative') {
newRange[0] = Math.max(0, newRange[0]);
}
}
}
// maintain reversal
if(axReverse) newRange.reverse();
return Lib.simpleMap(newRange, ax.l2r || Number);
}...
axes.doAutoRange = function(ax) {
if(!ax._length) ax.setScale();
// TODO do we really need this?
var hasDeps = (ax._min && ax._max && ax._min.length && ax._max.length);
if(ax.autorange && hasDeps) {
ax.range = axes.getAutoRange(ax);
// doAutoRange will get called on fullLayout,
// but we want to report its results back to layout
var axIn = ax._input;
axIn.range = ax.range.slice();
axIn.autorange = ax.autorange;
...getFromId = function (gd, id, type) {
var fullLayout = gd._fullLayout;
if(type === 'x') id = id.replace(/y[0-9]*/, '');
else if(type === 'y') id = id.replace(/x[0-9]*/, '');
return fullLayout[exports.id2name(id)];
}...
if(val !== undefined) p.set(val);
}
// for editing annotations or shapes - is it on autoscaled axes?
function refAutorange(obj, axLetter) {
if(!Lib.isPlainObject(obj)) return false;
var axRef = obj[axLetter + 'ref'] || axLetter,
ax = Plotly.Axes.getFromId(gd, axRef);
if(!ax && axRef.charAt(0) === axLetter) {
// fall back on the primary axis in case we've referenced a
// nonexistent axis (as we do above if axRef is missing).
// This assumes the object defaults to data referenced, which
// is the case for shapes and annotations but not for images.
// The only thing this is used for is to determine whether to
...getFromTrace = function (gd, fullTrace, type) {
var fullLayout = gd._fullLayout;
var ax = null;
if(Registry.traceIs(fullTrace, 'gl3d')) {
var scene = fullTrace.scene;
if(scene.substr(0, 5) === 'scene') {
ax = fullLayout[scene][type + 'axis'];
}
}
else {
ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type);
}
return ax;
}n/a
getSubplots = function (gd, ax) {
var subplots = [];
var i, j, sp;
// look for subplots in the data
var data = gd._fullData || gd.data || [];
for(i = 0; i < data.length; i++) {
var trace = data[i];
if(trace.visible === false || trace.visible === 'legendonly' ||
!(Registry.traceIs(trace, 'cartesian') || Registry.traceIs(trace, 'gl2d'))
) continue;
var xId = trace.xaxis || 'x',
yId = trace.yaxis || 'y';
sp = xId + yId;
if(subplots.indexOf(sp) === -1) subplots.push(sp);
}
// look for subplots in the axes/anchors, so that we at least draw all axes
var axesList = axes.list(gd, '', true);
function hasAx2(sp, ax2) {
return sp.indexOf(ax2._id) !== -1;
}
for(i = 0; i < axesList.length; i++) {
var ax2 = axesList[i],
ax2Letter = ax2._id.charAt(0),
ax3Id = (ax2.anchor === 'free') ?
((ax2Letter === 'x') ? 'y' : 'x') :
ax2.anchor,
ax3 = axes.getFromId(gd, ax3Id);
// look if ax2 is already represented in the data
var foundAx2 = false;
for(j = 0; j < subplots.length; j++) {
if(hasAx2(subplots[j], ax2)) {
foundAx2 = true;
break;
}
}
// ignore free axes that already represented in the data
if(ax2.anchor === 'free' && foundAx2) continue;
// ignore anchor-less axes
if(!ax3) continue;
sp = (ax2Letter === 'x') ?
ax2._id + ax3._id :
ax3._id + ax2._id;
if(subplots.indexOf(sp) === -1) subplots.push(sp);
}
// filter invalid subplots
var spMatch = axes.subplotMatch,
allSubplots = [];
for(i = 0; i < subplots.length; i++) {
sp = subplots[i];
if(spMatch.test(sp)) allSubplots.push(sp);
}
// sort the subplot ids
allSubplots.sort(function(a, b) {
var aMatch = a.match(spMatch),
bMatch = b.match(spMatch);
if(aMatch[1] === bMatch[1]) {
return +(aMatch[2] || 1) - (bMatch[2] || 1);
}
return +(aMatch[1]||0) - (bMatch[1]||0);
});
if(ax) return axes.findSubplotsWithAxis(allSubplots, ax);
return allSubplots;
}...
newSubplots = newFullLayout._plots = {};
var mockGd = {
_fullData: newFullData,
_fullLayout: newFullLayout
};
var ids = Plotly.Axes.getSubplots(mockGd);
for(var i = 0; i < ids.length; i++) {
var id = ids[i],
oldSubplot = oldSubplots[id],
plotinfo;
if(oldSubplot) {
...function id2name(id) {
if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
var axNum = id.substr(1);
if(axNum === '1') axNum = '';
return id.charAt(0) + 'axis' + axNum;
}...
});
// make a new empty vals array for undoit
function a0() { return traces.map(function() { return undefined; }); }
// for autoranging multiple axes
function addToAxlist(axid) {
var axName = Plotly.Axes.id2name(axid);
if(axlist.indexOf(axName) === -1) axlist.push(axName);
}
function autorangeAttr(axName) { return 'LAYOUT' + axName + '.autorange'; }
function rangeAttr(axName) { return 'LAYOUT' + axName + '.range'; }
...list = function (gd, axletter, only2d) {
return listNames(gd, axletter, only2d)
.map(function(axName) {
return Lib.nestedProperty(gd._fullLayout, axName).get();
});
}...
delete layout.xaxis1;
}
if(layout.yaxis1) {
if(!layout.yaxis) layout.yaxis = layout.yaxis1;
delete layout.yaxis1;
}
var axList = Axes.list({_fullLayout: layout});
for(i = 0; i < axList.length; i++) {
var ax = axList[i];
if(ax.anchor && ax.anchor !== 'free') {
ax.anchor = Axes.cleanId(ax.anchor);
}
if(ax.overlaying) ax.overlaying = Axes.cleanId(ax.overlaying);
...listIds = function (gd, axletter) {
return listNames(gd, axletter, true).map(exports.name2id);
}...
* dflt: the default to coerce to, or blank to use the first axis (falling back on
* extraOption if there is no axis)
* extraOption: aside from existing axes with this letter, what non-axis value is allowed?
* Only required if it's different from `dflt`
*/
axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption) {
var axLetter = attr.charAt(attr.length - 1),
axlist = axes.listIds(gd, axLetter),
refAttr = attr + 'ref',
attrDef = {};
if(!dflt) dflt = axlist[0] || extraOption;
if(!extraOption) extraOption = dflt;
// data-ref annotations are not supported in gl2d yet
...makeClipPaths = function (gd) {
var fullLayout = gd._fullLayout,
defs = fullLayout._defs,
fullWidth = {_offset: 0, _length: fullLayout.width, _id: ''},
fullHeight = {_offset: 0, _length: fullLayout.height, _id: ''},
xaList = axes.list(gd, 'x', true),
yaList = axes.list(gd, 'y', true),
clipList = [],
i,
j;
for(i = 0; i < xaList.length; i++) {
clipList.push({x: xaList[i], y: fullHeight});
for(j = 0; j < yaList.length; j++) {
if(i === 0) clipList.push({x: fullWidth, y: yaList[j]});
clipList.push({x: xaList[i], y: yaList[j]});
}
}
var defGroup = defs.selectAll('g.clips')
.data([0]);
defGroup.enter().append('g')
.classed('clips', true);
// selectors don't work right with camelCase tags,
// have to use class instead
// https://groups.google.com/forum/#!topic/d3-js/6EpAzQ2gU9I
var axClips = defGroup.selectAll('.axesclip')
.data(clipList, function(d) { return d.x._id + d.y._id; });
axClips.enter().append('clipPath')
.classed('axesclip', true)
.attr('id', function(d) { return 'clip' + fullLayout._uid + d.x._id + d.y._id; })
.append('rect');
axClips.exit().remove();
axClips.each(function(d) {
d3.select(this).select('rect').attr({
x: d.x._offset || 0,
y: d.y._offset || 0,
width: d.x._length || 1,
height: d.y._length || 1
});
});
}...
plotinfo.draglayer.attr('transform', origin);
// mark free axes as displayed, so we don't draw them again
if(showfreex) { freefinished.push(xa._id); }
if(showfreey) { freefinished.push(ya._id); }
});
Plotly.Axes.makeClipPaths(gd);
exports.drawMainTitle(gd);
ModeBar.manage(gd);
return gd._promises.length && Promise.all(gd._promises);
};
exports.drawMainTitle = function(gd) {
...minDtick = function (ax, newDiff, newFirst, allow) {
// doesn't make sense to do forced min dTick on log or category axes,
// and the plot itself may decide to cancel (ie non-grouped bars)
if(['log', 'category'].indexOf(ax.type) !== -1 || !allow) {
ax._minDtick = 0;
}
// undefined means there's nothing there yet
else if(ax._minDtick === undefined) {
ax._minDtick = newDiff;
ax._forceTick0 = newFirst;
}
else if(ax._minDtick) {
// existing minDtick is an integer multiple of newDiff
// (within rounding err)
// and forceTick0 can be shifted to newFirst
if((ax._minDtick / newDiff + 1e-6) % 1 < 2e-6 &&
(((newFirst - ax._forceTick0) / newDiff % 1) +
1.000001) % 1 < 2e-6) {
ax._minDtick = newDiff;
ax._forceTick0 = newFirst;
}
// if the converse is true (newDiff is a multiple of minDtick and
// newFirst can be shifted to forceTick0) then do nothing - same
// forcing stands. Otherwise, cancel forced minimum
else if((newDiff / ax._minDtick + 1e-6) % 1 > 2e-6 ||
(((newFirst - ax._forceTick0) / ax._minDtick % 1) +
1.000001) % 1 > 2e-6) {
ax._minDtick = 0;
}
}
}...
function updatePositionAxis(gd, pa, sieve, allowMinDtick) {
var calcTraces = sieve.traces,
distinctPositions = sieve.distinctPositions,
distinctPositions0 = distinctPositions[0],
minDiff = sieve.minDiff,
vpad = minDiff / 2;
Axes.minDtick(pa, minDiff, distinctPositions0, allowMinDtick);
// If the user set the bar width or the offset,
// then bars can be shifted away from their positions
// and widths can be larger than minDiff.
//
// Here, we compute pMin and pMax to expand the position axis,
// so that all bars are fully within the axis range.
...saveRangeInitial = function (gd, overwrite) {
var axList = axes.list(gd, '', true),
hasOneAxisChanged = false;
for(var i = 0; i < axList.length; i++) {
var ax = axList[i];
var isNew = (ax._rangeInitial === undefined);
var hasChanged = (
isNew || !(
ax.range[0] === ax._rangeInitial[0] &&
ax.range[1] === ax._rangeInitial[1]
)
);
if((isNew && ax.autorange === false) || (overwrite && hasChanged)) {
ax._rangeInitial = ax.range.slice();
hasOneAxisChanged = true;
}
}
return hasOneAxisChanged;
}...
Plotly.Axes.doAutoRange(axList[i]);
}
enforceAxisConstraints(gd);
// store initial ranges *after* enforcing constraints, otherwise
// we will never look like we're at the initial ranges
if(graphWasEmpty) Plotly.Axes.saveRangeInitial(gd);
}
// draw ticks, titles, and calculate axis scaling (._b, ._m)
function drawAxes() {
return Plotly.Axes.doTicks(gd, 'redraw');
}
...saveShowSpikeInitial = function (gd, overwrite) {
var axList = axes.list(gd, '', true),
hasOneAxisChanged = false,
allEnabled = 'on';
for(var i = 0; i < axList.length; i++) {
var ax = axList[i];
var isNew = (ax._showSpikeInitial === undefined);
var hasChanged = (
isNew || !(
ax.showspikes === ax._showspikes
)
);
if((isNew) || (overwrite && hasChanged)) {
ax._showSpikeInitial = ax.showspikes;
hasOneAxisChanged = true;
}
if(allEnabled === 'on' && !ax.showspikes) {
allEnabled = 'off';
}
}
gd._fullLayout._cartesianSpikesEnabled = allEnabled;
return hasOneAxisChanged;
}...
// polar need a different framework
if(gd.framework !== makePlotFramework) {
gd.framework = makePlotFramework;
makePlotFramework(gd);
}
// save initial show spikes once per graph
if(graphWasEmpty) Plotly.Axes.saveShowSpikeInitial(gd);
// prepare the data and find the autorange
// generate calcdata, if we need to
// to force redoing calcdata, just delete it before calling Plotly.plot
var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
if(recalc) Plots.doCalcdata(gd);
...function setConvert(ax, fullLayout) {
fullLayout = fullLayout || {};
// clipMult: how many axis lengths past the edge do we render?
// for panning, 1-2 would suffice, but for zooming more is nice.
// also, clipping can affect the direction of lines off the edge...
var clipMult = 10;
function toLog(v, clip) {
if(v > 0) return Math.log(v) / Math.LN10;
else if(v <= 0 && clip && ax.range && ax.range.length === 2) {
// clip NaN (ie past negative infinity) to clipMult axis
// length past the negative edge
var r0 = ax.range[0],
r1 = ax.range[1];
return 0.5 * (r0 + r1 - 3 * clipMult * Math.abs(r0 - r1));
}
else return BADNUM;
}
/*
* wrapped dateTime2ms that:
* - accepts ms numbers for backward compatibility
* - inserts a dummy arg so calendar is the 3rd arg (see notes below).
* - defaults to ax.calendar
*/
function dt2ms(v, _, calendar) {
// NOTE: Changed this behavior: previously we took any numeric value
// to be a ms, even if it was a string that could be a bare year.
// Now we convert it as a date if at all possible, and only try
// as (local) ms if that fails.
var ms = dateTime2ms(v, calendar || ax.calendar);
if(ms === BADNUM) {
if(isNumeric(v)) ms = dateTime2ms(new Date(+v));
else return BADNUM;
}
return ms;
}
// wrapped ms2DateTime to insert default ax.calendar
function ms2dt(v, r, calendar) {
return ms2DateTime(v, r, calendar || ax.calendar);
}
function getCategoryName(v) {
return ax._categories[Math.round(v)];
}
/*
* setCategoryIndex: return the index of category v,
* inserting it in the list if it's not already there
*
* this will enter the categories in the order it
* encounters them, ie all the categories from the
* first data set, then all the ones from the second
* that aren't in the first etc.
*
* it is assumed that this function is being invoked in the
* already sorted category order; otherwise there would be
* a disconnect between the array and the index returned
*/
function setCategoryIndex(v) {
if(v !== null && v !== undefined) {
if(ax._categoriesMap === undefined) {
ax._categoriesMap = {};
}
if(ax._categoriesMap[v] !== undefined) {
return ax._categoriesMap[v];
} else {
ax._categories.push(v);
var curLength = ax._categories.length - 1;
ax._categoriesMap[v] = curLength;
return curLength;
}
}
return BADNUM;
}
function getCategoryIndex(v) {
// d2l/d2c variant that that won't add categories but will also
// allow numbers to be mapped to the linearized axis positions
if(ax._categoriesMap) {
var index = ax._categoriesMap[v];
if(index !== undefined) return index;
}
if(typeof v === 'number') { return v; }
}
function l2p(v) {
if(!isNumeric(v)) return BADNUM;
// include 2 fractional digits on pixel, for PDF zooming etc
return d3.round(ax._b + ax._m * v, 2);
}
function p2l(px) { return (px - ax._b) / ax._m; }
// conversions among c/l/p are fairly simple - do them together for all axis types
ax.c2l = (ax.type === 'log') ? toLog : num;
ax.l2c = (ax.type === 'log') ? fromLog : num;
ax.l2p = l2p;
ax.p2l = p2l;
ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p;
ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l;
/*
* now type-specific conversions for **ALL** other combinations
* they're all written out, instead of being combinations of each other, for
* both clarity and speed.
*/
if(['linear', '-'].indexOf(ax.type) !== -1) {
// all are data vals, ......
else {
dummyAx = {
type: ax.type,
range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
calendar: calendar
};
}
axes.setConvert(dummyAx);
axes.autoTicks(dummyAx, size0);
var binStart = axes.tickIncrement(
axes.tickFirst(dummyAx), dummyAx.dtick, 'reverse', calendar),
binEnd;
// check for too many data points right at the edges of bins
...function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
var layoutKeys = Object.keys(layoutIn),
xaListCartesian = [],
yaListCartesian = [],
xaListGl2d = [],
yaListGl2d = [],
xaListCheater = [],
xaListNonCheater = [],
outerTicks = {},
noGrids = {},
i;
// look for axes in the data
for(i = 0; i < fullData.length; i++) {
var trace = fullData[i];
var listX, listY;
if(Registry.traceIs(trace, 'cartesian')) {
listX = xaListCartesian;
listY = yaListCartesian;
}
else if(Registry.traceIs(trace, 'gl2d')) {
listX = xaListGl2d;
listY = yaListGl2d;
}
else continue;
var xaName = axisIds.id2name(trace.xaxis),
yaName = axisIds.id2name(trace.yaxis);
// Two things trigger axis visibility:
// 1. is not carpet
// 2. carpet that's not cheater
if(!Registry.traceIs(trace, 'carpet') || (trace.type === 'carpet' && !trace._cheater)) {
if(xaName) Lib.pushUnique(xaListNonCheater, xaName);
}
// The above check for definitely-not-cheater is not adequate. This
// second list tracks which axes *could* be a cheater so that the
// full condition triggering hiding is:
// *could* be a cheater and *is not definitely visible*
if(trace.type === 'carpet' && trace._cheater) {
if(xaName) Lib.pushUnique(xaListCheater, xaName);
}
// add axes implied by traces
if(xaName && listX.indexOf(xaName) === -1) listX.push(xaName);
if(yaName && listY.indexOf(yaName) === -1) listY.push(yaName);
// check for default formatting tweaks
if(Registry.traceIs(trace, '2dMap')) {
outerTicks[xaName] = true;
outerTicks[yaName] = true;
}
if(Registry.traceIs(trace, 'oriented')) {
var positionAxis = trace.orientation === 'h' ? yaName : xaName;
noGrids[positionAxis] = true;
}
}
// N.B. Ignore orphan axes (i.e. axes that have no data attached to them)
// if gl3d or geo is present on graph. This is retain backward compatible.
//
// TODO drop this in version 2.0
var ignoreOrphan = (layoutOut._has('gl3d') || layoutOut._has('geo'));
if(!ignoreOrphan) {
for(i = 0; i < layoutKeys.length; i++) {
var key = layoutKeys[i];
// orphan layout axes are considered cartesian subplots
if(xaListGl2d.indexOf(key) === -1 &&
xaListCartesian.indexOf(key) === -1 &&
constants.xAxisMatch.test(key)) {
xaListCartesian.push(key);
}
else if(yaListGl2d.indexOf(key) === -1 &&
yaListCartesian.indexOf(key) === -1 &&
constants.yAxisMatch.test(key)) {
yaListCartesian.push(key);
}
}
}
// make sure that plots with orphan cartesian axes
// are considered 'cartesian'
if(xaListCartesian.length && yaListCartesian.length) {
Lib.pushUnique(layoutOut._basePlotModules, Registry.subplotsRegistry.cartesian);
}
function axSort(a, b) {
var aNum = Number(a.substr(5) || 1),
bNum = Number(b.substr(5) || 1);
return aNum - bNum;
}
var xaList = xaListCartesian.concat(xaListGl2d).sort(axSort),
yaList = yaListCartesian.concat(yaListGl2d).sort(axSort),
axesList = xaList.concat(yaList);
// plot_bgcolor only makes sense if there's a (2D) plot!
// TODO: bgcolor for each subplot, to inherit from the main one
var plot_bgcolor = Color.background;
if(xaList.length && yaList.length) {
plot_bgcolor = Lib.coerce(layoutIn, layoutOut, basePlotLayoutAttributes, 'plot_bgcolor');
}
var bgColor = Color.combine(plot_bgcolor, layoutOut.paper_bgcolor);
var axName, axLetter, axLayoutIn, axLayoutOut;
function coerce(attr, dflt) {
return Lib.coerce(axLayoutIn, axLayoutOut, la ......
}
plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) {
var i, _module;
// can't be be part of basePlotModules loop
// in order to handle the orphan axes case
Plotly.Axes.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
// base plot module layout defaults
var basePlotModules = layoutOut._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
_module = basePlotModules[i];
// done above already
...swap = function (gd, traces) {
var axGroups = makeAxisGroups(gd, traces);
for(var i = 0; i < axGroups.length; i++) {
swapAxisGroup(gd, axGroups[i].x, axGroups[i].y);
}
}...
// all the other ones, just modify that one attribute
param.set(newVal);
}
}
// swap the data attributes of the relevant x and y axes?
if(['swapxyaxes', 'orientationaxes'].indexOf(ai) !== -1) {
Plotly.Axes.swap(gd, traces);
}
// swap hovermode if set to "compare x/y data"
if(ai === 'orientationaxes') {
var hovermode = Lib.nestedProperty(gd.layout, 'hovermode');
if(hovermode.get() === 'x') {
hovermode.set('y');
...tickFirst = function (ax) {
var r2l = ax.r2l || Number,
rng = Lib.simpleMap(ax.range, r2l),
axrev = rng[1] < rng[0],
sRound = axrev ? Math.floor : Math.ceil,
// add a tiny extra bit to make sure we get ticks
// that may have been rounded out
r0 = rng[0] * 1.0001 - rng[1] * 0.0001,
dtick = ax.dtick,
tick0 = r2l(ax.tick0);
if(isNumeric(dtick)) {
var tmin = sRound((r0 - tick0) / dtick) * dtick + tick0;
// make sure no ticks outside the category list
if(ax.type === 'category') {
tmin = Lib.constrain(tmin, 0, ax._categories.length - 1);
}
return tmin;
}
var tType = dtick.charAt(0),
dtNum = Number(dtick.substr(1));
// Dates: months (or years)
if(tType === 'M') {
var cnt = 0,
t0 = tick0,
t1,
mult,
newDTick;
// This algorithm should work for *any* nonlinear (but close to linear!)
// tick spacing. Limit to 10 iterations, for gregorian months it's normally <=3.
while(cnt < 10) {
t1 = axes.tickIncrement(t0, dtick, axrev, ax.calendar);
if((t1 - r0) * (t0 - r0) <= 0) {
// t1 and t0 are on opposite sides of r0! we've succeeded!
if(axrev) return Math.min(t0, t1);
return Math.max(t0, t1);
}
mult = (r0 - ((t0 + t1) / 2)) / (t1 - t0);
newDTick = tType + ((Math.abs(Math.round(mult)) || 1) * dtNum);
t0 = axes.tickIncrement(t0, newDTick, mult < 0 ? !axrev : axrev, ax.calendar);
cnt++;
}
Lib.error('tickFirst did not converge', ax);
return t0;
}
// Log scales: Linear, Digits
else if(tType === 'L') {
return Math.log(sRound(
(Math.pow(10, r0) - tick0) / dtNum) * dtNum + tick0) / Math.LN10;
}
else if(tType === 'D') {
var tickset = (dtick === 'D2') ? roundLog2 : roundLog1,
frac = Lib.roundUp(Lib.mod(r0, 1), tickset, axrev);
return Math.floor(r0) +
Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
}
else throw 'unrecognized dtick ' + String(dtick);
}...
calendar: calendar
};
}
axes.setConvert(dummyAx);
axes.autoTicks(dummyAx, size0);
var binStart = axes.tickIncrement(
axes.tickFirst(dummyAx), dummyAx.dtick, 'reverse', calendar),
binEnd;
// check for too many data points right at the edges of bins
// (>50% within 1% of bin edges) or all data points integral
// and offset the bins accordingly
if(typeof dummyAx.dtick === 'number') {
binStart = autoShiftNumericBins(binStart, data, dummyAx, dataMin, dataMax);
...tickIncrement = function (x, dtick, axrev, calendar) {
var axSign = axrev ? -1 : 1;
// includes linear, all dates smaller than month, and pure 10^n in log
if(isNumeric(dtick)) return x + axSign * dtick;
// everything else is a string, one character plus a number
var tType = dtick.charAt(0),
dtSigned = axSign * Number(dtick.substr(1));
// Dates: months (or years - see Lib.incrementMonth)
if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar);
// Log scales: Linear, Digits
else if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
// log10 of 2,5,10, or all digits (logs just have to be
// close enough to round)
else if(tType === 'D') {
var tickset = (dtick === 'D2') ? roundLog2 : roundLog1,
x2 = x + axSign * 0.01,
frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev);
return Math.floor(x2) +
Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
}
else throw 'unrecognized dtick ' + String(dtick);
}...
range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar),
calendar: calendar
};
}
axes.setConvert(dummyAx);
axes.autoTicks(dummyAx, size0);
var binStart = axes.tickIncrement(
axes.tickFirst(dummyAx), dummyAx.dtick, 'reverse', calendar),
binEnd;
// check for too many data points right at the edges of bins
// (>50% within 1% of bin edges) or all data points integral
// and offset the bins accordingly
if(typeof dummyAx.dtick === 'number') {
...tickText = function (ax, x, hover) {
var out = tickTextObj(ax, x),
hideexp,
arrayMode = ax.tickmode === 'array',
extraPrecision = hover || arrayMode,
i,
tickVal2l = ax.type === 'category' ? ax.d2l_noadd : ax.d2l;
if(arrayMode && Array.isArray(ax.ticktext)) {
var rng = Lib.simpleMap(ax.range, ax.r2l),
minDiff = Math.abs(rng[1] - rng[0]) / 10000;
for(i = 0; i < ax.ticktext.length; i++) {
if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;
}
if(i < ax.ticktext.length) {
out.text = String(ax.ticktext[i]);
return out;
}
}
function isHidden(showAttr) {
var first_or_last;
if(showAttr === undefined) return true;
if(hover) return showAttr === 'none';
first_or_last = {
first: ax._tmin,
last: ax._tmax
}[showAttr];
return showAttr !== 'all' && x !== first_or_last;
}
hideexp = ax.exponentformat !== 'none' && isHidden(ax.showexponent) ? 'hide' : '';
if(ax.type === 'date') formatDate(ax, out, hover, extraPrecision);
else if(ax.type === 'log') formatLog(ax, out, hover, extraPrecision, hideexp);
else if(ax.type === 'category') formatCategory(ax, out);
else formatLinear(ax, out, hover, extraPrecision, hideexp);
// add prefix and suffix
if(ax.tickprefix && !isHidden(ax.showtickprefix)) out.text = ax.tickprefix + out.text;
if(ax.ticksuffix && !isHidden(ax.showticksuffix)) out.text += ax.ticksuffix;
return out;
}...
// latter part: ax._prevDateHead holds what we showed most recently.
// Start with it cleared and mark that we're in calcTicks (ie calculating a
// whole string of these so we should care what the previous date head was!)
ax._prevDateHead = '';
ax._inCalcTicks = true;
var ticksOut = new Array(vals.length);
for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]);
ax._inCalcTicks = false;
return ticksOut;
};
function arrayTicks(ax) {
...function cleanId(id, axLetter) {
if(!id.match(constants.AX_ID_PATTERN)) return;
if(axLetter && id.charAt(0) !== axLetter) return;
var axNum = id.substr(1).replace(/^0+/, '');
if(axNum === '1') axNum = '';
return id.charAt(0) + axNum;
}...
delete layout.yaxis1;
}
var axList = Axes.list({_fullLayout: layout});
for(i = 0; i < axList.length; i++) {
var ax = axList[i];
if(ax.anchor && ax.anchor !== 'free') {
ax.anchor = Axes.cleanId(ax.anchor);
}
if(ax.overlaying) ax.overlaying = Axes.cleanId(ax.overlaying);
// old method of axis type - isdate and islog (before category existed)
if(!ax.type) {
if(ax.isdate) ax.type = 'date';
else if(ax.islog) ax.type = 'log';
...getFromId = function (gd, id, type) {
var fullLayout = gd._fullLayout;
if(type === 'x') id = id.replace(/y[0-9]*/, '');
else if(type === 'y') id = id.replace(/x[0-9]*/, '');
return fullLayout[exports.id2name(id)];
}...
if(val !== undefined) p.set(val);
}
// for editing annotations or shapes - is it on autoscaled axes?
function refAutorange(obj, axLetter) {
if(!Lib.isPlainObject(obj)) return false;
var axRef = obj[axLetter + 'ref'] || axLetter,
ax = Plotly.Axes.getFromId(gd, axRef);
if(!ax && axRef.charAt(0) === axLetter) {
// fall back on the primary axis in case we've referenced a
// nonexistent axis (as we do above if axRef is missing).
// This assumes the object defaults to data referenced, which
// is the case for shapes and annotations but not for images.
// The only thing this is used for is to determine whether to
...getFromTrace = function (gd, fullTrace, type) {
var fullLayout = gd._fullLayout;
var ax = null;
if(Registry.traceIs(fullTrace, 'gl3d')) {
var scene = fullTrace.scene;
if(scene.substr(0, 5) === 'scene') {
ax = fullLayout[scene][type + 'axis'];
}
}
else {
ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type);
}
return ax;
}n/a
function id2name(id) {
if(typeof id !== 'string' || !id.match(constants.AX_ID_PATTERN)) return;
var axNum = id.substr(1);
if(axNum === '1') axNum = '';
return id.charAt(0) + 'axis' + axNum;
}...
});
// make a new empty vals array for undoit
function a0() { return traces.map(function() { return undefined; }); }
// for autoranging multiple axes
function addToAxlist(axid) {
var axName = Plotly.Axes.id2name(axid);
if(axlist.indexOf(axName) === -1) axlist.push(axName);
}
function autorangeAttr(axName) { return 'LAYOUT' + axName + '.autorange'; }
function rangeAttr(axName) { return 'LAYOUT' + axName + '.range'; }
...list = function (gd, axletter, only2d) {
return listNames(gd, axletter, only2d)
.map(function(axName) {
return Lib.nestedProperty(gd._fullLayout, axName).get();
});
}...
delete layout.xaxis1;
}
if(layout.yaxis1) {
if(!layout.yaxis) layout.yaxis = layout.yaxis1;
delete layout.yaxis1;
}
var axList = Axes.list({_fullLayout: layout});
for(i = 0; i < axList.length; i++) {
var ax = axList[i];
if(ax.anchor && ax.anchor !== 'free') {
ax.anchor = Axes.cleanId(ax.anchor);
}
if(ax.overlaying) ax.overlaying = Axes.cleanId(ax.overlaying);
...listIds = function (gd, axletter) {
return listNames(gd, axletter, true).map(exports.name2id);
}...
* dflt: the default to coerce to, or blank to use the first axis (falling back on
* extraOption if there is no axis)
* extraOption: aside from existing axes with this letter, what non-axis value is allowed?
* Only required if it's different from `dflt`
*/
axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption) {
var axLetter = attr.charAt(attr.length - 1),
axlist = axes.listIds(gd, axLetter),
refAttr = attr + 'ref',
attrDef = {};
if(!dflt) dflt = axlist[0] || extraOption;
if(!extraOption) extraOption = dflt;
// data-ref annotations are not supported in gl2d yet
...function name2id(name) {
if(!name.match(constants.AX_NAME_PATTERN)) return;
var axNum = name.substr(5);
if(axNum === '1') axNum = '';
return name.charAt(0) + axNum;
}...
// for constraint enforcement: keep track of all axes (as {id: name})
// we're editing the (auto)range of, so we can tell the others constrained
// to scale with them that it's OK for them to shrink
var rangesAltered = {};
function recordAlteredAxis(pleafPlus) {
var axId = axisIds.name2id(pleafPlus.split('.')[0]);
rangesAltered[axId] = 1;
}
// alter gd.layout
for(var ai in aobj) {
if(helpers.hasParent(aobj, ai)) {
throw new Error('cannot set ' + ai + 'and a parent attribute simultaneously');
...clean = function (newFullData, newFullLayout, oldFullData, oldFullLayout) {
var hadParcoords = (oldFullLayout._has && oldFullLayout._has('parcoords'));
var hasParcoords = (newFullLayout._has && newFullLayout._has('parcoords'));
if(hadParcoords && !hasParcoords) {
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove();
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove();
oldFullLayout._paperdiv.selectAll('.parcoords').remove();
oldFullLayout._paperdiv.selectAll('.parcoords').remove();
oldFullLayout._glimages.selectAll('*').remove();
}
}...
delete scene.cameraposition;
}
}
// sanitize rgb(fractions) and rgba(fractions) that old tinycolor
// supported, but new tinycolor does not because they're not valid css
Color.clean(layout);
return layout;
};
function cleanAxRef(container, attr) {
var valIn = container[attr],
axLetter = attr.charAt(0);
...plot = function (gd) {
var calcData = Plots.getSubplotCalcData(gd.calcdata, 'parcoords', 'parcoords');
if(calcData.length) parcoordsPlot(gd, calcData);
}...
fullLayout._infolayer.selectAll('.cb' + uid).remove();
}
}
// loop over the base plot modules present on graph
var basePlotModules = fullLayout._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
basePlotModules[i].plot(gd);
}
// keep reference to shape layers in subplots
var layerSubplot = fullLayout._paper.selectAll('.layer-subplot');
fullLayout._shapeSubplotLayers = layerSubplot.selectAll('.shapelayer');
// styling separate from drawing
...toSVG = function (gd) {
var imageRoot = gd._fullLayout._glimages;
var root = d3.selectAll('.svg-container');
var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
.selectAll('.parcoords-lines.context, .parcoords-lines.focus');
function canvasToImage(d) {
var canvas = this;
var imageData = canvas.toDataURL('image/png');
var image = imageRoot.append('svg:image');
var size = gd._fullLayout._size;
var domain = gd._fullData[d.model.key].domain;
image.attr({
xmlns: xmlnsNamespaces.svg,
'xlink:href': imageData,
x: size.l + size.w * domain.x[0] - c.overdrag,
y: size.t + size.h * (1 - domain.y[1]),
width: (domain.x[1] - domain.x[0]) * size.w + 2 * c.overdrag,
height: (domain.y[1] - domain.y[0]) * size.h,
preserveAspectRatio: 'none'
});
}
canvases.each(canvasToImage);
// Chrome / Safari bug workaround - browser apparently loses connection to the defined pattern
// Without the workaround, these browsers 'lose' the filter brush styling (color etc.) after a snapshot
// on a subsequent interaction.
// Firefox works fine without this workaround
window.setTimeout(function() {
d3.selectAll('#filterBarPattern')
.attr('id', 'filterBarPattern');
}, 60);
}...
// subplot-specific to-SVG methods
// which notably add the contents of the gl-container
// into the main svg node
var basePlotModules = fullLayout._basePlotModules || [];
for(i = 0; i < basePlotModules.length; i++) {
var _module = basePlotModules[i];
if(_module.toSVG) _module.toSVG(gd);
}
// add top items above them assumes everything in toppaper is either
// a group or a defs, and if it's empty (like hoverlayer) we can ignore it.
if(toppaper) {
var nodes = toppaper.node().childNodes;
...avg = function (n, i, size, counterData, counts) {
var v = counterData[i];
if(isNumeric(v)) {
v = Number(v);
size[n] += v;
counts[n]++;
}
return 0;
}n/a
count = function (n, i, size) {
size[n]++;
return 1;
}n/a
max = function (n, i, size, counterData) {
var v = counterData[i];
if(isNumeric(v)) {
v = Number(v);
if(!isNumeric(size[n])) {
size[n] = v;
return v;
}
else if(size[n] < v) {
var delta = v - size[n];
size[n] = v;
return delta;
}
}
return 0;
}...
if(e.preventDefault) e.preventDefault();
e.cancelBubble = true;
return false;
};
// constrain - restrict a number v to be between v0 and v1
lib.constrain = function(v, v0, v1) {
if(v0 > v1) return Math.max(v1, Math.min(v0, v));
return Math.max(v0, Math.min(v1, v));
};
/**
* do two bounding boxes from getBoundingClientRect,
* ie {left,right,top,bottom,width,height}, overlap?
* takes optional padding pixels
...min = function (n, i, size, counterData) {
var v = counterData[i];
if(isNumeric(v)) {
v = Number(v);
if(!isNumeric(size[n])) {
size[n] = v;
return v;
}
else if(size[n] > v) {
var delta = v - size[n];
size[n] = v;
return delta;
}
}
return 0;
}...
* d3's vocabulary:
* %{n}f where n is the max number of digits of fractional seconds
*/
var fracMatch = /%\d?f/g;
function modDateFormat(fmt, x, calendar) {
fmt = fmt.replace(fracMatch, function(match) {
var digits = Math.min(+(match.charAt(1)) || 6, 6),
fracSecs = ((x / 1000 % 1) + 2)
.toFixed(digits)
.substr(2).replace(/0+$/, '') || '0';
return fracSecs;
});
var d = new Date(Math.floor(x + 0.05));
...sum = function (n, i, size, counterData) {
var v = counterData[i];
if(isNumeric(v)) {
v = Number(v);
size[n] += v;
return v;
}
return 0;
}...
if (typeof keys === 'string') keys = keys.split('.');
var next = keys.shift();
return obj[next] && (!keys.length || objHasKeys(obj[next], keys));
};
µ.util.sumArrays = function(a, b) {
return d3.zip(a, b).map(function(d, i) {
return d3.sum(d);
});
};
µ.util.arrayLast = function(a) {
return a[a.length - 1];
};
...function getCal(calendar) {
var calendarObj = allCals[calendar];
if(calendarObj) return calendarObj;
calendarObj = allCals[calendar] = calendars.instance(calendar);
return calendarObj;
}n/a
handleDefaults = function (contIn, contOut, attr, dflt) {
var attrs = {};
attrs[attr] = attributes;
return Lib.coerce(contIn, contOut, attrs, attr, dflt);
}n/a
handleTraceDefaults = function (traceIn, traceOut, coords, layout) {
for(var i = 0; i < coords.length; i++) {
handleDefaults(traceIn, traceOut, coords[i] + 'calendar', layout.calendar);
}
}n/a
function worldCalFmt(fmt, x, calendar) {
var dateJD = Math.floor((x + 0.05) / ONEDAY) + EPOCHJD,
cDate = getCal(calendar).fromJD(dateJD),
i = 0,
modifier, directive, directiveLen, directiveObj, replacementPart;
while((i = fmt.indexOf('%', i)) !== -1) {
modifier = fmt.charAt(i + 1);
if(modifier === '0' || modifier === '-' || modifier === '_') {
directiveLen = 3;
directive = fmt.charAt(i + 2);
if(modifier === '_') modifier = '-';
}
else {
directive = modifier;
modifier = '0';
directiveLen = 2;
}
directiveObj = d3ToWorldCalendars[directive];
if(!directiveObj) {
i += directiveLen;
}
else {
// code is recognized as a date part but world-calendars doesn't support it
if(directiveObj === UNKNOWN) replacementPart = UNKNOWN;
// format the cDate according to the translated directive
else replacementPart = cDate.formatDate(directiveObj[modifier]);
fmt = fmt.substr(0, i) + replacementPart + fmt.substr(i + directiveLen);
i += replacementPart.length;
}
}
return fmt;
}n/a
function calc(gd, trace) {
var xa = Axes.getFromId(gd, trace.xaxis || 'x');
var ya = Axes.getFromId(gd, trace.yaxis || 'y');
var aax = trace.aaxis;
var bax = trace.baxis;
var a = trace._a = trace.a;
var b = trace._b = trace.b;
var t = {};
var x;
var y = trace.y;
if(trace._cheater) {
var avals = aax.cheatertype === 'index' ? a.length : a;
var bvals = bax.cheatertype === 'index' ? b.length : b;
trace.x = x = cheaterBasis(avals, bvals, trace.cheaterslope);
} else {
x = trace.x;
}
trace._x = trace.x = x = clean2dArray(x);
trace._y = trace.y = y = clean2dArray(y);
// Fill in any undefined values with elliptic smoothing. This doesn't take
// into account the spacing of the values. That is, the derivatives should
// be modified to use a and b values. It's not that hard, but this is already
// moderate overkill for just filling in missing values.
smoothFill2dArray(x, a, b);
smoothFill2dArray(y, a, b);
// create conversion functions that depend on the data
trace.setScale();
// Convert cartesian-space x/y coordinates to screen space pixel coordinates:
t.xp = trace.xp = map2dArray(trace.xp, x, xa.c2p);
t.yp = trace.yp = map2dArray(trace.yp, y, ya.c2p);
// This is a rather expensive scan. Nothing guarantees monotonicity,
// so we need to scan through all data to get proper ranges:
var xrange = arrayMinmax(x);
var yrange = arrayMinmax(y);
var dx = 0.5 * (xrange[1] - xrange[0]);
var xc = 0.5 * (xrange[1] + xrange[0]);
var dy = 0.5 * (yrange[1] - yrange[0]);
var yc = 0.5 * (yrange[1] + yrange[0]);
// Expand the axes to fit the plot, except just grow it by a factor of 1.3
// because the labels should be taken into account except that's difficult
// hence 1.3.
var grow = 1.3;
xrange = [xc - dx * grow, xc + dx * grow];
yrange = [yc - dy * grow, yc + dy * grow];
Axes.expand(xa, xrange, {padded: true});
Axes.expand(ya, yrange, {padded: true});
// Enumerate the gridlines, both major and minor, and store them on the trace
// object:
calcGridlines(trace, t, 'a', 'b');
calcGridlines(trace, t, 'b', 'a');
// Calculate the text labels for each major gridline and store them on the
// trace object:
calcLabels(trace, aax);
calcLabels(trace, bax);
// Tabulate points for the four segments that bound the axes so that we can
// map to pixel coordinates in the plot function and create a clip rect:
t.clipsegments = calcClipPath(trace.xctrl, trace.yctrl, aax, bax);
t.x = x;
t.y = y;
t.a = a;
t.b = b;
return [t];
}...
for(var j = 0; j < modules.length; j++) {
_module = modules[j];
if(_module.setPositions) _module.setPositions(gd, subplotInfo);
}
}
// calc and autorange for errorbars
ErrorBars.calc(gd);
// TODO: autosize extra for text markers and images
// see https://github.com/plotly/plotly.js/issues/1111
return Lib.syncOrAsync([
Registry.getComponentMethod('shapes', 'calcAutorange'),
Registry.getComponentMethod('annotations', 'calcAutorange'),
doAutoRangeAndConstraints,
...function plot(gd, plotinfo, cdcarpet) {
for(var i = 0; i < cdcarpet.length; i++) {
plotOne(gd, plotinfo, cdcarpet[i]);
}
}...
fullLayout._infolayer.selectAll('.cb' + uid).remove();
}
}
// loop over the base plot modules present on graph
var basePlotModules = fullLayout._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
basePlotModules[i].plot(gd);
}
// keep reference to shape layers in subplots
var layerSubplot = fullLayout._paper.selectAll('.layer-subplot');
fullLayout._shapeSubplotLayers = layerSubplot.selectAll('.shapelayer');
// styling separate from drawing
...function supplyDefaults(traceIn, traceOut, dfltColor, fullLayout) {
function coerce(attr, dflt) {
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
}
var defaultColor = coerce('color', colorAttrs.defaultLine);
Lib.coerceFont(coerce, 'font');
coerce('carpet');
handleABDefaults(traceIn, traceOut, fullLayout, coerce, defaultColor);
if(!traceOut.a || !traceOut.b) {
traceOut.visible = false;
return;
}
if(traceOut.a.length < 3) {
traceOut.aaxis.smoothing = 0;
}
if(traceOut.b.length < 3) {
traceOut.baxis.smoothing = 0;
}
// NB: the input is x/y arrays. You should know that the *first* dimension of x and y
// corresponds to b and the second to a. This sounds backwards but ends up making sense
// the important part to know is that when you write y[j][i], j goes from 0 to b.length - 1
// and i goes from 0 to a.length - 1.
var len = handleXYDefaults(traceIn, traceOut, coerce);
setConvert(traceOut);
if(traceOut._cheater) {
coerce('cheaterslope');
}
if(!len) {
traceOut.visible = false;
return;
}
}...
gd._replotPending = true;
return Promise.reject();
} else {
// we're going ahead with a replot now
gd._replotPending = false;
}
Plots.supplyDefaults(gd);
var fullLayout = gd._fullLayout;
// Polar plots
if(data && data[0] && data[0].r) return plotPolar(gd, data, layout);
// so we don't try to re-call Plotly.plot from inside
...function hasClickToShow(gd, hoverData) {
var sets = getToggleSets(gd, hoverData);
return sets.on.length > 0 || sets.explicitOff.length > 0;
}n/a
function onClick(gd, hoverData) {
var toggleSets = getToggleSets(gd, hoverData),
onSet = toggleSets.on,
offSet = toggleSets.off.concat(toggleSets.explicitOff),
update = {},
i;
if(!(onSet.length || offSet.length)) return;
for(i = 0; i < onSet.length; i++) {
update['annotations[' + onSet[i] + '].visible'] = true;
}
for(i = 0; i < offSet.length; i++) {
update['annotations[' + offSet[i] + '].visible'] = false;
}
return Plotly.update(gd, {}, update);
}n/a
coerce = function (containerIn, containerOut, attributes, attribute, dflt) {
var opts = nestedProperty(attributes, attribute).get(),
propIn = nestedProperty(containerIn, attribute),
propOut = nestedProperty(containerOut, attribute),
v = propIn.get();
if(dflt === undefined) dflt = opts.dflt;
/**
* arrayOk: value MAY be an array, then we do no value checking
* at this point, because it can be more complicated than the
* individual form (eg. some array vals can be numbers, even if the
* single values must be color strings)
*/
if(opts.arrayOk && Array.isArray(v)) {
propOut.set(v);
return v;
}
exports.valObjects[opts.valType].coerceFunction(v, propOut, dflt, opts);
return propOut.get();
}...
}
var items = opts.items,
vOut = [];
dflt = Array.isArray(dflt) ? dflt : [];
for(var i = 0; i < items.length; i++) {
exports.coerce(v, vOut, items, '[' + i + ']', dflt[i]);
}
propOut.set(vOut);
},
validateFunction: function(v, opts) {
if(!Array.isArray(v)) return false;
...coerce2 = function (containerIn, containerOut, attributes, attribute, dflt) {
var propIn = nestedProperty(containerIn, attribute),
propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt),
valIn = propIn.get();
return (valIn !== undefined && valIn !== null) ? propOut : false;
}...
var letter = options.letter,
font = options.font || {},
defaultTitle = 'Click to enter ' +
(options.title || (letter.toUpperCase() + ' axis')) +
' title';
function coerce2(attr, dflt) {
return Lib.coerce2(containerIn, containerOut, layoutAttributes, attr, dflt);
}
var visible = coerce('visible', !options.cheateronly);
var axType = containerOut.type;
if(axType === 'date') {
...coerceFont = function (coerce, attr, dfltObj) {
var out = {};
dfltObj = dfltObj || {};
out.family = coerce(attr + '.family', dfltObj.family);
out.size = coerce(attr + '.size', dfltObj.size);
out.color = coerce(attr + '.color', dfltObj.color);
return out;
}...
}
plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) {
function coerce(attr, dflt) {
return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt);
}
var globalFont = Lib.coerceFont(coerce, 'font');
coerce('title');
Lib.coerceFont(coerce, 'titlefont', {
family: globalFont.family,
size: Math.round(globalFont.size * 1.4),
color: globalFont.color
...validate = function (value, opts) {
var valObject = exports.valObjects[opts.valType];
if(opts.arrayOk && Array.isArray(value)) return true;
if(valObject.validateFunction) {
return valObject.validateFunction(value, opts);
}
var failed = {},
out = failed,
propMock = { set: function(v) { out = v; } };
// 'failed' just something mutable that won't be === anything else
valObject.coerceFunction(value, propMock, failed, opts);
return out !== failed;
}...
var items = opts.items;
// when free length is off, input and declared lengths must match
if(!opts.freeLength && v.length !== items.length) return false;
// valid when all input items are valid
for(var i = 0; i < v.length; i++) {
var isItemValid = exports.validate(v[i], opts.items[i]);
if(!isItemValid) return false;
}
return true;
}
}
...computeAPICommandBindings = function (gd, method, args) {
var bindings;
if(!Array.isArray(args)) args = [];
switch(method) {
case 'restyle':
bindings = computeDataBindings(gd, args);
break;
case 'relayout':
bindings = computeLayoutBindings(gd, args);
break;
case 'update':
bindings = computeDataBindings(gd, [args[0], args[2]])
.concat(computeLayoutBindings(gd, [args[1]]));
break;
case 'animate':
bindings = computeAnimateBindings(gd, args);
break;
default:
// This is the case where intelligent logic about what affects
// this command is not implemented. It causes no ill effects.
// For example, addFrames simply won't bind to a control component.
bindings = [];
}
return bindings;
}...
if(!Array.isArray(args)) args = [];
// If any command has no method, refuse to bind:
if(!method) {
return false;
}
var bindings = exports.computeAPICommandBindings(gd, method, args);
// Right now, handle one and *only* one property being set:
if(bindings.length !== 1) {
return false;
}
if(!refBinding) {
...executeAPICommand = function (gd, method, args) {
var apiMethod = Plotly[method];
var allArgs = [gd];
if(!Array.isArray(args)) args = [];
for(var i = 0; i < args.length; i++) {
allArgs.push(args[i]);
}
return apiMethod.apply(null, allArgs).catch(function(err) {
Lib.warn('API call to Plotly.' + method + ' rejected.', err);
return Promise.reject(err);
});
}n/a
hasSimpleAPICommandBindings = function (gd, commandList, bindingsByValue) {
var i;
var n = commandList.length;
var refBinding;
for(i = 0; i < n; i++) {
var binding;
var command = commandList[i];
var method = command.method;
var args = command.args;
if(!Array.isArray(args)) args = [];
// If any command has no method, refuse to bind:
if(!method) {
return false;
}
var bindings = exports.computeAPICommandBindings(gd, method, args);
// Right now, handle one and *only* one property being set:
if(bindings.length !== 1) {
return false;
}
if(!refBinding) {
refBinding = bindings[0];
if(Array.isArray(refBinding.traces)) {
refBinding.traces.sort();
}
} else {
binding = bindings[0];
if(binding.type !== refBinding.type) {
return false;
}
if(binding.prop !== refBinding.prop) {
return false;
}
if(Array.isArray(refBinding.traces)) {
if(Array.isArray(binding.traces)) {
binding.traces.sort();
for(var j = 0; j < refBinding.traces.length; j++) {
if(refBinding.traces[j] !== binding.traces[j]) {
return false;
}
}
} else {
return false;
}
} else {
if(binding.prop !== refBinding.prop) {
return false;
}
}
}
binding = bindings[0];
var value = binding.value;
if(Array.isArray(value)) {
if(value.length === 1) {
value = value[0];
} else {
return false;
}
}
if(bindingsByValue) {
bindingsByValue[value] = i;
}
}
return refBinding;
}...
if(!ret.cache) {
ret.cache = {};
}
// Either create or just recompute this:
ret.lookupTable = {};
var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
if(container && container._commandObserver) {
if(!binding) {
// If container exists and there are no longer any bindings,
// remove existing:
if(container._commandObserver.remove) {
container._commandObserver.remove();
...manageCommandObserver = function (gd, container, commandList, onchange) {
var ret = {};
var enabled = true;
if(container && container._commandObserver) {
ret = container._commandObserver;
}
if(!ret.cache) {
ret.cache = {};
}
// Either create or just recompute this:
ret.lookupTable = {};
var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
if(container && container._commandObserver) {
if(!binding) {
// If container exists and there are no longer any bindings,
// remove existing:
if(container._commandObserver.remove) {
container._commandObserver.remove();
container._commandObserver = null;
return ret;
}
} else {
// If container exists and there *are* bindings, then the lookup
// table should have been updated and check is already attached,
// so there's nothing to be done:
return ret;
}
}
// Determine whether there's anything to do for this binding:
if(binding) {
// Build the cache:
bindingValueHasChanged(gd, binding, ret.cache);
ret.check = function check() {
if(!enabled) return;
var update = bindingValueHasChanged(gd, binding, ret.cache);
if(update.changed && onchange) {
// Disable checks for the duration of this command in order to avoid
// infinite loops:
if(ret.lookupTable[update.value] !== undefined) {
ret.disable();
Promise.resolve(onchange({
value: update.value,
type: binding.type,
prop: binding.prop,
traces: binding.traces,
index: ret.lookupTable[update.value]
})).then(ret.enable, ret.enable);
}
}
return update.changed;
};
var checkEvents = [
'plotly_relayout',
'plotly_redraw',
'plotly_restyle',
'plotly_update',
'plotly_animatingframe',
'plotly_afterplot'
];
for(var i = 0; i < checkEvents.length; i++) {
gd._internalOn(checkEvents[i], ret.check);
}
ret.remove = function() {
for(var i = 0; i < checkEvents.length; i++) {
gd._removeInternalListener(checkEvents[i], ret.check);
}
};
} else {
// TODO: It'd be really neat to actually give a *reason* for this, but at least a warning
// is a start
Lib.warn('Unable to automatically bind plot updates to API command');
ret.lookupTable = {};
ret.remove = function() {};
}
ret.disable = function disable() {
enabled = false;
};
ret.enable = function enable() {
enabled = true;
};
if(container) {
container._commandObserver = ret;
}
return ret;
}n/a
doesDirExist = function (dirPath) {
try {
if(fs.statSync(dirPath).isDirectory()) return true;
}
catch(e) {
return false;
}
return false;
}...
logger('make build/credentials.json');
}
// Make artifact folders for image tests
function makeTestImageFolders() {
function makeOne(folderPath, info) {
if(!common.doesDirExist(folderPath)) {
fs.mkdirSync(folderPath);
logger('initialize ' + info);
}
else logger(info + ' is present');
}
makeOne(constants.pathToTestImages, 'test image folder');
...doesFileExist = function (filePath) {
try {
if(fs.statSync(filePath).isFile()) return true;
}
catch(e) {
return false;
}
return false;
}...
date.toLocaleDateString(),
date.toLocaleTimeString(),
date.toString().match(/\(([A-Za-z\s].*)\)/)[1]
].join(' ');
};
exports.getTimeLastModified = function(filePath) {
if(!exports.doesFileExist(filePath)) {
throw new Error(filePath + ' does not exist');
}
var stats = fs.statSync(filePath),
formattedTime = exports.formatTime(stats.mtime);
return formattedTime;
...execCmd = function (cmd, cb, errorCb) {
cb = cb ? cb : function() {};
errorCb = errorCb ? errorCb : function(err) { if(err) throw err; };
exec(cmd, function(err) {
errorCb(err);
cb();
})
.stdout.pipe(process.stdout);
}...
case 'run':
msg = 'Booting up ' + constants.testContainerName + ' docker container';
cmd = containerCommands.dockerRun;
// if docker-run fails, try docker-start.
errorCb = function(err) {
if(err) common.execCmd('docker start ' + constants.testContainerName
);
};
break;
case 'setup':
msg = 'Setting up ' + constants.testContainerName + ' docker container for testing';
cmd = containerCommands.getRunCmd(isCI, containerCommands.setup);
...formatTime = function (date) {
return [
date.toLocaleDateString(),
date.toLocaleTimeString(),
date.toString().match(/\(([A-Za-z\s].*)\)/)[1]
].join(' ');
}...
exports.getTimeLastModified = function(filePath) {
if(!exports.doesFileExist(filePath)) {
throw new Error(filePath + ' does not exist');
}
var stats = fs.statSync(filePath),
formattedTime = exports.formatTime(stats.mtime);
return formattedTime;
};
exports.touch = function(filePath) {
fs.closeSync(fs.openSync(filePath, 'w'));
};
...getTimeLastModified = function (filePath) {
if(!exports.doesFileExist(filePath)) {
throw new Error(filePath + ' does not exist');
}
var stats = fs.statSync(filePath),
formattedTime = exports.formatTime(stats.mtime);
return formattedTime;
}n/a
throwOnError = function (err) {
if(err) throw err;
}n/a
touch = function (filePath) {
fs.closeSync(fs.openSync(filePath, 'w'));
}n/a
writeFile = function (filePath, content, cb) {
fs.writeFile(filePath, content, function(err) {
if(err) throw err;
if(cb) cb();
});
}...
// Create a credentials json file,
// to be required in jasmine test suites and test dashboard
function makeCredentialsFile() {
var credentials = JSON.stringify({
MAPBOX_ACCESS_TOKEN: constants.mapboxAccessToken
}, null, 2);
common.writeFile(constants.pathToCredentials, credentials);
logger('make build/credentials.json');
}
// Make artifact folders for image tests
function makeTestImageFolders() {
function makeOne(folderPath, info) {
...getRunCmd = function (isCI, commands) {
var _commands = Array.isArray(commands) ? commands.slice() : [commands];
if(isCI) return getRunCI(_commands);
// add setup commands locally
_commands = [containerCommands.setup].concat(_commands);
return getRunLocal(_commands);
}...
if(err) common.execCmd('docker start ' + constants.testContainerName);
};
break;
case 'setup':
msg = 'Setting up ' + constants.testContainerName + ' docker container for testing';
cmd = containerCommands.getRunCmd(isCI, containerCommands.setup);
break;
case 'stop':
msg = 'Stopping ' + constants.testContainerName + ' docker container';
cmd = 'docker stop ' + constants.testContainerName;
break;
...cleanDate = function (v, dflt, calendar) {
if(exports.isJSDate(v) || typeof v === 'number') {
// do not allow milliseconds (old) or jsdate objects (inherently
// described as gregorian dates) with world calendars
if(isWorldCalendar(calendar)) {
logError('JS Dates and milliseconds are incompatible with world calendars', v);
return dflt;
}
// NOTE: if someone puts in a year as a number rather than a string,
// this will mistakenly convert it thinking it's milliseconds from 1970
// that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
v = exports.ms2DateTimeLocal(+v);
if(!v && dflt !== undefined) return dflt;
}
else if(!exports.isDateTime(v, calendar)) {
logError('unrecognized date', v);
return dflt;
}
return v;
}...
if(typeof pos === 'string' && (ax._categories || []).length) {
newPos = ax._categories.indexOf(pos);
containerOut[attr] = (newPos === -1) ? dflt : newPos;
return;
}
}
else if(ax.type === 'date') {
containerOut[attr] = Lib.cleanDate(pos, BADNUM, ax.calendar);
return;
}
}
// finally make sure we have a number (unless date type already returned a string)
containerOut[attr] = isNumeric(pos) ? Number(pos) : dflt;
};
...dateTick0 = function (calendar, sunday) {
if(isWorldCalendar(calendar)) {
return sunday ?
Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] :
Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
}
else {
return sunday ? '2000-01-02' : '2000-01-01';
}
}...
// log with linear ticks: L# where # is the linear tick spacing
// log showing powers plus some intermediates:
// D1 shows all digits, D2 shows 2 and 5
axes.autoTicks = function(ax, roughDTick) {
var base;
if(ax.type === 'date') {
ax.tick0 = Lib.dateTick0(ax.calendar);
// the criteria below are all based on the rough spacing we calculate
// being > half of the final unit - so precalculate twice the rough val
var roughX2 = 2 * roughDTick;
if(roughX2 > ONEAVGYEAR) {
roughDTick /= ONEAVGYEAR;
base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10));
...dateTime2ms = function (s, calendar) {
// first check if s is a date object
if(exports.isJSDate(s)) {
// Convert to the UTC milliseconds that give the same
// hours as this date has in the local timezone
s = Number(s) - s.getTimezoneOffset() * ONEMIN;
if(s >= MIN_MS && s <= MAX_MS) return s;
return BADNUM;
}
// otherwise only accept strings and numbers
if(typeof s !== 'string' && typeof s !== 'number') return BADNUM;
s = String(s);
var isWorld = isWorldCalendar(calendar);
// to handle out-of-range dates in international calendars, accept
// 'G' as a prefix to force the built-in gregorian calendar.
var s0 = s.charAt(0);
if(isWorld && (s0 === 'G' || s0 === 'g')) {
s = s.substr(1);
calendar = '';
}
var isChinese = isWorld && calendar.substr(0, 7) === 'chinese';
var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP);
if(!match) return BADNUM;
var y = match[1],
m = match[3] || '1',
d = Number(match[5] || 1),
H = Number(match[7] || 0),
M = Number(match[9] || 0),
S = Number(match[11] || 0);
if(isWorld) {
// disallow 2-digit years for world calendars
if(y.length === 2) return BADNUM;
y = Number(y);
var cDate;
try {
var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
if(isChinese) {
var isIntercalary = m.charAt(m.length - 1) === 'i';
m = parseInt(m, 10);
cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d);
}
else {
cDate = calInstance.newDate(y, Number(m), d);
}
}
catch(e) { return BADNUM; } // Invalid ... date
if(!cDate) return BADNUM;
return ((cDate.toJD() - EPOCHJD) * ONEDAY) +
(H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC);
}
if(y.length === 2) {
y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST;
}
else y = Number(y);
// new Date uses months from 0; subtract 1 here just so we
// don't have to do it again during the validity test below
m -= 1;
// javascript takes new Date(0..99,m,d) to mean 1900-1999, so
// to support years 0-99 we need to use setFullYear explicitly
// Note that 2000 is a leap year.
var date = new Date(Date.UTC(2000, m, d, H, M));
date.setUTCFullYear(y);
if(date.getUTCMonth() !== m) return BADNUM;
if(date.getUTCDate() !== d) return BADNUM;
return date.getTime() + S * ONESEC;
}...
if(date.getUTCMonth() !== m) return BADNUM;
if(date.getUTCDate() !== d) return BADNUM;
return date.getTime() + S * ONESEC;
};
MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999');
MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999');
// is string s a date? (see above)
exports.isDateTime = function(s, calendar) {
return (exports.dateTime2ms(s, calendar) !== BADNUM);
};
...dfltRange = function (calendar) {
if(isWorldCalendar(calendar)) {
return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar];
}
else {
return ['2000-01-01', '2001-01-01'];
}
}...
*/
ax.cleanRange = function(rangeAttr) {
if(!rangeAttr) rangeAttr = 'range';
var range = Lib.nestedProperty(ax, rangeAttr).get(),
axLetter = (ax._id || 'x').charAt(0),
i, dflt;
if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar);
else if(axLetter === 'y') dflt = constants.DFLTRANGEY;
else dflt = constants.DFLTRANGEX;
// make sure we don't later mutate the defaults
dflt = dflt.slice();
if(!range || range.length !== 2) {
...findExactDates = function (data, calendar) {
var exactYears = 0,
exactMonths = 0,
exactDays = 0,
blankCount = 0,
d,
di;
var calInstance = (
isWorldCalendar(calendar) &&
Registry.getComponentMethod('calendars', 'getCal')(calendar)
);
for(var i = 0; i < data.length; i++) {
di = data[i];
// not date data at all
if(!isNumeric(di)) {
blankCount ++;
continue;
}
// not an exact date
if(di % ONEDAY) continue;
if(calInstance) {
try {
d = calInstance.fromJD(di / ONEDAY + EPOCHJD);
if(d.day() === 1) {
if(d.month() === 1) exactYears++;
else exactMonths++;
}
else exactDays++;
}
catch(e) {
// invalid date in this calendar - ignore it here.
}
}
else {
d = new Date(di);
if(d.getUTCDate() === 1) {
if(d.getUTCMonth() === 0) exactYears++;
else exactMonths++;
}
else exactDays++;
}
}
exactMonths += exactYears;
exactDays += exactMonths;
var dataCount = data.length - blankCount;
return {
exactYears: exactYears / dataCount,
exactMonths: exactMonths / dataCount,
exactDays: exactDays / dataCount
};
}...
}
}
return binStart;
}
function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
var stats = Lib.findExactDates(data, calendar);
// number of data points that needs to be an exact value
// to shift that increment to (near) the bin center
var threshold = 0.8;
if(stats.exactDays > threshold) {
var numMonths = Number(dtick.substr(1));
...formatDate = function (x, fmt, tr, calendar) {
var headStr,
dateStr;
calendar = isWorldCalendar(calendar) && calendar;
if(fmt) return modDateFormat(fmt, x, calendar);
if(calendar) {
try {
var dateJD = Math.floor((x + 0.05) / ONEDAY) + EPOCHJD,
cDate = Registry.getComponentMethod('calendars', 'getCal')(calendar)
.fromJD(dateJD);
if(tr === 'y') dateStr = yearFormatWorld(cDate);
else if(tr === 'm') dateStr = monthFormatWorld(cDate);
else if(tr === 'd') {
headStr = yearFormatWorld(cDate);
dateStr = dayFormatWorld(cDate);
}
else {
headStr = yearMonthDayFormatWorld(cDate);
dateStr = formatTime(x, tr);
}
}
catch(e) { return 'Invalid'; }
}
else {
var d = new Date(Math.floor(x + 0.05));
if(tr === 'y') dateStr = yearFormat(d);
else if(tr === 'm') dateStr = monthFormat(d);
else if(tr === 'd') {
headStr = yearFormat(d);
dateStr = dayFormat(d);
}
else {
headStr = yearMonthDayFormat(d);
dateStr = formatTime(x, tr);
}
}
return dateStr + (headStr ? '\n' + headStr : '');
}...
dateStr, h, m, s, msec10, d;
if(isWorldCalendar(calendar)) {
var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD,
timeMs = Math.floor(mod(ms, ONEDAY));
try {
dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
.fromJD(dateJD).formatDate('yyyy-mm-dd');
}
catch(e) {
// invalid date in this calendar - fall back to Gyyyy-mm-dd
dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
}
// yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
...incrementMonth = function (ms, dMonth, calendar) {
calendar = isWorldCalendar(calendar) && calendar;
// pull time out and operate on pure dates, then add time back at the end
// this gives maximum precision - not that we *normally* care if we're
// incrementing by month, but better to be safe!
var timeMs = mod(ms, ONEDAY);
ms = Math.round(ms - timeMs);
if(calendar) {
try {
var dateJD = Math.round(ms / ONEDAY) + EPOCHJD,
calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar),
cDate = calInstance.fromJD(dateJD);
if(dMonth % 12) calInstance.add(cDate, dMonth, 'm');
else calInstance.add(cDate, dMonth / 12, 'y');
return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs;
}
catch(e) {
logError('invalid ms ' + ms + ' in calendar ' + calendar);
// then keep going in gregorian even though the result will be 'Invalid'
}
}
var y = new Date(ms + THREEDAYS);
return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS;
}...
if(isNumeric(dtick)) return x + axSign * dtick;
// everything else is a string, one character plus a number
var tType = dtick.charAt(0),
dtSigned = axSign * Number(dtick.substr(1));
// Dates: months (or years - see Lib.incrementMonth)
if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar);
// Log scales: Linear, Digits
else if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
// log10 of 2,5,10, or all digits (logs just have to be
// close enough to round)
else if(tType === 'D') {
...isDateTime = function (s, calendar) {
return (exports.dateTime2ms(s, calendar) !== BADNUM);
}...
// NOTE: if someone puts in a year as a number rather than a string,
// this will mistakenly convert it thinking it's milliseconds from 1970
// that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
v = exports.ms2DateTimeLocal(+v);
if(!v && dflt !== undefined) return dflt;
}
else if(!exports.isDateTime(v, calendar)) {
logError('unrecognized date', v);
return dflt;
}
return v;
};
/*
...isJSDate = function (v) {
return typeof v === 'object' && v !== null && typeof v.getTime === 'function';
}...
* make now will cover all possibilities. mostly this will all be taken
* care of in initial parsing, should only be an issue for hand-entered data
* currently (2016) this range is:
* 1946-2045
*/
exports.dateTime2ms = function(s, calendar) {
// first check if s is a date object
if(exports.isJSDate(s)) {
// Convert to the UTC milliseconds that give the same
// hours as this date has in the local timezone
s = Number(s) - s.getTimezoneOffset() * ONEMIN;
if(s >= MIN_MS && s <= MAX_MS) return s;
return BADNUM;
}
// otherwise only accept strings and numbers
...ms2DateTime = function (ms, r, calendar) {
if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM;
if(!r) r = 0;
var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10),
msRounded = Math.round(ms - msecTenths / 10),
dateStr, h, m, s, msec10, d;
if(isWorldCalendar(calendar)) {
var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD,
timeMs = Math.floor(mod(ms, ONEDAY));
try {
dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
.fromJD(dateJD).formatDate('yyyy-mm-dd');
}
catch(e) {
// invalid date in this calendar - fall back to Gyyyy-mm-dd
dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
}
// yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
// other things for a few calendars, so we can't trust it. Just pad
// it manually (after the '-' if there is one)
if(dateStr.charAt(0) === '-') {
while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1);
}
else {
while(dateStr.length < 10) dateStr = '0' + dateStr;
}
// TODO: if this is faster, we could use this block for extracting
// the time components of regular gregorian too
h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0;
m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0;
s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0;
msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0;
}
else {
d = new Date(msRounded);
dateStr = utcFormat('%Y-%m-%d')(d);
// <90 days: add hours and minutes - never *only* add hours
h = (r < NINETYDAYS) ? d.getUTCHours() : 0;
m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0;
// <3 hours: add seconds
s = (r < THREEHOURS) ? d.getUTCSeconds() : 0;
// <5 minutes: add ms (plus one extra digit, this is msec*10)
msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0;
}
return includeTime(dateStr, h, m, s, msec10);
}n/a
ms2DateTimeLocal = function (ms) {
if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM;
var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10),
d = new Date(Math.round(ms - msecTenths / 10)),
dateStr = d3.time.format('%Y-%m-%d')(d),
h = d.getHours(),
m = d.getMinutes(),
s = d.getSeconds(),
msec10 = d.getUTCMilliseconds() * 10 + msecTenths;
return includeTime(dateStr, h, m, s, msec10);
}...
logError('JS Dates and milliseconds are incompatible with world calendars', v);
return dflt;
}
// NOTE: if someone puts in a year as a number rather than a string,
// this will mistakenly convert it thinking it's milliseconds from 1970
// that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
v = exports.ms2DateTimeLocal(+v);
if(!v && dflt !== undefined) return dflt;
}
else if(!exports.isDateTime(v, calendar)) {
logError('unrecognized date', v);
return dflt;
}
return v;
...function draw(gd) {
var fullLayout = gd._fullLayout;
fullLayout._infolayer.selectAll('.annotation').remove();
for(var i = 0; i < fullLayout.annotations.length; i++) {
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}
return Plots.previousPromises(gd);
}...
return gd._promises.length && Promise.all(gd._promises);
};
exports.drawMainTitle = function(gd) {
var fullLayout = gd._fullLayout;
Titles.draw(gd, 'gtitle', {
propContainer: fullLayout,
propName: 'title',
dfltName: 'Plot',
attributes: {
x: fullLayout.width / 2,
y: fullLayout._size.t / 2,
'text-anchor': 'middle'
...function drawOne(gd, index) {
var layout = gd.layout,
fullLayout = gd._fullLayout,
gs = gd._fullLayout._size;
// remove the existing annotation if there is one
fullLayout._infolayer.selectAll('.annotation[data-index="' + index + '"]').remove();
// remember a few things about what was already there,
var optionsIn = (layout.annotations || [])[index],
options = fullLayout.annotations[index];
var annClipID = 'clip' + fullLayout._uid + '_ann' + index;
// this annotation is gone - quit now after deleting it
// TODO: use d3 idioms instead of deleting and redrawing every time
if(!optionsIn || options.visible === false) {
d3.selectAll('#' + annClipID).remove();
return;
}
var xa = Axes.getFromId(gd, options.xref),
ya = Axes.getFromId(gd, options.yref),
// calculated pixel positions
// x & y each will get text, head, and tail as appropriate
annPosPx = {x: {}, y: {}},
textangle = +options.textangle || 0;
// create the components
// made a single group to contain all, so opacity can work right
// with border/arrow together this could handle a whole bunch of
// cleanup at this point, but works for now
var annGroup = fullLayout._infolayer.append('g')
.classed('annotation', true)
.attr('data-index', String(index))
.style('opacity', options.opacity);
// another group for text+background so that they can rotate together
var annTextGroup = annGroup.append('g')
.classed('annotation-text-g', true)
.attr('data-index', String(index));
var annTextGroupInner = annTextGroup.append('g')
.style('pointer-events', options.captureevents ? 'all' : null)
.call(setCursor, 'default')
.on('click', function() {
gd._dragging = false;
gd.emit('plotly_clickannotation', {
index: index,
annotation: optionsIn,
fullAnnotation: options
});
});
if(options.hovertext) {
annTextGroupInner
.on('mouseover', function() {
var hoverOptions = options.hoverlabel;
var hoverFont = hoverOptions.font;
var bBox = this.getBoundingClientRect();
var bBoxRef = gd.getBoundingClientRect();
Fx.loneHover({
x0: bBox.left - bBoxRef.left,
x1: bBox.right - bBoxRef.left,
y: (bBox.top + bBox.bottom) / 2 - bBoxRef.top,
text: options.hovertext,
color: hoverOptions.bgcolor,
borderColor: hoverOptions.bordercolor,
fontFamily: hoverFont.family,
fontSize: hoverFont.size,
fontColor: hoverFont.color
}, {
container: fullLayout._hoverlayer.node(),
outerContainer: fullLayout._paper.node()
});
})
.on('mouseout', function() {
Fx.loneUnhover(fullLayout._hoverlayer.node());
});
}
var borderwidth = options.borderwidth,
borderpad = options.borderpad,
borderfull = borderwidth + borderpad;
var annTextBG = annTextGroupInner.append('rect')
.attr('class', 'bg')
.style('stroke-width', borderwidth + 'px')
.call(Color.stroke, options.bordercolor)
.call(Color.fill, options.bgcolor);
var isSizeConstrained = options.width || options.height;
var annTextClip = fullLayout._defs.select('.clips')
.selectAll('#' + annClipID)
.data(isSizeConstrained ? [0] : []);
annTextClip.enter().append('clipPath')
.classed('annclip', true)
.attr('id', annClipID)
.append('rect');
annTextClip.exit().remove();
var font = options.font;
var annText = annTextGroupInner.append('text')
.classed('annotation', true)
.attr('data-unformatted', options.text)
.text(options.text);
function textLayout(s) {
s.call(Drawing.font, font)
.attr({
'text-anc ...n/a
init = function (plotObj) {
/*
* If we have already instantiated an emitter for this plot
* return early.
*/
if(plotObj._ev instanceof EventEmitter) return plotObj;
var ev = new EventEmitter();
var internalEv = new EventEmitter();
/*
* Assign to plot._ev while we still live in a land
* where plot is a DOM element with stuff attached to it.
* In the future we can make plot the event emitter itself.
*/
plotObj._ev = ev;
/*
* Create a second event handler that will manage events *internally*.
* This allows parts of plotly to respond to thing like relayout without
* having to use the user-facing event handler. They cannot peacefully
* coexist on the same handler because a user invoking
* plotObj.removeAllListeners() would detach internal events, breaking
* plotly.
*/
plotObj._internalEv = internalEv;
/*
* Assign bound methods from the ev to the plot object. These methods
* will reference the 'this' of plot._ev even though they are methods
* of plot. This will keep the event machinery away from the plot object
* which currently is often a DOM element but presents an API that will
* continue to function when plot becomes an emitter. Not all EventEmitter
* methods have been bound to `plot` as some do not currently add value to
* the Plotly event API.
*/
plotObj.on = ev.on.bind(ev);
plotObj.once = ev.once.bind(ev);
plotObj.removeListener = ev.removeListener.bind(ev);
plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);
/*
* Create funtions for managing internal events. These are *only* triggered
* by the mirroring of external events via the emit function.
*/
plotObj._internalOn = internalEv.on.bind(internalEv);
plotObj._internalOnce = internalEv.once.bind(internalEv);
plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv);
plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv);
/*
* We must wrap emit to continue to support JQuery events. The idea
* is to check to see if the user is using JQuery events, if they are
* we emit JQuery events to trigger user handlers as well as the EventEmitter
* events.
*/
plotObj.emit = function(event, data) {
if(typeof jQuery !== 'undefined') {
jQuery(plotObj).trigger(event, data);
}
ev.emit(event, data);
internalEv.emit(event, data);
};
return plotObj;
}...
*/
Plotly.plot = function(gd, data, layout, config) {
var frames;
gd = helpers.getGraphDiv(gd);
// Events.init is idempotent and bails early if gd has already been init'd
Events.init(gd);
if(Lib.isPlainObject(data)) {
var obj = data;
data = obj.data;
layout = obj.layout;
config = obj.config;
frames = obj.frames;
...purge = function (plotObj) {
delete plotObj._ev;
delete plotObj.on;
delete plotObj.once;
delete plotObj.removeListener;
delete plotObj.removeAllListeners;
delete plotObj.emit;
delete plotObj._ev;
delete plotObj._internalEv;
delete plotObj._internalOn;
delete plotObj._internalOnce;
delete plotObj._removeInternalListener;
delete plotObj._removeAllInternalListeners;
return plotObj;
}...
*/
Plotly.newPlot = function(gd, data, layout, config) {
gd = helpers.getGraphDiv(gd);
// remove gl contexts
Plots.cleanPlot([], {}, gd._fullData || {}, gd._fullLayout || {});
Plots.purge(gd);
return Plotly.plot(gd, data, layout, config);
};
/**
* Wrap negative indicies to their positive counterparts.
*
* @param {Number[]} indices An array of indices
...triggerHandler = function (plotObj, event, data) {
var jQueryHandlerValue;
var nodeEventHandlerValue;
/*
* If Jquery exists run all its handlers for this event and
* collect the return value of the LAST handler function
*/
if(typeof jQuery !== 'undefined') {
jQueryHandlerValue = jQuery(plotObj).triggerHandler(event, data);
}
/*
* Now run all the node style event handlers
*/
var ev = plotObj._ev;
if(!ev) return jQueryHandlerValue;
var handlers = ev._events[event];
if(!handlers) return jQueryHandlerValue;
/*
* handlers can be function or an array of functions
*/
if(typeof handlers === 'function') handlers = [handlers];
var lastHandler = handlers.pop();
/*
* Call all the handlers except the last one.
*/
for(var i = 0; i < handlers.length; i++) {
handlers[i](data);
}
/*
* Now call the final handler and collect its value
*/
nodeEventHandlerValue = lastHandler(data);
/*
* Return either the jquery handler value if it exists or the
* nodeEventHandler value. Jquery event value superceeds nodejs
* events for backwards compatability reasons.
*/
return jQueryHandlerValue !== undefined ? jQueryHandlerValue :
nodeEventHandlerValue;
}...
var jQueryHandlerValue;
var nodeEventHandlerValue;
/*
* If Jquery exists run all its handlers for this event and
* collect the return value of the LAST handler function
*/
if(typeof jQuery !== 'undefined') {
jQueryHandlerValue = jQuery(plotObj).triggerHandler(event, data);
}
/*
* Now run all the node style event handlers
*/
var ev = plotObj._ev;
if(!ev) return jQueryHandlerValue;
...extendDeep = function () {
return _extend(arguments, true, false, false);
}...
for(var i = 0; i < args.length; i++) {
arg = args[i];
if(arg === gd) copy[i] = arg;
else if(typeof arg === 'object') {
copy[i] = Array.isArray(arg) ?
Lib.extendDeep([], arg) :
Lib.extendDeepAll({}, arg);
}
else copy[i] = arg;
}
return copy;
}
...extendDeepAll = function () {
return _extend(arguments, true, true, false);
}...
for(var i = 0; i < args.length; i++) {
arg = args[i];
if(arg === gd) copy[i] = arg;
else if(typeof arg === 'object') {
copy[i] = Array.isArray(arg) ?
Lib.extendDeep([], arg) :
Lib.extendDeepAll({}, arg);
}
else copy[i] = arg;
}
return copy;
}
...extendDeepNoArrays = function () {
return _extend(arguments, true, false, true);
}...
if(data.hasOwnProperty(key)) {
if((match = key.match(dottedPropertyRegex))) {
datum = data[key];
prop = match[1];
delete data[key];
data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths
(datum))[prop]);
} else if((match = key.match(indexedPropertyRegex))) {
datum = data[key];
prop = match[1];
idx = parseInt(match[2]);
delete data[key];
...extendFlat = function () {
return _extend(arguments, false, false, false);
}...
function opaqueSetBackground(gd, bgColor) {
gd._fullLayout._paperdiv.style('background', 'white');
Plotly.defaultConfig.setBackground(gd, bgColor);
}
function setPlotContext(gd, config) {
if(!gd._context) gd._context = Lib.extendFlat({}, Plotly.defaultConfig);
var context = gd._context;
if(config) {
Object.keys(config).forEach(function(key) {
if(key in context) {
if(key === 'setBackground' && config[key] === 'opaque') {
context[key] = opaqueSetBackground;
...function Geo(options) {
this.id = options.id;
this.graphDiv = options.graphDiv;
this.container = options.container;
this.topojsonURL = options.topojsonURL;
this.topojsonName = null;
this.topojson = null;
this.projectionType = null;
this.projection = null;
this.clipAngle = null;
this.setScale = null;
this.path = null;
this.zoom = null;
this.zoomReset = null;
this.makeFramework();
this.traceHash = {};
}n/a
adjustLayout = function (geoLayout, graphSize) {
var domain = geoLayout.domain;
var left = graphSize.l + graphSize.w * domain.x[0] + geoLayout._marginX,
top = graphSize.t + graphSize.h * (1 - domain.y[1]) + geoLayout._marginY;
Drawing.setTranslate(this.framework, left, top);
var dimsAttrs = {
x: 0,
y: 0,
width: geoLayout._width,
height: geoLayout._height
};
this.clipDef.select('rect')
.attr(dimsAttrs);
this.framework.select('.bglayer').select('rect')
.attr(dimsAttrs)
.call(Color.fill, geoLayout.bgcolor);
this.xaxis._offset = left;
this.xaxis._length = geoLayout._width;
this.yaxis._offset = top;
this.yaxis._length = geoLayout._height;
}...
// TODO don't reset projection on all graph edits
_this.projection = null;
_this.setScale = createGeoScale(geoLayout, graphSize);
_this.makeProjection(geoLayout);
_this.makePath();
_this.adjustLayout(geoLayout, graphSize);
_this.zoom = createGeoZoom(_this, geoLayout);
_this.zoomReset = createGeoZoomReset(_this, geoLayout);
_this.mockAxis = createMockAxis(fullLayout);
_this.framework
.call(_this.zoom)
...drawGraticule = function (selection, axisName, geoLayout) {
var axisLayout = geoLayout[axisName];
if(axisLayout.showgrid !== true) return;
var scopeDefaults = constants.scopeDefaults[geoLayout.scope],
lonaxisRange = scopeDefaults.lonaxisRange,
lataxisRange = scopeDefaults.lataxisRange,
step = axisName === 'lonaxis' ?
[axisLayout.dtick] :
[0, axisLayout.dtick],
graticule = makeGraticule(lonaxisRange, lataxisRange, step);
selection.append('g')
.datum(graticule)
.attr('class', axisName + 'graticule')
.append('path')
.attr('class', 'graticulepath');
}...
// N.B. html('') does not work in IE11
gBaseLayer.selectAll('*').remove();
for(var i = 0; i < baseLayers.length; i++) {
layerName = baseLayers[i];
if(axesNames.indexOf(layerName) !== -1) {
this.drawGraticule(gBaseLayer, layerName, geoLayout);
}
else this.drawTopo(gBaseLayer, layerName, geoLayout);
}
this.styleLayout(geoLayout);
};
...drawLayout = function (geoLayout) {
var gBaseLayer = this.framework.select('g.baselayer'),
baseLayers = constants.baseLayers,
axesNames = constants.axesNames,
layerName;
// TODO move to more d3-idiomatic pattern (that's work on replot)
// N.B. html('') does not work in IE11
gBaseLayer.selectAll('*').remove();
for(var i = 0; i < baseLayers.length; i++) {
layerName = baseLayers[i];
if(axesNames.indexOf(layerName) !== -1) {
this.drawGraticule(gBaseLayer, layerName, geoLayout);
}
else this.drawTopo(gBaseLayer, layerName, geoLayout);
}
this.styleLayout(geoLayout);
}...
else _this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
// TODO handle topojson-is-loading case
// to avoid making multiple request while streaming
};
proto.onceTopojsonIsLoaded = function(geoCalcData, geoLayout) {
this.drawLayout(geoLayout);
Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);
this.render();
};
proto.makeProjection = function(geoLayout) {
...drawTopo = function (selection, layerName, geoLayout) {
if(geoLayout['show' + layerName] !== true) return;
var topojson = this.topojson,
datum = layerName === 'frame' ?
constants.sphereSVG :
topojsonFeature(topojson, topojson.objects[layerName]);
selection.append('g')
.datum(datum)
.attr('class', layerName)
.append('path')
.attr('class', 'basepath');
}...
for(var i = 0; i < baseLayers.length; i++) {
layerName = baseLayers[i];
if(axesNames.indexOf(layerName) !== -1) {
this.drawGraticule(gBaseLayer, layerName, geoLayout);
}
else this.drawTopo(gBaseLayer, layerName, geoLayout);
}
this.styleLayout(geoLayout);
};
function styleFillLayer(selection, layerName, geoLayout) {
var layerAdj = constants.layerNameToAdjective[layerName];
...isLonLatOverEdges = function (lonlat) {
var clipAngle = this.clipAngle;
if(clipAngle === null) return false;
var p = this.projection.rotate(),
angle = d3.geo.distance(lonlat, [-p[0], -p[1]]),
maxAngle = clipAngle * Math.PI / 180;
return angle > maxAngle;
}...
if(!lonlatPx) return null;
return 'translate(' + lonlatPx[0] + ',' + lonlatPx[1] + ')';
}
// hide paths over edges of clipped projections
function hideShowPoints(d) {
return _this.isLonLatOverEdges(d.lonlat) ? '0' : '1.0';
}
framework.selectAll('path.basepath').attr('d', path);
framework.selectAll('path.graticulepath').attr('d', path);
gChoropleth.selectAll('path.choroplethlocation').attr('d', path);
gChoropleth.selectAll('path.basepath').attr('d', path);
...makeFramework = function () {
var fullLayout = this.graphDiv._fullLayout;
var clipId = 'clip' + fullLayout._uid + this.id;
var defGroup = fullLayout._defs.selectAll('g.clips')
.data([0]);
defGroup.enter().append('g')
.classed('clips', true);
var clipDef = this.clipDef = defGroup.selectAll('#' + clipId)
.data([0]);
clipDef.enter().append('clipPath').attr('id', clipId)
.append('rect');
var framework = this.framework = d3.select(this.container).append('g');
framework
.attr('class', 'geo ' + this.id)
.style('pointer-events', 'all')
.call(Drawing.setClipUrl, clipId);
framework.append('g')
.attr('class', 'bglayer')
.append('rect');
framework.append('g').attr('class', 'baselayer');
framework.append('g').attr('class', 'choroplethlayer');
framework.append('g').attr('class', 'baselayeroverchoropleth');
framework.append('g').attr('class', 'scattergeolayer');
// N.B. disable dblclick zoom default
framework.on('dblclick.zoom', null);
this.xaxis = { _id: 'x' };
this.yaxis = { _id: 'y' };
}...
this.clipAngle = null;
this.setScale = null;
this.path = null;
this.zoom = null;
this.zoomReset = null;
this.makeFramework();
this.traceHash = {};
}
module.exports = Geo;
var proto = Geo.prototype;
...makePath = function () {
this.path = d3.geo.path().projection(this.projection);
}...
// N.B. 'geoLayout' is unambiguous, no need for 'user' geo layout here
// TODO don't reset projection on all graph edits
_this.projection = null;
_this.setScale = createGeoScale(geoLayout, graphSize);
_this.makeProjection(geoLayout);
_this.makePath();
_this.adjustLayout(geoLayout, graphSize);
_this.zoom = createGeoZoom(_this, geoLayout);
_this.zoomReset = createGeoZoomReset(_this, geoLayout);
_this.mockAxis = createMockAxis(fullLayout);
_this.framework
...makeProjection = function (geoLayout) {
var projLayout = geoLayout.projection,
projType = projLayout.type,
isNew = this.projection === null || projType !== this.projectionType,
projection;
if(isNew) {
this.projectionType = projType;
projection = this.projection = d3.geo[constants.projNames[projType]]();
}
else projection = this.projection;
projection
.translate(projLayout._translate0)
.precision(constants.precision);
if(!geoLayout._isAlbersUsa) {
projection
.rotate(projLayout._rotate)
.center(projLayout._center);
}
if(geoLayout._clipAngle) {
this.clipAngle = geoLayout._clipAngle; // needed in proto.render
projection
.clipAngle(geoLayout._clipAngle - constants.clipPad);
}
else this.clipAngle = null; // for graph edits
if(projLayout.parallels) {
projection
.parallels(projLayout.parallels);
}
if(isNew) this.setScale(projection);
projection
.translate(projLayout._translate)
.scale(projLayout._scale);
}...
// N.B. 'geoLayout' is unambiguous, no need for 'user' geo layout here
// TODO don't reset projection on all graph edits
_this.projection = null;
_this.setScale = createGeoScale(geoLayout, graphSize);
_this.makeProjection(geoLayout);
_this.makePath();
_this.adjustLayout(geoLayout, graphSize);
_this.zoom = createGeoZoom(_this, geoLayout);
_this.zoomReset = createGeoZoomReset(_this, geoLayout);
_this.mockAxis = createMockAxis(fullLayout);
...onceTopojsonIsLoaded = function (geoCalcData, geoLayout) {
this.drawLayout(geoLayout);
Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);
this.render();
}...
topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout);
if(_this.topojson === null || topojsonNameNew !== _this.topojsonName) {
_this.topojsonName = topojsonNameNew;
if(PlotlyGeoAssets.topojson[_this.topojsonName] !== undefined) {
_this.topojson = PlotlyGeoAssets.topojson[_this.topojsonName];
_this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
}
else {
topojsonPath = topojsonUtils.getTopojsonPath(
_this.topojsonURL,
_this.topojsonName
);
...plot = function (geoCalcData, fullLayout, promises) {
var _this = this,
geoLayout = fullLayout[_this.id],
graphSize = fullLayout._size;
var topojsonNameNew, topojsonPath;
// N.B. 'geoLayout' is unambiguous, no need for 'user' geo layout here
// TODO don't reset projection on all graph edits
_this.projection = null;
_this.setScale = createGeoScale(geoLayout, graphSize);
_this.makeProjection(geoLayout);
_this.makePath();
_this.adjustLayout(geoLayout, graphSize);
_this.zoom = createGeoZoom(_this, geoLayout);
_this.zoomReset = createGeoZoomReset(_this, geoLayout);
_this.mockAxis = createMockAxis(fullLayout);
_this.framework
.call(_this.zoom)
.on('dblclick.zoom', _this.zoomReset);
_this.framework.on('mousemove', function() {
var mouse = d3.mouse(this),
lonlat = _this.projection.invert(mouse);
if(!lonlat || isNaN(lonlat[0]) || isNaN(lonlat[1])) return;
var evt = d3.event;
evt.xpx = mouse[0];
evt.ypx = mouse[1];
_this.xaxis.c2p = function() { return mouse[0]; };
_this.xaxis.p2c = function() { return lonlat[0]; };
_this.yaxis.c2p = function() { return mouse[1]; };
_this.yaxis.p2c = function() { return lonlat[1]; };
Fx.hover(_this.graphDiv, evt, _this.id);
});
_this.framework.on('mouseout', function() {
Fx.loneUnhover(fullLayout._toppaper);
});
_this.framework.on('click', function() {
Fx.click(_this.graphDiv, d3.event);
});
topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout);
if(_this.topojson === null || topojsonNameNew !== _this.topojsonName) {
_this.topojsonName = topojsonNameNew;
if(PlotlyGeoAssets.topojson[_this.topojsonName] !== undefined) {
_this.topojson = PlotlyGeoAssets.topojson[_this.topojsonName];
_this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
}
else {
topojsonPath = topojsonUtils.getTopojsonPath(
_this.topojsonURL,
_this.topojsonName
);
promises.push(new Promise(function(resolve, reject) {
d3.json(topojsonPath, function(error, topojson) {
if(error) {
if(error.status === 404) {
reject(new Error([
'plotly.js could not find topojson file at',
topojsonPath, '.',
'Make sure the *topojsonURL* plot config option',
'is set properly.'
].join(' ')));
}
else {
reject(new Error([
'unexpected error while fetching topojson file at',
topojsonPath
].join(' ')));
}
return;
}
_this.topojson = topojson;
PlotlyGeoAssets.topojson[_this.topojsonName] = topojson;
_this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
resolve();
});
}));
}
}
else _this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
// TODO handle topojson-is-loading case
// to avoid making multiple request while streaming
}...
fullLayout._infolayer.selectAll('.cb' + uid).remove();
}
}
// loop over the base plot modules present on graph
var basePlotModules = fullLayout._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
basePlotModules[i].plot(gd);
}
// keep reference to shape layers in subplots
var layerSubplot = fullLayout._paper.selectAll('.layer-subplot');
fullLayout._shapeSubplotLayers = layerSubplot.selectAll('.shapelayer');
// styling separate from drawing
...render = function () {
var _this = this,
framework = _this.framework,
gChoropleth = framework.select('g.choroplethlayer'),
gScatterGeo = framework.select('g.scattergeolayer'),
path = _this.path;
function translatePoints(d) {
var lonlatPx = _this.projection(d.lonlat);
if(!lonlatPx) return null;
return 'translate(' + lonlatPx[0] + ',' + lonlatPx[1] + ')';
}
// hide paths over edges of clipped projections
function hideShowPoints(d) {
return _this.isLonLatOverEdges(d.lonlat) ? '0' : '1.0';
}
framework.selectAll('path.basepath').attr('d', path);
framework.selectAll('path.graticulepath').attr('d', path);
gChoropleth.selectAll('path.choroplethlocation').attr('d', path);
gChoropleth.selectAll('path.basepath').attr('d', path);
gScatterGeo.selectAll('path.js-line').attr('d', path);
if(_this.clipAngle !== null) {
gScatterGeo.selectAll('path.point')
.style('opacity', hideShowPoints)
.attr('transform', translatePoints);
gScatterGeo.selectAll('text')
.style('opacity', hideShowPoints)
.attr('transform', translatePoints);
}
else {
gScatterGeo.selectAll('path.point')
.attr('transform', translatePoints);
gScatterGeo.selectAll('text')
.attr('transform', translatePoints);
}
}...
var geo = fullLayout[geoIds[i]]._subplot;
if(attr === 'zoom') {
var scale = geo.projection.scale();
var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;
geo.projection.scale(newScale);
geo.zoom.scale(newScale);
geo.render();
}
else if(attr === 'reset') geo.zoomReset();
}
}
modeBarButtons.hoverClosestGl2d = {
name: 'hoverClosestGl2d',
...styleLayer = function (selection, layerName, geoLayout) {
var fillLayers = constants.fillLayers,
lineLayers = constants.lineLayers;
if(fillLayers.indexOf(layerName) !== -1) {
styleFillLayer(selection, layerName, geoLayout);
}
else if(lineLayers.indexOf(layerName) !== -1) {
styleLineLayer(selection, layerName, geoLayout);
}
}...
for(var i = 0; i < baseLayers.length; i++) {
layerName = baseLayers[i];
if(axesNames.indexOf(layerName) !== -1) {
styleGraticule(gBaseLayer, layerName, geoLayout);
}
else this.styleLayer(gBaseLayer, layerName, geoLayout);
}
};
proto.isLonLatOverEdges = function(lonlat) {
var clipAngle = this.clipAngle;
if(clipAngle === null) return false;
...styleLayout = function (geoLayout) {
var gBaseLayer = this.framework.select('g.baselayer'),
baseLayers = constants.baseLayers,
axesNames = constants.axesNames,
layerName;
for(var i = 0; i < baseLayers.length; i++) {
layerName = baseLayers[i];
if(axesNames.indexOf(layerName) !== -1) {
styleGraticule(gBaseLayer, layerName, geoLayout);
}
else this.styleLayer(gBaseLayer, layerName, geoLayout);
}
}...
if(axesNames.indexOf(layerName) !== -1) {
this.drawGraticule(gBaseLayer, layerName, geoLayout);
}
else this.drawTopo(gBaseLayer, layerName, geoLayout);
}
this.styleLayout(geoLayout);
};
function styleFillLayer(selection, layerName, geoLayout) {
var layerAdj = constants.layerNameToAdjective[layerName];
selection.select('.' + layerName)
.selectAll('path')
...locationToFeature = function (locationmode, location, features) {
var locationId = getLocationId(locationmode, location);
if(locationId) {
for(var i = 0; i < features.length; i++) {
var feature = features[i];
if(feature.id === locationId) return feature;
}
Lib.warn([
'Location with id', locationId,
'does not have a matching topojson feature at this resolution.'
].join(' '));
}
return false;
}n/a
calcTraceToLineCoords = function (calcTrace) {
var trace = calcTrace[0].trace;
var connectgaps = trace.connectgaps;
var coords = [];
var lineString = [];
for(var i = 0; i < calcTrace.length; i++) {
var calcPt = calcTrace[i];
var lonlat = calcPt.lonlat;
if(lonlat[0] !== BADNUM) {
lineString.push(lonlat);
} else if(!connectgaps && lineString.length > 0) {
coords.push(lineString);
lineString = [];
}
}
if(lineString.length > 0) {
coords.push(lineString);
}
return coords;
}n/a
makeBlank = function () {
return {
type: 'Point',
coordinates: []
};
}n/a
makeLine = function (coords, trace) {
var out = {};
if(coords.length === 1) {
out = {
type: 'LineString',
coordinates: coords[0]
};
}
else {
out = {
type: 'MultiLineString',
coordinates: coords
};
}
if(trace) out.trace = trace;
return out;
}n/a
makePolygon = function (coords, trace) {
var out = {};
if(coords.length === 1) {
out = {
type: 'Polygon',
coordinates: coords
};
}
else {
var _coords = new Array(coords.length);
for(var i = 0; i < coords.length; i++) {
_coords[i] = [coords[i]];
}
out = {
type: 'MultiPolygon',
coordinates: _coords
};
}
if(trace) out.trace = trace;
return out;
}n/a
click = function (gd, evt) {
var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata);
function emitClick() { gd.emit('plotly_click', {points: gd._hoverdata, event: evt}); }
if(gd._hoverdata && evt && evt.target) {
if(annotationsDone && annotationsDone.then) {
annotationsDone.then(emitClick);
}
else emitClick();
// why do we get a double event without this???
if(evt.stopImmediatePropagation) evt.stopImmediatePropagation();
}
}...
name = 'download';
}
if(canUseSaveLink) {
saveLink.href = url;
saveLink.download = name;
document.body.appendChild(saveLink);
saveLink.click();
document.body.removeChild(saveLink);
resolve(name);
}
// IE 10+ (native saveAs)
if(typeof navigator !== 'undefined' && navigator.msSaveBlob) {
navigator.msSaveBlob(new Blob([url]), name);
...getClosest = function (cd, distfn, pointData) {
// do we already have a point number? (array mode only)
if(pointData.index !== false) {
if(pointData.index >= 0 && pointData.index < cd.length) {
pointData.distance = 0;
}
else pointData.index = false;
}
else {
// apply the distance function to each data point
// this is the longest loop... if this bogs down, we may need
// to create pre-sorted data (by x or y), not sure how to
// do this for 'closest'
for(var i = 0; i < cd.length; i++) {
var newDistance = distfn(cd[i]);
if(newDistance <= pointData.distance) {
pointData.index = i;
pointData.distance = newDistance;
}
}
}
return pointData;
}...
maxPos = (hovermode === 'closest') ?
thisBarMaxPos :
function(di) {
return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2);
};
var distfn = Fx.getDistanceFunction(hovermode, dx, dy);
Fx.getClosest(cd, distfn, pointData);
// skip the rest (for this trace) if we didn't find a close point
if(pointData.index === false) return;
// the closest data point
var index = pointData.index,
di = cd[index],
...getDistanceFunction = function (mode, dx, dy, dxy) {
if(mode === 'closest') return dxy || quadrature(dx, dy);
return mode === 'x' ? dx : dy;
}...
maxPos = (hovermode === 'closest') ?
thisBarMaxPos :
function(di) {
return Math.max(thisBarMaxPos(di), di.p + t.bargroupwidth / 2);
};
var distfn = Fx.getDistanceFunction(hovermode, dx, dy);
Fx.getClosest(cd, distfn, pointData);
// skip the rest (for this trace) if we didn't find a close point
if(pointData.index === false) return;
// the closest data point
var index = pointData.index,
...hover = function (gd, evt, subplot) {
if(typeof gd === 'string') gd = document.getElementById(gd);
if(gd._lastHoverTime === undefined) gd._lastHoverTime = 0;
// If we have an update queued, discard it now
if(gd._hoverTimer !== undefined) {
clearTimeout(gd._hoverTimer);
gd._hoverTimer = undefined;
}
// Is it more than 100ms since the last update? If so, force
// an update now (synchronously) and exit
if(Date.now() > gd._lastHoverTime + constants.HOVERMINTIME) {
hover(gd, evt, subplot);
gd._lastHoverTime = Date.now();
return;
}
// Queue up the next hover for 100ms from now (if no further events)
gd._hoverTimer = setTimeout(function() {
hover(gd, evt, subplot);
gd._lastHoverTime = Date.now();
gd._hoverTimer = undefined;
}, constants.HOVERMINTIME);
}...
xa._length, ya._length, 'ns', 'ew');
maindrag.onmousemove = function(evt) {
// This is on `gd._fullLayout`, *not* fullLayout because the reference
// changes by the time this is called again.
gd._fullLayout._rehover = function() {
if(gd._fullLayout._hoversubplot === subplot) {
fx.hover(gd, evt, subplot);
}
};
fx.hover(gd, evt, subplot);
// Note that we have *not* used the cached fullLayout variable here
// since that may be outdated when this is called as a callback later on
...inbox = function (v0, v1) {
if(v0 * v1 < 0 || v0 === 0) {
return constants.MAXDIST * (0.6 - 0.3 / Math.max(3, Math.abs(v0 - v1)));
}
return Infinity;
}...
var t = cd[0].t;
var xa = pointData.xa;
var ya = pointData.ya;
var posVal, thisBarMinPos, thisBarMaxPos, minPos, maxPos, dx, dy;
var positionFn = function(di) {
return Fx.inbox(minPos(di) - posVal, maxPos(di) - posVal);
};
if(trace.orientation === 'h') {
posVal = yval;
thisBarMinPos = function(di) { return di.y - di.w / 2; };
thisBarMaxPos = function(di) { return di.y + di.w / 2; };
dx = function(di) {
...init = function (gd) {
var fullLayout = gd._fullLayout;
if(!fullLayout._has('cartesian') || gd._context.staticPlot) return;
var subplots = Object.keys(fullLayout._plots || {}).sort(function(a, b) {
// sort overlays last, then by x axis number, then y axis number
if((fullLayout._plots[a].mainplot && true) ===
(fullLayout._plots[b].mainplot && true)) {
var aParts = a.split('y'),
bParts = b.split('y');
return (aParts[0] === bParts[0]) ?
(Number(aParts[1] || 1) - Number(bParts[1] || 1)) :
(Number(aParts[0] || 1) - Number(bParts[0] || 1));
}
return fullLayout._plots[a].mainplot ? 1 : -1;
});
subplots.forEach(function(subplot) {
var plotinfo = fullLayout._plots[subplot];
if(!fullLayout._has('cartesian')) return;
var xa = plotinfo.xaxis,
ya = plotinfo.yaxis,
// the y position of the main x axis line
y0 = (xa._linepositions[subplot] || [])[3],
// the x position of the main y axis line
x0 = (ya._linepositions[subplot] || [])[3];
var DRAGGERSIZE = constants.DRAGGERSIZE;
if(isNumeric(y0) && xa.side === 'top') y0 -= DRAGGERSIZE;
if(isNumeric(x0) && ya.side !== 'right') x0 -= DRAGGERSIZE;
// main and corner draggers need not be repeated for
// overlaid subplots - these draggers drag them all
if(!plotinfo.mainplot) {
// main dragger goes over the grids and data, so we use its
// mousemove events for all data hover effects
var maindrag = dragBox(gd, plotinfo, 0, 0,
xa._length, ya._length, 'ns', 'ew');
maindrag.onmousemove = function(evt) {
// This is on `gd._fullLayout`, *not* fullLayout because the reference
// changes by the time this is called again.
gd._fullLayout._rehover = function() {
if(gd._fullLayout._hoversubplot === subplot) {
fx.hover(gd, evt, subplot);
}
};
fx.hover(gd, evt, subplot);
// Note that we have *not* used the cached fullLayout variable here
// since that may be outdated when this is called as a callback later on
gd._fullLayout._lasthover = maindrag;
gd._fullLayout._hoversubplot = subplot;
};
/*
* IMPORTANT:
* We must check for the presence of the drag cover here.
* If we don't, a 'mouseout' event is triggered on the
* maindrag before each 'click' event, which has the effect
* of clearing the hoverdata; thus, cancelling the click event.
*/
maindrag.onmouseout = function(evt) {
if(gd._dragging) return;
// When the mouse leaves this maindrag, unset the hovered subplot.
// This may cause problems if it leaves the subplot directly *onto*
// another subplot, but that's a tiny corner case at the moment.
gd._fullLayout._hoversubplot = null;
dragElement.unhover(gd, evt);
};
maindrag.onclick = function(evt) {
fx.click(gd, evt);
};
// corner draggers
if(gd._context.showAxisDragHandles) {
dragBox(gd, plotinfo, -DRAGGERSIZE, -DRAGGERSIZE,
DRAGGERSIZE, DRAGGERSIZE, 'n', 'w');
dragBox(gd, plotinfo, xa._length, -DRAGGERSIZE,
DRAGGERSIZE, DRAGGERSIZE, 'n', 'e');
dragBox(gd, plotinfo, -DRAGGERSIZE, ya._length,
DRAGGERSIZE, DRAGGERSIZE, 's', 'w');
dragBox(gd, plotinfo, xa._length, ya._length,
DRAGGERSIZE, DRAGGERSIZE, 's', 'e');
}
}
if(gd._context.showAxisDragHandles) {
// x axis draggers - if you have overlaid plots, ......
*/
Plotly.plot = function(gd, data, layout, config) {
var frames;
gd = helpers.getGraphDiv(gd);
// Events.init is idempotent and bails early if gd has already been init'd
Events.init(gd);
if(Lib.isPlainObject(data)) {
var obj = data;
data = obj.data;
layout = obj.layout;
config = obj.config;
frames = obj.frames;
...isHoriz = function (fullData) {
var isHoriz = true;
for(var i = 0; i < fullData.length; i++) {
var trace = fullData[i];
if(trace.orientation !== 'h') {
isHoriz = false;
break;
}
}
return isHoriz;
}...
coerce('dragmode');
var hovermodeDflt;
if(layoutOut._has('cartesian')) {
// flag for 'horizontal' plots:
// determines the state of the mode bar 'compare' hovermode button
var isHoriz = layoutOut._isHoriz = fx.isHoriz(fullData);
hovermodeDflt = isHoriz ? 'y' : 'x';
}
else hovermodeDflt = 'closest';
coerce('hovermode', hovermodeDflt);
};
...loneHover = function (hoverItem, opts) {
var pointData = {
color: hoverItem.color || Color.defaultLine,
x0: hoverItem.x0 || hoverItem.x || 0,
x1: hoverItem.x1 || hoverItem.x || 0,
y0: hoverItem.y0 || hoverItem.y || 0,
y1: hoverItem.y1 || hoverItem.y || 0,
xLabel: hoverItem.xLabel,
yLabel: hoverItem.yLabel,
zLabel: hoverItem.zLabel,
text: hoverItem.text,
name: hoverItem.name,
idealAlign: hoverItem.idealAlign,
// optional extra bits of styling
borderColor: hoverItem.borderColor,
fontFamily: hoverItem.fontFamily,
fontSize: hoverItem.fontSize,
fontColor: hoverItem.fontColor,
// filler to make createHoverText happy
trace: {
index: 0,
hoverinfo: ''
},
xa: {_offset: 0},
ya: {_offset: 0},
index: 0
};
var container3 = d3.select(opts.container),
outerContainer3 = opts.outerContainer ?
d3.select(opts.outerContainer) : container3;
var fullOpts = {
hovermode: 'closest',
rotateLabels: false,
bgColor: opts.bgColor || Color.background,
container: container3,
outerContainer: outerContainer3
};
var hoverLabel = createHoverText([pointData], fullOpts);
alignHoverText(hoverLabel, fullOpts.rotateLabels);
return hoverLabel.node();
}...
annTextGroupInner
.on('mouseover', function() {
var hoverOptions = options.hoverlabel;
var hoverFont = hoverOptions.font;
var bBox = this.getBoundingClientRect();
var bBoxRef = gd.getBoundingClientRect();
Fx.loneHover({
x0: bBox.left - bBoxRef.left,
x1: bBox.right - bBoxRef.left,
y: (bBox.top + bBox.bottom) / 2 - bBoxRef.top,
text: options.hovertext,
color: hoverOptions.bgcolor,
borderColor: hoverOptions.bordercolor,
fontFamily: hoverFont.family,
...loneUnhover = function (containerOrSelection) {
// duck type whether the arg is a d3 selection because ie9 doesn't
// handle instanceof like modern browsers do.
var selection = Lib.isD3Selection(containerOrSelection) ?
containerOrSelection :
d3.select(containerOrSelection);
selection.selectAll('g.hovertext').remove();
selection.selectAll('.spikeline').remove();
}...
fontColor: hoverFont.color
}, {
container: fullLayout._hoverlayer.node(),
outerContainer: fullLayout._paper.node()
});
})
.on('mouseout', function() {
Fx.loneUnhover(fullLayout._hoverlayer.node());
});
}
var borderwidth = options.borderwidth,
borderpad = options.borderpad,
borderfull = borderwidth + borderpad;
...supplyLayoutDefaults = function (layoutIn, layoutOut, fullData) {
function coerce(attr, dflt) {
return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
}
coerce('dragmode');
var hovermodeDflt;
if(layoutOut._has('cartesian')) {
// flag for 'horizontal' plots:
// determines the state of the mode bar 'compare' hovermode button
var isHoriz = layoutOut._isHoriz = fx.isHoriz(fullData);
hovermodeDflt = isHoriz ? 'y' : 'x';
}
else hovermodeDflt = 'closest';
coerce('hovermode', hovermodeDflt);
}...
}
plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) {
var i, _module;
// can't be be part of basePlotModules loop
// in order to handle the orphan axes case
Plotly.Axes.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
// base plot module layout defaults
var basePlotModules = layoutOut._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
_module = basePlotModules[i];
// done above already
...unhover = function (gd, evt, subplot) {
if(typeof gd === 'string') gd = document.getElementById(gd);
// Important, clear any queued hovers
if(gd._hoverTimer) {
clearTimeout(gd._hoverTimer);
gd._hoverTimer = undefined;
}
unhover.raw(gd, evt, subplot);
}...
if(gd._dragging) return;
// When the mouse leaves this maindrag, unset the hovered subplot.
// This may cause problems if it leaves the subplot directly *onto*
// another subplot, but that's a tiny corner case at the moment.
gd._fullLayout._hoversubplot = null;
dragElement.unhover(gd, evt);
};
maindrag.onclick = function(evt) {
fx.click(gd, evt);
};
// corner draggers
...cleanData = function (data, existingData) {
// Enforce unique IDs
var suids = [], // seen uids --- so we can weed out incoming repeats
uids = data.concat(Array.isArray(existingData) ? existingData : [])
.filter(function(trace) { return 'uid' in trace; })
.map(function(trace) { return trace.uid; });
for(var tracei = 0; tracei < data.length; tracei++) {
var trace = data[tracei];
var i;
// assign uids to each trace and detect collisions.
if(!('uid' in trace) || suids.indexOf(trace.uid) !== -1) {
var newUid;
for(i = 0; i < 100; i++) {
newUid = Lib.randstr(uids);
if(suids.indexOf(newUid) === -1) break;
}
trace.uid = Lib.randstr(uids);
uids.push(trace.uid);
}
// keep track of already seen uids, so that if there are
// doubles we force the trace with a repeat uid to
// acquire a new one
suids.push(trace.uid);
// BACKWARD COMPATIBILITY FIXES
// use xbins to bin data in x, and ybins to bin data in y
if(trace.type === 'histogramy' && 'xbins' in trace && !('ybins' in trace)) {
trace.ybins = trace.xbins;
delete trace.xbins;
}
// error_y.opacity is obsolete - merge into color
if(trace.error_y && 'opacity' in trace.error_y) {
var dc = Color.defaults,
yeColor = trace.error_y.color ||
(Registry.traceIs(trace, 'bar') ? Color.defaultLine : dc[tracei % dc.length]);
trace.error_y.color = Color.addOpacity(
Color.rgb(yeColor),
Color.opacity(yeColor) * trace.error_y.opacity);
delete trace.error_y.opacity;
}
// convert bardir to orientation, and put the data into
// the axes it's eventually going to be used with
if('bardir' in trace) {
if(trace.bardir === 'h' && (Registry.traceIs(trace, 'bar') ||
trace.type.substr(0, 9) === 'histogram')) {
trace.orientation = 'h';
exports.swapXYData(trace);
}
delete trace.bardir;
}
// now we have only one 1D histogram type, and whether
// it uses x or y data depends on trace.orientation
if(trace.type === 'histogramy') exports.swapXYData(trace);
if(trace.type === 'histogramx' || trace.type === 'histogramy') {
trace.type = 'histogram';
}
// scl->scale, reversescl->reversescale
if('scl' in trace) {
trace.colorscale = trace.scl;
delete trace.scl;
}
if('reversescl' in trace) {
trace.reversescale = trace.reversescl;
delete trace.reversescl;
}
// axis ids x1 -> x, y1-> y
if(trace.xaxis) trace.xaxis = Axes.cleanId(trace.xaxis, 'x');
if(trace.yaxis) trace.yaxis = Axes.cleanId(trace.yaxis, 'y');
// scene ids scene1 -> scene
if(Registry.traceIs(trace, 'gl3d') && trace.scene) {
trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
}
if(!Registry.traceIs(trace, 'pie') && !Registry.traceIs(trace, 'bar')) {
if(Array.isArray(trace.textposition)) {
trace.textposition = trace.textposition.map(cleanTextPosition);
}
else if(trace.textposition) {
trace.textposition = cleanTextPosition(trace.textposition);
}
}
// fix typo in colorscale definition
if(Registry.traceIs(trace, '2dMap')) {
if(trace.colorscale === 'YIGnBu') trace.colorscale = 'YlGnBu';
if(trace.colorscale === 'YIOrRd') trace.colorscale = 'YlOrRd';
}
if(Registry.traceIs(trace, 'markerColorscale') && trace.marker) {
var cont = trace.marker;
if(cont.colorscale === 'YIGnBu') cont.colorscale = 'YlGnBu';
if(cont.colorscale === 'YIOrRd') cont.colorscale = 'YlOrRd';
} ......
gd._promises = [];
var graphWasEmpty = ((gd.data || []).length === 0 && Array.isArray(data));
// if there is already data on the graph, append the new data
// if you only want to redraw, pass a non-array for data
if(Array.isArray(data)) {
helpers.cleanData(data, gd.data);
if(graphWasEmpty) gd.data = data;
else gd.data.push.apply(gd.data, data);
// for routines outside graph_obj that want a clean tab
// (rather than appending to an existing one) gd.empty
// is used to determine whether to make a new tab
...cleanLayout = function (layout) {
var i, j;
if(!layout) layout = {};
// cannot have (x|y)axis1, numbering goes axis, axis2, axis3...
if(layout.xaxis1) {
if(!layout.xaxis) layout.xaxis = layout.xaxis1;
delete layout.xaxis1;
}
if(layout.yaxis1) {
if(!layout.yaxis) layout.yaxis = layout.yaxis1;
delete layout.yaxis1;
}
var axList = Axes.list({_fullLayout: layout});
for(i = 0; i < axList.length; i++) {
var ax = axList[i];
if(ax.anchor && ax.anchor !== 'free') {
ax.anchor = Axes.cleanId(ax.anchor);
}
if(ax.overlaying) ax.overlaying = Axes.cleanId(ax.overlaying);
// old method of axis type - isdate and islog (before category existed)
if(!ax.type) {
if(ax.isdate) ax.type = 'date';
else if(ax.islog) ax.type = 'log';
else if(ax.isdate === false && ax.islog === false) ax.type = 'linear';
}
if(ax.autorange === 'withzero' || ax.autorange === 'tozero') {
ax.autorange = true;
ax.rangemode = 'tozero';
}
delete ax.islog;
delete ax.isdate;
delete ax.categories; // replaced by _categories
// prune empty domain arrays made before the new nestedProperty
if(emptyContainer(ax, 'domain')) delete ax.domain;
// autotick -> tickmode
if(ax.autotick !== undefined) {
if(ax.tickmode === undefined) {
ax.tickmode = ax.autotick ? 'auto' : 'linear';
}
delete ax.autotick;
}
}
var annotationsLen = Array.isArray(layout.annotations) ? layout.annotations.length : 0;
for(i = 0; i < annotationsLen; i++) {
var ann = layout.annotations[i];
if(!Lib.isPlainObject(ann)) continue;
if(ann.ref) {
if(ann.ref === 'paper') {
ann.xref = 'paper';
ann.yref = 'paper';
}
else if(ann.ref === 'data') {
ann.xref = 'x';
ann.yref = 'y';
}
delete ann.ref;
}
cleanAxRef(ann, 'xref');
cleanAxRef(ann, 'yref');
}
var shapesLen = Array.isArray(layout.shapes) ? layout.shapes.length : 0;
for(i = 0; i < shapesLen; i++) {
var shape = layout.shapes[i];
if(!Lib.isPlainObject(shape)) continue;
cleanAxRef(shape, 'xref');
cleanAxRef(shape, 'yref');
}
var legend = layout.legend;
if(legend) {
// check for old-style legend positioning (x or y is +/- 100)
if(legend.x > 3) {
legend.x = 1.02;
legend.xanchor = 'left';
}
else if(legend.x < -2) {
legend.x = -0.02;
legend.xanchor = 'right';
}
if(legend.y > 3) {
legend.y = 1.02;
legend.yanchor = 'bottom';
}
else if(legend.y < -2) {
legend.y = -0.02;
legend.yanchor = 'top';
}
}
/*
* Moved from rotate -> orbit for dragmode
*/
if(layout.dragmode === 'rotate') layout.dragmode = 'orbit';
// cannot have scene1, numbering goes scene, scene2, scene3...
if(layout.scene1) {
if(!layout.scene) layout.scene = layout.scene1;
delete layout.scene1;
}
/*
* Clean up Scene layouts
*/
var sceneIds = Plots.getSubplotIds(layout, 'gl3d');
for(i = 0; i < sceneIds.length; i++) {
var scene = layout[sceneIds[i]];
// clean old Camera coords
var cameraposition = scene.cameraposition;
if(Array.isArray(cameraposition) && cameraposition[0].length === 4) {
var rotation = cameraposition[0],
center = cameraposition[1],
radius = cameraposition[2],
mat = m4FromQuat([], rotation),
eye = [];
for(j = 0; j < 3; ++j) {
eye[j] = center[i] + radius * mat[2 + 4 * j];
}
scene.camera = {
eye: {x: eye[0], y: eye[1], z: eye[2]}, ......
// for routines outside graph_obj that want a clean tab
// (rather than appending to an existing one) gd.empty
// is used to determine whether to make a new tab
gd.empty = false;
}
if(!gd.layout || graphWasEmpty) gd.layout = helpers.cleanLayout(layout);
// if the user is trying to drag the axes, allow new data and layout
// to come in but don't allow a replot.
if(gd._dragging && !gd._transitioning) {
// signal to drag handler that after everything else is done
// we need to replot, because something has changed
gd._replotPending = true;
...clearPromiseQueue = function (gd) {
if(Array.isArray(gd._promises) && gd._promises.length > 0) {
Lib.log('Clearing previous rejected promises from queue.');
}
gd._promises = [];
}...
* to apply different values to each trace.
*
* If the array is too short, it will wrap around (useful for
* style files that want to specify cyclical default values).
*/
Plotly.restyle = function restyle(gd, astr, val, traces) {
gd = helpers.getGraphDiv(gd);
helpers.clearPromiseQueue(gd);
var aobj = {};
if(typeof astr === 'string') aobj[astr] = val;
else if(Lib.isPlainObject(astr)) {
// the 3-arg form
aobj = Lib.extendFlat({}, astr);
if(traces === undefined) traces = val;
...coerceTraceIndices = function (gd, traceIndices) {
if(isNumeric(traceIndices)) {
return [traceIndices];
}
else if(!Array.isArray(traceIndices) || !traceIndices.length) {
return gd.data.map(function(_, i) { return i; });
}
return traceIndices;
}...
function _restyle(gd, aobj, _traces) {
var fullLayout = gd._fullLayout,
fullData = gd._fullData,
data = gd.data,
i;
var traces = helpers.coerceTraceIndices(gd, _traces);
// initialize flags
var flags = {
docalc: false,
docalcAutorange: false,
doplot: false,
dostyle: false,
...getGraphDiv = function (gd) {
var gdElement;
if(typeof gd === 'string') {
gdElement = document.getElementById(gd);
if(gdElement === null) {
throw new Error('No DOM element with id \'' + gd + '\' exists on the page.');
}
return gdElement;
}
else if(gd === null || gd === undefined) {
throw new Error('DOM element provided is null or undefined');
}
return gd; // otherwise assume that gd is a DOM element
}...
* @param {object} config
* configuration options (see ./plot_config.js for more info)
*
*/
Plotly.plot = function(gd, data, layout, config) {
var frames;
gd = helpers.getGraphDiv(gd);
// Events.init is idempotent and bails early if gd has already been init'd
Events.init(gd);
if(Lib.isPlainObject(data)) {
var obj = data;
data = obj.data;
...hasParent = function (aobj, attr) {
var attrParent = getParent(attr);
while(attrParent) {
if(attrParent in aobj) return true;
attrParent = getParent(attrParent);
}
return false;
}...
// attr can be an array to set several at once (all to the same val)
function doextra(attr, val, i) {
if(Array.isArray(attr)) {
attr.forEach(function(a) { doextra(a, val, i); });
return;
}
// quit if explicitly setting this elsewhere
if(attr in aobj || helpers.hasParent(aobj, attr)) return;
var extraparam;
if(attr.substr(0, 6) === 'LAYOUT') {
extraparam = Lib.nestedProperty(gd.layout, attr.replace('LAYOUT', ''));
} else {
extraparam = Lib.nestedProperty(data[traces[i]], attr);
}
...manageArrayContainers = function (np, newVal, undoit) {
var obj = np.obj,
parts = np.parts,
pLength = parts.length,
pLast = parts[pLength - 1];
var pLastIsNumber = isNumeric(pLast);
// delete item
if(pLastIsNumber && newVal === null) {
// Clear item in array container when new value is null
var contPath = parts.slice(0, pLength - 1).join('.'),
cont = Lib.nestedProperty(obj, contPath).get();
cont.splice(pLast, 1);
// Note that nested property clears null / undefined at end of
// array container, but not within them.
}
// create item
else if(pLastIsNumber && np.get() === undefined) {
// When adding a new item, make sure undo command will remove it
if(np.get() === undefined) undoit[np.astr] = null;
np.set(newVal);
}
// update item
else {
// If the last part of attribute string isn't a number,
// np.set is all we need.
np.set(newVal);
}
}...
cont.orientation =
{v: 'h', h: 'v'}[contFull.orientation];
}
helpers.swapXYData(cont);
}
else if(Plots.dataArrayContainers.indexOf(param.parts[0]) !== -1) {
// TODO: use manageArrays.applyContainerArrayChanges here too
helpers.manageArrayContainers(param, newVal, undoit);
flags.docalc = true;
}
else {
var moduleAttrs = (contFull._module || {}).attributes || {};
var valObject = Lib.nestedProperty(moduleAttrs, ai).get() || {};
// if restyling entire attribute container, assume worse case
...swapXYData = function (trace) {
var i;
Lib.swapAttrs(trace, ['?', '?0', 'd?', '?bins', 'nbins?', 'autobin?', '?src', 'error_?']);
if(Array.isArray(trace.z) && Array.isArray(trace.z[0])) {
if(trace.transpose) delete trace.transpose;
else trace.transpose = true;
}
if(trace.error_x && trace.error_y) {
var errorY = trace.error_y,
copyYstyle = ('copy_ystyle' in errorY) ? errorY.copy_ystyle :
!(errorY.color || errorY.thickness || errorY.width);
Lib.swapAttrs(trace, ['error_?.copy_ystyle']);
if(copyYstyle) {
Lib.swapAttrs(trace, ['error_?.color', 'error_?.thickness', 'error_?.width']);
}
}
if(trace.hoverinfo) {
var hoverInfoParts = trace.hoverinfo.split('+');
for(i = 0; i < hoverInfoParts.length; i++) {
if(hoverInfoParts[i] === 'x') hoverInfoParts[i] = 'y';
else if(hoverInfoParts[i] === 'y') hoverInfoParts[i] = 'x';
}
trace.hoverinfo = hoverInfoParts.join('+');
}
}...
// convert bardir to orientation, and put the data into
// the axes it's eventually going to be used with
if('bardir' in trace) {
if(trace.bardir === 'h' && (Registry.traceIs(trace, 'bar') ||
trace.type.substr(0, 9) === 'histogram')) {
trace.orientation = 'h';
exports.swapXYData(trace);
}
delete trace.bardir;
}
// now we have only one 1D histogram type, and whether
// it uses x or y data depends on trace.orientation
if(trace.type === 'histogramy') exports.swapXYData(trace);
...OptionControl = function (opt, optname) {
/*
* An environment to contain all option setters and
* getters that collectively modify opts.
*
* You can call up opts from any function in new object
* as this.optname || this.opt
*
* See FitOpts for example of usage
*/
if(!opt) opt = {};
if(!optname) optname = 'opt';
var self = {};
self.optionList = [];
self._newoption = function(optObj) {
optObj[optname] = opt;
self[optObj.name] = optObj;
self.optionList.push(optObj);
};
self['_' + optname] = opt;
return self;
}n/a
addStyleRule = function (selector, styleString) {
if(!lib.styleSheet) {
var style = document.createElement('style');
// WebKit hack :(
style.appendChild(document.createTextNode(''));
document.head.appendChild(style);
lib.styleSheet = style.sheet;
}
var styleSheet = lib.styleSheet;
if(styleSheet.insertRule) {
styleSheet.insertRule(selector + '{' + styleString + '}', 0);
}
else if(styleSheet.addRule) {
styleSheet.addRule(selector, styleString, 0);
}
else lib.warn('addStyleRule failed');
}...
'var Lib = require(\'../src/lib\');',
'var rules = ' + rulesStr + ';',
'',
'for(var selector in rules) {',
' var fullSelector = selector.replace(/^,/,\' ,\')',
' .replace(/X/g, \'.js-plotly-plot .plotly\')',
' .replace(/Y/g, \'.plotly-notifier\');',
' Lib.addStyleRule(fullSelector, rules[selector]);',
'}',
''
].join('\n');
fs.writeFile(pathOut, outStr, function(err) {
if(err) throw err;
});
...aggNums = function (f, v, a, len) {
var i,
b;
if(!len) len = a.length;
if(!isNumeric(v)) v = false;
if(Array.isArray(a[0])) {
b = new Array(len);
for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
a = b;
}
for(i = 0; i < len; i++) {
if(!isNumeric(v)) v = a[i];
else if(isNumeric(a[i])) v = f(+v, +a[i]);
}
return v;
}...
exports.aggNums = function(f, v, a, len) {
var i,
b;
if(!len) len = a.length;
if(!isNumeric(v)) v = false;
if(Array.isArray(a[0])) {
b = new Array(len);
for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
a = b;
}
for(i = 0; i < len; i++) {
if(!isNumeric(v)) v = a[i];
else if(isNumeric(a[i])) v = f(+v, +a[i]);
}
...apply2DTransform = function (transform) {
return function() {
var args = arguments;
if(args.length === 3) {
args = args[0];
}// from map
var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
};
}...
var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
};
};
// applies a 2D transformation matrix to an [x1,y1,x2,y2] array (to transform a segment)
exports.apply2DTransform2 = function(transform) {
var at = exports.apply2DTransform(transform);
return function(xys) {
return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
};
};
...apply2DTransform2 = function (transform) {
var at = exports.apply2DTransform(transform);
return function(xys) {
return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
};
}...
textX = annPosPx.x.text + dx,
textY = annPosPx.y.text + dy,
// find the edge of the text box, where we'll start the arrow:
// create transform matrix to rotate the text box corners
transform = Lib.rotationXYMatrix(textangle, textX, textY),
applyTransform = Lib.apply2DTransform(transform),
applyTransform2 = Lib.apply2DTransform2(transform),
// calculate and transform bounding box
width = +annTextBG.attr('width'),
height = +annTextBG.attr('height'),
xLeft = textX - 0.5 * width,
xRight = xLeft + width,
yTop = textY - 0.5 * height,
...bBoxIntersect = function (a, b, pad) {
pad = pad || 0;
return (a.left <= b.right + pad &&
b.left <= a.right + pad &&
a.top <= b.bottom + pad &&
b.top <= a.bottom + pad);
}...
left: x - bb.width / 2,
// impose a 2px gap
right: x + bb.width / 2 + 2,
width: bb.width + 2
});
});
for(i = 0; i < lbbArray.length - 1; i++) {
if(Lib.bBoxIntersect(lbbArray[i], lbbArray[i + 1])) {
// any overlap at all - set 30 degrees
autoangle = 30;
break;
}
}
if(autoangle) {
var tickspacing = Math.abs(
...cleanDate = function (v, dflt, calendar) {
if(exports.isJSDate(v) || typeof v === 'number') {
// do not allow milliseconds (old) or jsdate objects (inherently
// described as gregorian dates) with world calendars
if(isWorldCalendar(calendar)) {
logError('JS Dates and milliseconds are incompatible with world calendars', v);
return dflt;
}
// NOTE: if someone puts in a year as a number rather than a string,
// this will mistakenly convert it thinking it's milliseconds from 1970
// that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
v = exports.ms2DateTimeLocal(+v);
if(!v && dflt !== undefined) return dflt;
}
else if(!exports.isDateTime(v, calendar)) {
logError('unrecognized date', v);
return dflt;
}
return v;
}...
if(typeof pos === 'string' && (ax._categories || []).length) {
newPos = ax._categories.indexOf(pos);
containerOut[attr] = (newPos === -1) ? dflt : newPos;
return;
}
}
else if(ax.type === 'date') {
containerOut[attr] = Lib.cleanDate(pos, BADNUM, ax.calendar);
return;
}
}
// finally make sure we have a number (unless date type already returned a string)
containerOut[attr] = isNumeric(pos) ? Number(pos) : dflt;
};
...function cleanNumber(v) {
if(typeof v === 'string') {
v = v.replace(JUNK, '');
}
if(isNumeric(v)) return Number(v);
return BADNUM;
}...
var inc = Math.max(1, (a.length - 1) / 1000),
curvenums = 0,
curvecats = 0,
ai;
for(var i = 0; i < a.length; i += inc) {
ai = a[Math.round(i)];
if(Lib.cleanNumber(ai) !== BADNUM) curvenums++;
else if(typeof ai === 'string' && ai !== '' && ai !== 'None') curvecats
++;
}
return curvecats > curvenums * 2;
}
...coerce = function (containerIn, containerOut, attributes, attribute, dflt) {
var opts = nestedProperty(attributes, attribute).get(),
propIn = nestedProperty(containerIn, attribute),
propOut = nestedProperty(containerOut, attribute),
v = propIn.get();
if(dflt === undefined) dflt = opts.dflt;
/**
* arrayOk: value MAY be an array, then we do no value checking
* at this point, because it can be more complicated than the
* individual form (eg. some array vals can be numbers, even if the
* single values must be color strings)
*/
if(opts.arrayOk && Array.isArray(v)) {
propOut.set(v);
return v;
}
exports.valObjects[opts.valType].coerceFunction(v, propOut, dflt, opts);
return propOut.get();
}...
}
var items = opts.items,
vOut = [];
dflt = Array.isArray(dflt) ? dflt : [];
for(var i = 0; i < items.length; i++) {
exports.coerce(v, vOut, items, '[' + i + ']', dflt[i]);
}
propOut.set(vOut);
},
validateFunction: function(v, opts) {
if(!Array.isArray(v)) return false;
...coerce2 = function (containerIn, containerOut, attributes, attribute, dflt) {
var propIn = nestedProperty(containerIn, attribute),
propOut = exports.coerce(containerIn, containerOut, attributes, attribute, dflt),
valIn = propIn.get();
return (valIn !== undefined && valIn !== null) ? propOut : false;
}...
var letter = options.letter,
font = options.font || {},
defaultTitle = 'Click to enter ' +
(options.title || (letter.toUpperCase() + ' axis')) +
' title';
function coerce2(attr, dflt) {
return Lib.coerce2(containerIn, containerOut, layoutAttributes, attr, dflt);
}
var visible = coerce('visible', !options.cheateronly);
var axType = containerOut.type;
if(axType === 'date') {
...coerceFont = function (coerce, attr, dfltObj) {
var out = {};
dfltObj = dfltObj || {};
out.family = coerce(attr + '.family', dfltObj.family);
out.size = coerce(attr + '.size', dfltObj.size);
out.color = coerce(attr + '.color', dfltObj.color);
return out;
}...
}
plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) {
function coerce(attr, dflt) {
return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt);
}
var globalFont = Lib.coerceFont(coerce, 'font');
coerce('title');
Lib.coerceFont(coerce, 'titlefont', {
family: globalFont.family,
size: Math.round(globalFont.size * 1.4),
color: globalFont.color
...constrain = function (v, v0, v1) {
if(v0 > v1) return Math.max(v1, Math.min(v0, v));
return Math.max(v0, Math.min(v1, v));
}...
posPx.text = posPx.tail + textShift;
// constrain pixel/paper referenced so the draggers are at least
// partially visible
var maxPx = fullLayout[(axLetter === 'x') ? 'width' : 'height'];
if(axRef === 'paper') {
posPx.head = Lib.constrain(posPx.head, 1, maxPx - 1);
}
if(tailRef === 'pixel') {
var shiftPlus = -Math.max(posPx.tail - 3, posPx.text),
shiftMinus = Math.min(posPx.tail + 3, posPx.text) - maxPx;
if(shiftPlus > 0) {
posPx.tail += shiftPlus;
posPx.text += shiftPlus;
...containsAny = function (s, fragments) {
for(var i = 0; i < fragments.length; i++) {
if(s.indexOf(fragments[i]) !== -1) return true;
}
return false;
}...
if(refAutorange(toggledObj, 'x') || refAutorange(toggledObj, 'y') &&
ai.indexOf('updatemenus') === -1) {
flags.docalc = true;
}
}
else if((refAutorange(obji, 'x') || refAutorange(obji, 'y')) &&
!Lib.containsAny(ai, ['color', 'opacity', 'align'
;, 'dash', 'updatemenus'])) {
flags.docalc = true;
}
// prepare the edits object we'll send to applyContainerArrayChanges
if(!arrayEdits[arrayStr]) arrayEdits[arrayStr] = {};
var objEdits = arrayEdits[arrayStr][i];
if(!objEdits) objEdits = arrayEdits[arrayStr][i] = {};
...dateTick0 = function (calendar, sunday) {
if(isWorldCalendar(calendar)) {
return sunday ?
Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] :
Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
}
else {
return sunday ? '2000-01-02' : '2000-01-01';
}
}...
// log with linear ticks: L# where # is the linear tick spacing
// log showing powers plus some intermediates:
// D1 shows all digits, D2 shows 2 and 5
axes.autoTicks = function(ax, roughDTick) {
var base;
if(ax.type === 'date') {
ax.tick0 = Lib.dateTick0(ax.calendar);
// the criteria below are all based on the rough spacing we calculate
// being > half of the final unit - so precalculate twice the rough val
var roughX2 = 2 * roughDTick;
if(roughX2 > ONEAVGYEAR) {
roughDTick /= ONEAVGYEAR;
base = Math.pow(10, Math.floor(Math.log(roughDTick) / Math.LN10));
...dateTime2ms = function (s, calendar) {
// first check if s is a date object
if(exports.isJSDate(s)) {
// Convert to the UTC milliseconds that give the same
// hours as this date has in the local timezone
s = Number(s) - s.getTimezoneOffset() * ONEMIN;
if(s >= MIN_MS && s <= MAX_MS) return s;
return BADNUM;
}
// otherwise only accept strings and numbers
if(typeof s !== 'string' && typeof s !== 'number') return BADNUM;
s = String(s);
var isWorld = isWorldCalendar(calendar);
// to handle out-of-range dates in international calendars, accept
// 'G' as a prefix to force the built-in gregorian calendar.
var s0 = s.charAt(0);
if(isWorld && (s0 === 'G' || s0 === 'g')) {
s = s.substr(1);
calendar = '';
}
var isChinese = isWorld && calendar.substr(0, 7) === 'chinese';
var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP);
if(!match) return BADNUM;
var y = match[1],
m = match[3] || '1',
d = Number(match[5] || 1),
H = Number(match[7] || 0),
M = Number(match[9] || 0),
S = Number(match[11] || 0);
if(isWorld) {
// disallow 2-digit years for world calendars
if(y.length === 2) return BADNUM;
y = Number(y);
var cDate;
try {
var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar);
if(isChinese) {
var isIntercalary = m.charAt(m.length - 1) === 'i';
m = parseInt(m, 10);
cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d);
}
else {
cDate = calInstance.newDate(y, Number(m), d);
}
}
catch(e) { return BADNUM; } // Invalid ... date
if(!cDate) return BADNUM;
return ((cDate.toJD() - EPOCHJD) * ONEDAY) +
(H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC);
}
if(y.length === 2) {
y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST;
}
else y = Number(y);
// new Date uses months from 0; subtract 1 here just so we
// don't have to do it again during the validity test below
m -= 1;
// javascript takes new Date(0..99,m,d) to mean 1900-1999, so
// to support years 0-99 we need to use setFullYear explicitly
// Note that 2000 is a leap year.
var date = new Date(Date.UTC(2000, m, d, H, M));
date.setUTCFullYear(y);
if(date.getUTCMonth() !== m) return BADNUM;
if(date.getUTCDate() !== d) return BADNUM;
return date.getTime() + S * ONESEC;
}...
if(date.getUTCMonth() !== m) return BADNUM;
if(date.getUTCDate() !== d) return BADNUM;
return date.getTime() + S * ONESEC;
};
MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999');
MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999');
// is string s a date? (see above)
exports.isDateTime = function(s, calendar) {
return (exports.dateTime2ms(s, calendar) !== BADNUM);
};
...dfltRange = function (calendar) {
if(isWorldCalendar(calendar)) {
return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar];
}
else {
return ['2000-01-01', '2001-01-01'];
}
}...
*/
ax.cleanRange = function(rangeAttr) {
if(!rangeAttr) rangeAttr = 'range';
var range = Lib.nestedProperty(ax, rangeAttr).get(),
axLetter = (ax._id || 'x').charAt(0),
i, dflt;
if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar);
else if(axLetter === 'y') dflt = constants.DFLTRANGEY;
else dflt = constants.DFLTRANGEX;
// make sure we don't later mutate the defaults
dflt = dflt.slice();
if(!range || range.length !== 2) {
...distinctVals = function (valsIn) {
var vals = valsIn.slice(); // otherwise we sort the original array...
vals.sort(exports.sorterAsc);
var l = vals.length - 1,
minDiff = (vals[l] - vals[0]) || 1,
errDiff = minDiff / (l || 1) / 10000,
v2 = [vals[0]];
for(var i = 0; i < l; i++) {
// make sure values aren't just off by a rounding error
if(vals[i + 1] > vals[i] + errDiff) {
minDiff = Math.min(minDiff, vals[i + 1] - vals[i]);
v2.push(vals[i + 1]);
}
}
return {vals: v2, minDiff: minDiff};
}...
var size0;
if(nbins) size0 = ((dataMax - dataMin) / nbins);
else {
// totally auto: scale off std deviation so the highest bin is
// somewhat taller than the total number of bins, but don't let
// the size get smaller than the 'nice' rounded down minimum
// difference between values
var distinctData = Lib.distinctVals(data),
msexp = Math.pow(10, Math.floor(
Math.log(distinctData.minDiff) / Math.LN10)),
minSize = msexp * Lib.roundUp(
distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
size0 = Math.max(minSize, 2 * Lib.stdev(data) /
Math.pow(data.length, is2d ? 0.25 : 0.4));
...dot = function (x, y) {
if(!(x.length && y.length) || x.length !== y.length) return null;
var len = x.length,
out,
i;
if(x[0].length) {
// mat-vec or mat-mat
out = new Array(len);
for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
}
else if(y[0].length) {
// vec-mat
var yTranspose = exports.transposeRagged(y);
out = new Array(yTranspose.length);
for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
}
else {
// vec-vec
out = 0;
for(i = 0; i < len; i++) out += x[i] * y[i];
}
return out;
}...
var len = x.length,
out,
i;
if(x[0].length) {
// mat-vec or mat-mat
out = new Array(len);
for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
}
else if(y[0].length) {
// vec-mat
var yTranspose = exports.transposeRagged(y);
out = new Array(yTranspose.length);
for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
}
...function ensureArray(out, n) {
if(!Array.isArray(out)) out = [];
// If too long, truncate. (If too short, it will grow
// automatically so we don't care about that case)
out.length = n;
return out;
}...
return d3.range(_count).map(function(d, i) {
return arr[i] || arr[0];
});
};
µ.util.fillArrays = function(_obj, _valueNames, _count) {
_valueNames.forEach(function(d, i) {
_obj[d] = µ.util.ensureArray(_obj[d], _count);
});
return _obj;
};
µ.util.cloneJson = function(json) {
return JSON.parse(JSON.stringify(json));
};
...error = function () {
if(config.logging > 0) {
var messages = ['ERROR:'];
for(var i = 0; i < arguments.length; i++) {
messages.push(arguments[i]);
}
apply(console.error, messages);
}
}...
// so we define the default (plotly.js) behavior here
function defaultSetBackground(gd, bgColor) {
try {
gd._fullLayout._paper.style('background', bgColor);
}
catch(e) {
if(module.exports.logging > 0) {
console.error(e);
}
}
}
...expandObjectPaths = function (data) {
var match, key, prop, datum, idx, dest, trailingPath;
if(typeof data === 'object' && !Array.isArray(data)) {
for(key in data) {
if(data.hasOwnProperty(key)) {
if((match = key.match(dottedPropertyRegex))) {
datum = data[key];
prop = match[1];
delete data[key];
data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths(datum))[
prop]);
} else if((match = key.match(indexedPropertyRegex))) {
datum = data[key];
prop = match[1];
idx = parseInt(match[2]);
delete data[key];
data[prop] = data[prop] || [];
if(match[3] === '.') {
// This is the case where theere are subsequent properties into which
// we must recurse, e.g. transforms[0].value
trailingPath = match[4];
dest = data[prop][idx] = data[prop][idx] || {};
// NB: Extend deep no arrays prevents this from working on multiple
// nested properties in the same object, e.g.
//
// {
// foo[0].bar[1].range
// foo[0].bar[0].range
// }
//
// In this case, the extendDeepNoArrays will overwrite one array with
// the other, so that both properties *will not* be present in the
// result. Fixing this would require a more intelligent tracking
// of changes and merging than extendDeepNoArrays currently accomplishes.
lib.extendDeepNoArrays(dest, lib.objectFromPath(trailingPath, lib.expandObjectPaths(datum)));
} else {
// This is the case where this property is the end of the line,
// e.g. xaxis.range[0]
data[prop][idx] = lib.expandObjectPaths(datum);
}
} else {
data[key] = lib.expandObjectPaths(data[key]);
}
}
}
}
return data;
}...
};
/**
* Iterate through an object in-place, converting dotted properties to objects.
*
* Examples:
*
* lib.expandObjectPaths({'nested.test.path': 'value'});
* => { nested: { test: {path: 'value'}}}
*
* It also handles array notation, e.g.:
*
* lib.expandObjectPaths({'foo[1].bar': 'value'});
* => { foo: [null, {bar: value}] }
*
...extendDeep = function () {
return _extend(arguments, true, false, false);
}...
for(var i = 0; i < args.length; i++) {
arg = args[i];
if(arg === gd) copy[i] = arg;
else if(typeof arg === 'object') {
copy[i] = Array.isArray(arg) ?
Lib.extendDeep([], arg) :
Lib.extendDeepAll({}, arg);
}
else copy[i] = arg;
}
return copy;
}
...extendDeepAll = function () {
return _extend(arguments, true, true, false);
}...
for(var i = 0; i < args.length; i++) {
arg = args[i];
if(arg === gd) copy[i] = arg;
else if(typeof arg === 'object') {
copy[i] = Array.isArray(arg) ?
Lib.extendDeep([], arg) :
Lib.extendDeepAll({}, arg);
}
else copy[i] = arg;
}
return copy;
}
...extendDeepNoArrays = function () {
return _extend(arguments, true, false, true);
}...
if(data.hasOwnProperty(key)) {
if((match = key.match(dottedPropertyRegex))) {
datum = data[key];
prop = match[1];
delete data[key];
data[prop] = lib.extendDeepNoArrays(data[prop] || {}, lib.objectFromPath(key, lib.expandObjectPaths
(datum))[prop]);
} else if((match = key.match(indexedPropertyRegex))) {
datum = data[key];
prop = match[1];
idx = parseInt(match[2]);
delete data[key];
...extendFlat = function () {
return _extend(arguments, false, false, false);
}...
function opaqueSetBackground(gd, bgColor) {
gd._fullLayout._paperdiv.style('background', 'white');
Plotly.defaultConfig.setBackground(gd, bgColor);
}
function setPlotContext(gd, config) {
if(!gd._context) gd._context = Lib.extendFlat({}, Plotly.defaultConfig);
var context = gd._context;
if(config) {
Object.keys(config).forEach(function(key) {
if(key in context) {
if(key === 'setBackground' && config[key] === 'opaque') {
context[key] = opaqueSetBackground;
...function filterUnique(array) {
var seen = {},
out = [],
j = 0;
for(var i = 0; i < array.length; i++) {
var item = array[i];
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}...
/**
* Return news array containing only the unique items
* found in input array.
*
* IMPORTANT: Note that items are considered unique
* if `String({})` is unique. For example;
*
* Lib.filterUnique([ { a: 1 }, { b: 2 } ])
*
* returns [{ a: 1 }]
*
* and
*
* Lib.filterUnique([ '1', 1 ])
*
...function filterVisible(container) {
var out = [];
for(var i = 0; i < container.length; i++) {
var item = container[i];
if(item.visible === true) out.push(item);
}
return out;
}...
var Axes = require('../../plots/cartesian/axes');
var draw = require('./draw').draw;
module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = Lib.filterVisible(fullLayout.annotations);
if(!annotationList.length || !gd._fullData.length) return;
var annotationAxes = {};
annotationList.forEach(function(ann) {
annotationAxes[ann.xref] = true;
annotationAxes[ann.yref] = true;
...findBin = function (val, bins, linelow) {
if(isNumeric(bins.start)) {
return linelow ?
Math.ceil((val - bins.start) / bins.size) - 1 :
Math.floor((val - bins.start) / bins.size);
}
else {
var n1 = 0,
n2 = bins.length,
c = 0,
n,
test;
if(bins[bins.length - 1] >= bins[0]) {
test = linelow ? lessThan : lessOrEqual;
} else {
test = linelow ? greaterOrEqual : greaterThan;
}
// c is just to avoid infinite loops if there's an error
while(n1 < n2 && c++ < 100) {
n = Math.floor((n1 + n2) / 2);
if(test(bins[n], val)) n1 = n + 1;
else n2 = n;
}
if(c > 90) loggers.log('Long binary search...');
return n1 - 1;
}
}...
var i1, i2, text;
if(hasColumnText) text = Lib.init2dArray(col2vals.length, col1vals.length);
for(i = 0; i < colLen; i++) {
if(col1[i] !== BADNUM && col2[i] !== BADNUM) {
i1 = Lib.findBin(col1[i] + col1dv.minDiff / 2, col1vals);
i2 = Lib.findBin(col2[i] + col2dv.minDiff / 2, col2vals);
for(j = 0; j < arrayVarNames.length; j++) {
arrayVarName = arrayVarNames[j];
arrayVar = trace[arrayVarName];
newArray = newArrays[j];
newArray[i2][i1] = arrayVar[i];
...findExactDates = function (data, calendar) {
var exactYears = 0,
exactMonths = 0,
exactDays = 0,
blankCount = 0,
d,
di;
var calInstance = (
isWorldCalendar(calendar) &&
Registry.getComponentMethod('calendars', 'getCal')(calendar)
);
for(var i = 0; i < data.length; i++) {
di = data[i];
// not date data at all
if(!isNumeric(di)) {
blankCount ++;
continue;
}
// not an exact date
if(di % ONEDAY) continue;
if(calInstance) {
try {
d = calInstance.fromJD(di / ONEDAY + EPOCHJD);
if(d.day() === 1) {
if(d.month() === 1) exactYears++;
else exactMonths++;
}
else exactDays++;
}
catch(e) {
// invalid date in this calendar - ignore it here.
}
}
else {
d = new Date(di);
if(d.getUTCDate() === 1) {
if(d.getUTCMonth() === 0) exactYears++;
else exactMonths++;
}
else exactDays++;
}
}
exactMonths += exactYears;
exactDays += exactMonths;
var dataCount = data.length - blankCount;
return {
exactYears: exactYears / dataCount,
exactMonths: exactMonths / dataCount,
exactDays: exactDays / dataCount
};
}...
}
}
return binStart;
}
function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
var stats = Lib.findExactDates(data, calendar);
// number of data points that needs to be an exact value
// to shift that increment to (near) the bin center
var threshold = 0.8;
if(stats.exactDays > threshold) {
var numMonths = Number(dtick.substr(1));
...formatDate = function (x, fmt, tr, calendar) {
var headStr,
dateStr;
calendar = isWorldCalendar(calendar) && calendar;
if(fmt) return modDateFormat(fmt, x, calendar);
if(calendar) {
try {
var dateJD = Math.floor((x + 0.05) / ONEDAY) + EPOCHJD,
cDate = Registry.getComponentMethod('calendars', 'getCal')(calendar)
.fromJD(dateJD);
if(tr === 'y') dateStr = yearFormatWorld(cDate);
else if(tr === 'm') dateStr = monthFormatWorld(cDate);
else if(tr === 'd') {
headStr = yearFormatWorld(cDate);
dateStr = dayFormatWorld(cDate);
}
else {
headStr = yearMonthDayFormatWorld(cDate);
dateStr = formatTime(x, tr);
}
}
catch(e) { return 'Invalid'; }
}
else {
var d = new Date(Math.floor(x + 0.05));
if(tr === 'y') dateStr = yearFormat(d);
else if(tr === 'm') dateStr = monthFormat(d);
else if(tr === 'd') {
headStr = yearFormat(d);
dateStr = dayFormat(d);
}
else {
headStr = yearMonthDayFormat(d);
dateStr = formatTime(x, tr);
}
}
return dateStr + (headStr ? '\n' + headStr : '');
}...
dateStr, h, m, s, msec10, d;
if(isWorldCalendar(calendar)) {
var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD,
timeMs = Math.floor(mod(ms, ONEDAY));
try {
dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
.fromJD(dateJD).formatDate('yyyy-mm-dd');
}
catch(e) {
// invalid date in this calendar - fall back to Gyyyy-mm-dd
dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
}
// yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
...getPlotDiv = function (el) {
for(; el && el.removeAttribute; el = el.parentNode) {
if(lib.isPlotDiv(el)) return el;
}
}...
_context.style('pointer-events', 'all');
}
if(_callback) _callback.call(that);
}
if(tex) {
var gd = Lib.getPlotDiv(that.node());
((gd && gd._promises) || []).push(new Promise(function(resolve) {
that.style({visibility: 'hidden'});
var config = {fontSize: parseInt(that.style('font-size'), 10)};
texToSVG(tex[2], config, function(_svgEl, _glyphDefs, _svgBBox) {
parent.selectAll('svg.' + svgClass).remove();
parent.selectAll('g.' + svgClass + '-group').remove();
...function identity(d) { return d; }n/a
incrementMonth = function (ms, dMonth, calendar) {
calendar = isWorldCalendar(calendar) && calendar;
// pull time out and operate on pure dates, then add time back at the end
// this gives maximum precision - not that we *normally* care if we're
// incrementing by month, but better to be safe!
var timeMs = mod(ms, ONEDAY);
ms = Math.round(ms - timeMs);
if(calendar) {
try {
var dateJD = Math.round(ms / ONEDAY) + EPOCHJD,
calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar),
cDate = calInstance.fromJD(dateJD);
if(dMonth % 12) calInstance.add(cDate, dMonth, 'm');
else calInstance.add(cDate, dMonth / 12, 'y');
return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs;
}
catch(e) {
logError('invalid ms ' + ms + ' in calendar ' + calendar);
// then keep going in gregorian even though the result will be 'Invalid'
}
}
var y = new Date(ms + THREEDAYS);
return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS;
}...
if(isNumeric(dtick)) return x + axSign * dtick;
// everything else is a string, one character plus a number
var tType = dtick.charAt(0),
dtSigned = axSign * Number(dtick.substr(1));
// Dates: months (or years - see Lib.incrementMonth)
if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar);
// Log scales: Linear, Digits
else if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
// log10 of 2,5,10, or all digits (logs just have to be
// close enough to round)
else if(tType === 'D') {
...init2dArray = function (rowLength, colLength) {
var array = new Array(rowLength);
for(var i = 0; i < rowLength; i++) array[i] = new Array(colLength);
return array;
}...
var col1dv = Lib.distinctVals(col1),
col1vals = col1dv.vals,
col2dv = Lib.distinctVals(col2),
col2vals = col2dv.vals,
newArrays = [];
for(i = 0; i < arrayVarNames.length; i++) {
newArrays[i] = Lib.init2dArray(col2vals.length, col1vals.length);
}
var i1, i2, text;
if(hasColumnText) text = Lib.init2dArray(col2vals.length, col1vals.length);
for(i = 0; i < colLen; i++) {
...interp = function (arr, n) {
if(!isNumeric(n)) throw 'n should be a finite number';
n = n * arr.length - 0.5;
if(n < 0) return arr[0];
if(n > arr.length - 1) return arr[arr.length - 1];
var frac = n % 1;
return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)];
}n/a
function isArray(a) {
return Array.isArray(a) || ab.isView(a);
}...
*/
'use strict';
// similar to Lib.mergeArray, but using inside a loop
module.exports = function arrayToCalcItem(traceAttr, calcItem, calcAttr, i) {
if(Array.isArray(traceAttr)) calcItem[calcAttr] = traceAttr[i];
};
...isD3Selection = function (obj) {
return obj && (typeof obj.classed === 'function');
}...
return hoverLabel.node();
};
fx.loneUnhover = function(containerOrSelection) {
// duck type whether the arg is a d3 selection because ie9 doesn't
// handle instanceof like modern browsers do.
var selection = Lib.isD3Selection(containerOrSelection) ?
containerOrSelection :
d3.select(containerOrSelection);
selection.selectAll('g.hovertext').remove();
selection.selectAll('.spikeline').remove();
};
...isDateTime = function (s, calendar) {
return (exports.dateTime2ms(s, calendar) !== BADNUM);
}...
// NOTE: if someone puts in a year as a number rather than a string,
// this will mistakenly convert it thinking it's milliseconds from 1970
// that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
v = exports.ms2DateTimeLocal(+v);
if(!v && dflt !== undefined) return dflt;
}
else if(!exports.isDateTime(v, calendar)) {
logError('unrecognized date', v);
return dflt;
}
return v;
};
/*
...isIE = function () {
return typeof window.navigator.msSaveBlob !== 'undefined';
}...
}
// see comments within svgtoimg for additional
// discussion of problems with IE
// can now draw to canvas, but CORS tainted canvas
// does not allow toDataURL
// svg format will work though
if(Lib.isIE() && opts.format !== 'svg') {
reject(new Error('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.'));
}
gd._snapshotInProgress = true;
var promise = toImage(gd, opts);
var filename = opts.filename || gd.fn || 'newplot';
...isJSDate = function (v) {
return typeof v === 'object' && v !== null && typeof v.getTime === 'function';
}...
* make now will cover all possibilities. mostly this will all be taken
* care of in initial parsing, should only be an issue for hand-entered data
* currently (2016) this range is:
* 1946-2045
*/
exports.dateTime2ms = function(s, calendar) {
// first check if s is a date object
if(exports.isJSDate(s)) {
// Convert to the UTC milliseconds that give the same
// hours as this date has in the local timezone
s = Number(s) - s.getTimezoneOffset() * ONEMIN;
if(s >= MIN_MS && s <= MAX_MS) return s;
return BADNUM;
}
// otherwise only accept strings and numbers
...function isPlainObject(obj) {
// We need to be a little less strict in the `imagetest` container because
// of how async image requests are handled.
//
// N.B. isPlainObject(new Constructor()) will return true in `imagetest`
if(window && window.process && window.process.versions) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
return (
Object.prototype.toString.call(obj) === '[object Object]' &&
Object.getPrototypeOf(obj) === Object.prototype
);
}...
}
}
var annotationsLen = Array.isArray(layout.annotations) ? layout.annotations.length : 0;
for(i = 0; i < annotationsLen; i++) {
var ann = layout.annotations[i];
if(!Lib.isPlainObject(ann)) continue;
if(ann.ref) {
if(ann.ref === 'paper') {
ann.xref = 'paper';
ann.yref = 'paper';
}
else if(ann.ref === 'data') {
...isPlotDiv = function (el) {
var el3 = d3.select(el);
return el3.node() instanceof HTMLElement &&
el3.size() &&
el3.classed('js-plotly-plot');
}...
}
return false;
};
// get the parent Plotly plot of any element. Whoo jquery-free tree climbing!
lib.getPlotDiv = function(el) {
for(; el && el.removeAttribute; el = el.parentNode) {
if(lib.isPlotDiv(el)) return el;
}
};
lib.isPlotDiv = function(el) {
var el3 = d3.select(el);
return el3.node() instanceof HTMLElement &&
el3.size() &&
...len = function (data) {
return exports.aggNums(function(a) { return a + 1; }, 0, data);
}...
* even need to use aggNums instead of .length, to toss out non-numerics
*/
exports.len = function(data) {
return exports.aggNums(function(a) { return a + 1; }, 0, data);
};
exports.mean = function(data, len) {
if(!len) len = exports.len(data);
return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
};
exports.variance = function(data, len, mean) {
if(!len) len = exports.len(data);
if(!isNumeric(mean)) mean = exports.mean(data, len);
...log = function () {
if(config.logging > 1) {
var messages = ['LOG:'];
for(var i = 0; i < arguments.length; i++) {
messages.push(arguments[i]);
}
apply(console.trace || console.log, messages);
}
}...
* @param {string} thisType
* @param {array of strings} categoriesIn all the categories this type is in,
* tested by calls: traceIs(trace, oneCategory)
* @param {object} meta meta information about the trace type
*/
exports.register = function(_module, thisType, categoriesIn, meta) {
if(exports.modules[thisType]) {
Loggers.log('Type ' + thisType + ' already registered');
return;
}
var categoryObj = {};
for(var i = 0; i < categoriesIn.length; i++) {
categoryObj[categoriesIn[i]] = true;
exports.allCategories[categoriesIn[i]] = true;
...mean = function (data, len) {
if(!len) len = exports.len(data);
return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
}...
exports.mean = function(data, len) {
if(!len) len = exports.len(data);
return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
};
exports.variance = function(data, len, mean) {
if(!len) len = exports.len(data);
if(!isNumeric(mean)) mean = exports.mean(data, len);
return exports.aggNums(function(a, b) {
return a + Math.pow(b - mean, 2);
}, 0, data) / len;
};
exports.stdev = function(data, len, mean) {
...mergeArray = function (traceAttr, cd, cdAttr) {
if(Array.isArray(traceAttr)) {
var imax = Math.min(traceAttr.length, cd.length);
for(var i = 0; i < imax; i++) cd[i][cdAttr] = traceAttr[i];
}
}n/a
minExtend = function (obj1, obj2) {
var objOut = {};
if(typeof obj2 !== 'object') obj2 = {};
var arrayLen = 3,
keys = Object.keys(obj1),
i,
k,
v;
for(i = 0; i < keys.length; i++) {
k = keys[i];
v = obj1[k];
if(k.charAt(0) === '_' || typeof v === 'function') continue;
else if(k === 'module') objOut[k] = v;
else if(Array.isArray(v)) objOut[k] = v.slice(0, arrayLen);
else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]);
else objOut[k] = v;
}
keys = Object.keys(obj2);
for(i = 0; i < keys.length; i++) {
k = keys[i];
v = obj2[k];
if(typeof v !== 'object' || !(k in objOut) || typeof objOut[k] !== 'object') {
objOut[k] = v;
}
}
return objOut;
}...
v;
for(i = 0; i < keys.length; i++) {
k = keys[i];
v = obj1[k];
if(k.charAt(0) === '_' || typeof v === 'function') continue;
else if(k === 'module') objOut[k] = v;
else if(Array.isArray(v)) objOut[k] = v.slice(0, arrayLen);
else if(v && (typeof v === 'object')) objOut[k] = lib.minExtend(obj1[k], obj2[k]);
else objOut[k] = v;
}
keys = Object.keys(obj2);
for(i = 0; i < keys.length; i++) {
k = keys[i];
v = obj2[k];
...function mod(v, d) {
var out = v % d;
return out < 0 ? out + d : out;
}...
else if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10;
// log10 of 2,5,10, or all digits (logs just have to be
// close enough to round)
else if(tType === 'D') {
var tickset = (dtick === 'D2') ? roundLog2 : roundLog1,
x2 = x + axSign * 0.01,
frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev);
return Math.floor(x2) +
Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10;
}
else throw 'unrecognized dtick ' + String(dtick);
};
...ms2DateTime = function (ms, r, calendar) {
if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM;
if(!r) r = 0;
var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10),
msRounded = Math.round(ms - msecTenths / 10),
dateStr, h, m, s, msec10, d;
if(isWorldCalendar(calendar)) {
var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD,
timeMs = Math.floor(mod(ms, ONEDAY));
try {
dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar)
.fromJD(dateJD).formatDate('yyyy-mm-dd');
}
catch(e) {
// invalid date in this calendar - fall back to Gyyyy-mm-dd
dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded));
}
// yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does
// other things for a few calendars, so we can't trust it. Just pad
// it manually (after the '-' if there is one)
if(dateStr.charAt(0) === '-') {
while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1);
}
else {
while(dateStr.length < 10) dateStr = '0' + dateStr;
}
// TODO: if this is faster, we could use this block for extracting
// the time components of regular gregorian too
h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0;
m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0;
s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0;
msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0;
}
else {
d = new Date(msRounded);
dateStr = utcFormat('%Y-%m-%d')(d);
// <90 days: add hours and minutes - never *only* add hours
h = (r < NINETYDAYS) ? d.getUTCHours() : 0;
m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0;
// <3 hours: add seconds
s = (r < THREEHOURS) ? d.getUTCSeconds() : 0;
// <5 minutes: add ms (plus one extra digit, this is msec*10)
msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0;
}
return includeTime(dateStr, h, m, s, msec10);
}n/a
ms2DateTimeLocal = function (ms) {
if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM;
var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10),
d = new Date(Math.round(ms - msecTenths / 10)),
dateStr = d3.time.format('%Y-%m-%d')(d),
h = d.getHours(),
m = d.getMinutes(),
s = d.getSeconds(),
msec10 = d.getUTCMilliseconds() * 10 + msecTenths;
return includeTime(dateStr, h, m, s, msec10);
}...
logError('JS Dates and milliseconds are incompatible with world calendars', v);
return dflt;
}
// NOTE: if someone puts in a year as a number rather than a string,
// this will mistakenly convert it thinking it's milliseconds from 1970
// that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds
v = exports.ms2DateTimeLocal(+v);
if(!v && dflt !== undefined) return dflt;
}
else if(!exports.isDateTime(v, calendar)) {
logError('unrecognized date', v);
return dflt;
}
return v;
...function nestedProperty(container, propStr) {
if(isNumeric(propStr)) propStr = String(propStr);
else if(typeof propStr !== 'string' ||
propStr.substr(propStr.length - 4) === '[-1]') {
throw 'bad property string';
}
var j = 0,
propParts = propStr.split('.'),
indexed,
indices,
i;
// check for parts of the nesting hierarchy that are numbers (ie array elements)
while(j < propParts.length) {
// look for non-bracket chars, then any number of [##] blocks
indexed = String(propParts[j]).match(/^([^\[\]]*)((\[\-?[0-9]*\])+)$/);
if(indexed) {
if(indexed[1]) propParts[j] = indexed[1];
// allow propStr to start with bracketed array indices
else if(j === 0) propParts.splice(0, 1);
else throw 'bad property string';
indices = indexed[2]
.substr(1, indexed[2].length - 2)
.split('][');
for(i = 0; i < indices.length; i++) {
j++;
propParts.splice(j, 0, Number(indices[i]));
}
}
j++;
}
if(typeof container !== 'object') {
return badContainer(container, propStr, propParts);
}
return {
set: npSet(container, propParts, propStr),
get: npGet(container, propParts),
astr: propStr,
parts: propParts,
obj: container
};
}...
* you can also swap other things than x/y by providing part1 and part2
*/
lib.swapAttrs = function(cont, attrList, part1, part2) {
if(!part1) part1 = 'x';
if(!part2) part2 = 'y';
for(var i = 0; i < attrList.length; i++) {
var attr = attrList[i],
xp = lib.nestedProperty(cont, attr.replace('?', part1)),
yp = lib.nestedProperty(cont, attr.replace('?', part2)),
temp = xp.get();
xp.set(yp.get());
yp.set(temp);
}
};
...noneOrAll = function (containerIn, containerOut, attrList) {
/**
* some attributes come together, so if you have one of them
* in the input, you should copy the default values of the others
* to the input as well.
*/
if(!containerIn) return;
var hasAny = false,
hasAll = true,
i,
val;
for(i = 0; i < attrList.length; i++) {
val = containerIn[attrList[i]];
if(val !== undefined && val !== null) hasAny = true;
else hasAll = false;
}
if(hasAny && !hasAll) {
for(i = 0; i < attrList.length; i++) {
containerIn[attrList[i]] = containerOut[attrList[i]];
}
}
}...
coerce(axLetter + 'anchor');
// xshift, yshift
coerce(axLetter + 'shift');
}
// if you have one coordinate you should have both
Lib.noneOrAll(annIn, annOut, ['x', 'y']);
if(showArrow) {
coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine);
coerce('arrowhead');
coerce('arrowsize');
coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2);
coerce('standoff');
...function noop() {}n/a
notifier = function (text, displayLength) {
if(NOTEDATA.indexOf(text) !== -1) return;
NOTEDATA.push(text);
var ts = 1000;
if(isNumeric(displayLength)) ts = displayLength;
else if(displayLength === 'long') ts = 3000;
var notifierContainer = d3.select('body')
.selectAll('.plotly-notifier')
.data([0]);
notifierContainer.enter()
.append('div')
.classed('plotly-notifier', true);
var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA);
function killNote(transition) {
transition
.duration(700)
.style('opacity', 0)
.each('end', function(thisText) {
var thisIndex = NOTEDATA.indexOf(thisText);
if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1);
d3.select(this).remove();
});
}
notes.enter().append('div')
.classed('notifier-note', true)
.style('opacity', 0)
.each(function(thisText) {
var note = d3.select(this);
note.append('button')
.classed('notifier-close', true)
.html('×')
.on('click', function() {
note.transition().call(killNote);
});
var p = note.append('p');
var lines = thisText.split(/<br\s*\/?>/g);
for(var i = 0; i < lines.length; i++) {
if(i) p.append('br');
p.append('span').text(lines[i]);
}
note.transition()
.duration(700)
.style('opacity', 1)
.transition()
.delay(ts)
.call(killNote);
});
}...
modeBarButtons.toImage = {
name: 'toImage',
title: 'Download plot as a png',
icon: Icons.camera,
click: function(gd) {
var format = 'png';
Lib.notifier('Taking snapshot - this may take a few seconds', 'long'
;);
if(Lib.isIE()) {
Lib.notifier('IE only supports svg. Changing format to svg.', 'long');
format = 'svg';
}
downloadImage(gd, {'format': format})
...numSeparate = function (value, separators, separatethousands) {
if(!separatethousands) separatethousands = false;
if(typeof separators !== 'string' || separators.length === 0) {
throw new Error('Separator string required for formatting!');
}
if(typeof value === 'number') {
value = String(value);
}
var thousandsRe = /(\d+)(\d{3})/,
decimalSep = separators.charAt(0),
thouSep = separators.charAt(1);
var x = value.split('.'),
x1 = x[0],
x2 = x.length > 1 ? decimalSep + x[1] : '';
// Years are ignored for thousands separators
if(thouSep && (x.length > 1 || x1.length > 4 || separatethousands)) {
while(thousandsRe.test(x1)) {
x1 = x1.replace(thousandsRe, '$1' + thouSep + '$2');
}
}
return x1 + x2;
}...
return data;
};
/**
* Converts value to string separated by the provided separators.
*
* @example
* lib.numSeparate(2016, '.,');
* // returns '2016'
*
* @example
* lib.numSeparate(3000, '.,', true);
* // returns '3,000'
*
* @example
...objectFromPath = function (path, value) {
var keys = path.split('.'),
tmpObj,
obj = tmpObj = {};
for(var i = 0; i < keys.length; i++) {
var key = keys[i];
var el = null;
var parts = keys[i].match(/(.*)\[([0-9]+)\]/);
if(parts) {
key = parts[1];
el = parts[2];
tmpObj = tmpObj[key] = [];
if(i === keys.length - 1) {
tmpObj[el] = value;
} else {
tmpObj[el] = {};
}
tmpObj = tmpObj[el];
} else {
if(i === keys.length - 1) {
tmpObj[key] = value;
} else {
tmpObj[key] = {};
}
tmpObj = tmpObj[key];
}
}
return obj;
}...
/**
* Converts a string path to an object.
*
* When given a string containing an array element, it will create a `null`
* filled array of the given size.
*
* @example
* lib.objectFromPath('nested.test[2].path', 'value');
* // returns { nested: { test: [null, null, { path: 'value' }]}
*
* @param {string} path to nested value
* @param {*} any value to be set
*
* @return {Object} the constructed object with a full nested path
*/
...pauseEvent = function (e) {
if(e.stopPropagation) e.stopPropagation();
if(e.preventDefault) e.preventDefault();
e.cancelBubble = true;
return false;
}...
// but _ ensures this setting won't leave their page
if(!gd._context.scrollZoom && !fullLayout._enablescrollzoom) {
return;
}
// If a transition is in progress, then disable any behavior:
if(gd._transitioningWithDuration) {
return Lib.pauseEvent(e);
}
var pc = gd.querySelector('.plotly');
recomputeAxisLists();
// if the plot has scrollbars (more than a tiny excess)
...function pushUnique(array, item) {
if(item instanceof RegExp) {
var itemStr = item.toString(),
i;
for(i = 0; i < array.length; i++) {
if(array[i] instanceof RegExp && array[i].toString() === itemStr) {
return array;
}
}
array.push(item);
}
else if(item && array.indexOf(item) === -1) array.push(item);
return array;
}...
function pushModule(fullTrace) {
dataOut.push(fullTrace);
var _module = fullTrace._module;
if(!_module) return;
Lib.pushUnique(modules, _module);
Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule);
cnt++;
}
var carpetIndex = {};
var carpetDependents = [];
...function randstr(existing, bits, base) {
/*
* Include number of bits, the base of the string you want
* and an optional array of existing strings to avoid.
*/
if(!base) base = 16;
if(bits === undefined) bits = 24;
if(bits <= 0) return '0';
var digits = Math.log(Math.pow(2, bits)) / Math.log(base),
res = '',
i,
b,
x;
for(i = 2; digits === Infinity; i *= 2) {
digits = Math.log(Math.pow(2, bits / i)) / Math.log(base) * i;
}
var rem = digits - Math.floor(digits);
for(i = 0; i < Math.floor(digits); i++) {
x = Math.floor(Math.random() * base).toString(base);
res = x + res;
}
if(rem) {
b = Math.pow(base, rem);
x = Math.floor(Math.random() * b).toString(base);
res = x + res;
}
var parsed = parseInt(res, base);
if((existing && (existing.indexOf(res) > -1)) ||
(parsed !== Infinity && parsed >= Math.pow(2, bits))) {
return randstr(existing, bits, base);
}
else return res;
}...
function cleanEscapesForTex(s) {
return s.replace(/(<|<|<)/g, '\\lt ')
.replace(/(>|>|>)/g, '\\gt ');
}
function texToSVG(_texString, _config, _callback) {
var randomID = 'math-output-' + Lib.randstr([], 64);
var tmpDiv = d3.select('body').append('div')
.attr({id: randomID})
.style({visibility: 'hidden', position: 'absolute'})
.style({'font-size': _config.fontSize + 'px'})
.text(cleanEscapesForTex(_texString));
MathJax.Hub.Queue(['Typeset', MathJax.Hub, tmpDiv.node()], function() {
...function relinkPrivateKeys(toContainer, fromContainer) {
var keys = Object.keys(fromContainer || {});
for(var i = 0; i < keys.length; i++) {
var k = keys[i],
fromVal = fromContainer[k],
toVal = toContainer[k];
if(k.charAt(0) === '_' || typeof fromVal === 'function') {
// if it already exists at this point, it's something
// that we recreate each time around, so ignore it
if(k in toContainer) continue;
toContainer[k] = fromVal;
}
else if(isArray(fromVal) && isArray(toVal) && isPlainObject(fromVal[0])) {
// recurse into arrays containers
for(var j = 0; j < fromVal.length; j++) {
if(isPlainObject(fromVal[j]) && isPlainObject(toVal[j])) {
relinkPrivateKeys(toVal[j], fromVal[j]);
}
}
}
else if(isPlainObject(fromVal) && isPlainObject(toVal)) {
// recurse into objects, but only if they still exist
relinkPrivateKeys(toVal, fromVal);
if(!Object.keys(toVal).length) delete toContainer[k];
}
}
}...
}
// in case this array gets its defaults rebuilt independent of the whole layout,
// relink the private keys just for this array.
if(Lib.isArray(previousContOut)) {
var len = Math.min(previousContOut.length, contOut.length);
for(i = 0; i < len; i++) {
Lib.relinkPrivateKeys(contOut[i], previousContOut[i]);
}
}
};
...removeElement = function (el) {
var elParent = el && el.parentNode;
if(elParent) elParent.removeChild(el);
}...
this.element.removeChild(this.element.firstChild);
}
this.hasLogo = false;
};
proto.destroy = function() {
Lib.removeElement(this.container.querySelector('.modebar'));
};
function createModeBar(gd, buttons) {
var fullLayout = gd._fullLayout;
var modeBar = new ModeBar({
graphInfo: gd,
...rotationMatrix = function (alpha) {
var a = alpha * Math.PI / 180;
return [[Math.cos(a), -Math.sin(a), 0],
[Math.sin(a), Math.cos(a), 0],
[0, 0, 1]];
}...
[0, 0, 1]];
};
// rotate by alpha around (x,y)
exports.rotationXYMatrix = function(a, x, y) {
return exports.dot(
exports.dot(exports.translationMatrix(x, y),
exports.rotationMatrix(a)),
exports.translationMatrix(-x, -y));
};
// applies a 2D transformation matrix to either x and y params or an [x,y] array
exports.apply2DTransform = function(transform) {
return function() {
var args = arguments;
...rotationXYMatrix = function (a, x, y) {
return exports.dot(
exports.dot(exports.translationMatrix(x, y),
exports.rotationMatrix(a)),
exports.translationMatrix(-x, -y));
}...
tailX = annPosPx.x.tail + dx,
tailY = annPosPx.y.tail + dy,
textX = annPosPx.x.text + dx,
textY = annPosPx.y.text + dy,
// find the edge of the text box, where we'll start the arrow:
// create transform matrix to rotate the text box corners
transform = Lib.rotationXYMatrix(textangle, textX, textY),
applyTransform = Lib.apply2DTransform(transform),
applyTransform2 = Lib.apply2DTransform2(transform),
// calculate and transform bounding box
width = +annTextBG.attr('width'),
height = +annTextBG.attr('height'),
xLeft = textX - 0.5 * width,
...roundUp = function (val, arrayIn, reverse) {
var low = 0,
high = arrayIn.length - 1,
mid,
c = 0,
dlow = reverse ? 0 : 1,
dhigh = reverse ? 1 : 0,
rounded = reverse ? Math.ceil : Math.floor;
// c is just to avoid infinite loops if there's an error
while(low < high && c++ < 100) {
mid = rounded((low + high) / 2);
if(arrayIn[mid] <= val) low = mid + dlow;
else high = mid - dhigh;
}
return arrayIn[low];
}...
// totally auto: scale off std deviation so the highest bin is
// somewhat taller than the total number of bins, but don't let
// the size get smaller than the 'nice' rounded down minimum
// difference between values
var distinctData = Lib.distinctVals(data),
msexp = Math.pow(10, Math.floor(
Math.log(distinctData.minDiff) / Math.LN10)),
minSize = msexp * Lib.roundUp(
distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
size0 = Math.max(minSize, 2 * Lib.stdev(data) /
Math.pow(data.length, is2d ? 0.25 : 0.4));
// fallback if ax.d2c output BADNUMs
// e.g. when user try to plot categorical bins
// on a layout.xaxis.type: 'linear'
...simpleMap = function (array, func, x1, x2) {
var len = array.length,
out = new Array(len);
for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2);
return out;
}...
}
var j, minpt, maxpt, minbest, maxbest, dp, dv,
mbest = 0,
axReverse = false;
if(ax.range) {
var rng = Lib.simpleMap(ax.range, ax.r2l);
axReverse = rng[1] < rng[0];
}
// one-time setting to easily reverse the axis
// when plotting from code
if(ax.autorange === 'reversed') {
axReverse = true;
...smooth = function (arrayIn, FWHM) {
FWHM = Math.round(FWHM) || 0; // only makes sense for integers
if(FWHM < 2) return arrayIn;
var alen = arrayIn.length,
alen2 = 2 * alen,
wlen = 2 * FWHM - 1,
w = new Array(wlen),
arrayOut = new Array(alen),
i,
j,
k,
v;
// first make the window array
for(i = 0; i < wlen; i++) {
w[i] = (1 - Math.cos(Math.PI * (i + 1) / FWHM)) / (2 * FWHM);
}
// now do the convolution
for(i = 0; i < alen; i++) {
v = 0;
for(j = 0; j < wlen; j++) {
k = i + j + 1 - FWHM;
// multibounce
if(k < -alen) k -= alen2 * Math.round(k / alen2);
else if(k >= alen2) k -= alen2 * Math.floor(k / alen2);
// single bounce
if(k < 0) k = - 1 - k;
else if(k >= alen) k = alen2 - 1 - k;
v += arrayIn[k] * w[j];
}
arrayOut[i] = v;
}
return arrayOut;
}n/a
sorterAsc = function (a, b) { return a - b; }n/a
sorterDes = function (a, b) { return b - a; }n/a
stdev = function (data, len, mean) {
return Math.sqrt(exports.variance(data, len, mean));
}...
// the size get smaller than the 'nice' rounded down minimum
// difference between values
var distinctData = Lib.distinctVals(data),
msexp = Math.pow(10, Math.floor(
Math.log(distinctData.minDiff) / Math.LN10)),
minSize = msexp * Lib.roundUp(
distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
size0 = Math.max(minSize, 2 * Lib.stdev(data) /
Math.pow(data.length, is2d ? 0.25 : 0.4));
// fallback if ax.d2c output BADNUMs
// e.g. when user try to plot categorical bins
// on a layout.xaxis.type: 'linear'
if(!isNumeric(size0)) size0 = 1;
}
...stripTrailingSlash = function (str) {
if(str.substr(-1) === '/') return str.substr(0, str.length - 1);
return str;
}n/a
swapAttrs = function (cont, attrList, part1, part2) {
if(!part1) part1 = 'x';
if(!part2) part2 = 'y';
for(var i = 0; i < attrList.length; i++) {
var attr = attrList[i],
xp = lib.nestedProperty(cont, attr.replace('?', part1)),
yp = lib.nestedProperty(cont, attr.replace('?', part2)),
temp = xp.get();
xp.set(yp.get());
yp.set(temp);
}
}...
(Object.keys(outer[innerStr]).length === 0);
}
// swap all the data and data attributes associated with x and y
exports.swapXYData = function(trace) {
var i;
Lib.swapAttrs(trace, ['?', '?0', 'd?', '?bins'
;, 'nbins?', 'autobin?', '?src', 'error_?']);
if(Array.isArray(trace.z) && Array.isArray(trace.z[0])) {
if(trace.transpose) delete trace.transpose;
else trace.transpose = true;
}
if(trace.error_x && trace.error_y) {
var errorY = trace.error_y,
copyYstyle = ('copy_ystyle' in errorY) ? errorY.copy_ystyle :
...syncOrAsync = function (sequence, arg, finalStep) {
var ret, fni;
function continueAsync() {
return lib.syncOrAsync(sequence, arg, finalStep);
}
while(sequence.length) {
fni = sequence.splice(0, 1)[0];
ret = fni(arg);
if(ret && ret.then) {
return ret.then(continueAsync)
.then(undefined, lib.promiseError);
}
}
return finalStep && finalStep(arg);
}...
* this doesn't happen yet because we want to make sure
* that it gets reported
*/
lib.syncOrAsync = function(sequence, arg, finalStep) {
var ret, fni;
function continueAsync() {
return lib.syncOrAsync(sequence, arg, finalStep);
}
while(sequence.length) {
fni = sequence.splice(0, 1)[0];
ret = fni(arg);
if(ret && ret.then) {
...titleCase = function (s) {
return s.charAt(0).toUpperCase() + s.substr(1);
}n/a
function toLogRange(val, range) {
if(val > 0) return Math.log(val) / Math.LN10;
// move a negative value reference to a log axis - just put the
// result at the lowest range value on the plot (or if the range also went negative,
// one millionth of the top of the range)
var newVal = Math.log(Math.min(range[0], range[1])) / Math.LN10;
if(!isNumeric(newVal)) newVal = Math.log(Math.max(range[0], range[1])) / Math.LN10 - 6;
return newVal;
}n/a
translationMatrix = function (x, y) {
return [[1, 0, x], [0, 1, y], [0, 0, 1]];
}...
[Math.sin(a), Math.cos(a), 0],
[0, 0, 1]];
};
// rotate by alpha around (x,y)
exports.rotationXYMatrix = function(a, x, y) {
return exports.dot(
exports.dot(exports.translationMatrix(x, y),
exports.rotationMatrix(a)),
exports.translationMatrix(-x, -y));
};
// applies a 2D transformation matrix to either x and y params or an [x,y] array
exports.apply2DTransform = function(transform) {
return function() {
...transposeRagged = function (z) {
var maxlen = 0,
zlen = z.length,
i,
j;
// Maximum row length:
for(i = 0; i < zlen; i++) maxlen = Math.max(maxlen, z[i].length);
var t = new Array(maxlen);
for(i = 0; i < maxlen; i++) {
t[i] = new Array(zlen);
for(j = 0; j < zlen; j++) t[i][j] = z[j][i];
}
return t;
}...
if(x[0].length) {
// mat-vec or mat-mat
out = new Array(len);
for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
}
else if(y[0].length) {
// vec-mat
var yTranspose = exports.transposeRagged(y);
out = new Array(yTranspose.length);
for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
}
else {
// vec-vec
out = 0;
for(i = 0; i < len; i++) out += x[i] * y[i];
...validate = function (value, opts) {
var valObject = exports.valObjects[opts.valType];
if(opts.arrayOk && Array.isArray(value)) return true;
if(valObject.validateFunction) {
return valObject.validateFunction(value, opts);
}
var failed = {},
out = failed,
propMock = { set: function(v) { out = v; } };
// 'failed' just something mutable that won't be === anything else
valObject.coerceFunction(value, propMock, failed, opts);
return out !== failed;
}...
var items = opts.items;
// when free length is off, input and declared lengths must match
if(!opts.freeLength && v.length !== items.length) return false;
// valid when all input items are valid
for(var i = 0; i < v.length; i++) {
var isItemValid = exports.validate(v[i], opts.items[i]);
if(!isItemValid) return false;
}
return true;
}
}
...variance = function (data, len, mean) {
if(!len) len = exports.len(data);
if(!isNumeric(mean)) mean = exports.mean(data, len);
return exports.aggNums(function(a, b) {
return a + Math.pow(b - mean, 2);
}, 0, data) / len;
}...
return exports.aggNums(function(a, b) {
return a + Math.pow(b - mean, 2);
}, 0, data) / len;
};
exports.stdev = function(data, len, mean) {
return Math.sqrt(exports.variance(data, len, mean));
};
/**
* interp() computes a percentile (quantile) for a given distribution.
* We interpolate the distribution (to compute quantiles, we follow method #10 here:
* http://www.amstat.org/publications/jse/v14n3/langford.html).
* Typically the index or rank (n * arr.length) may be non-integer.
...warn = function () {
if(config.logging > 0) {
var messages = ['WARN:'];
for(var i = 0; i < arguments.length; i++) {
messages.push(arguments[i]);
}
apply(console.trace || console.log, messages);
}
}...
* @param {object||string} trace
* trace object with prop 'type' or trace type as a string
* @return {object}
* module object corresponding to trace type
*/
exports.getModule = function(trace) {
if(trace.r !== undefined) {
Loggers.warn('Tried to put a polar trace ' +
'on an incompatible graph of cartesian ' +
'data. Ignoring this dataset.', trace
);
return false;
}
var _module = exports.modules[getTraceType(trace)];
...error = function () {
if(config.logging > 0) {
var messages = ['ERROR:'];
for(var i = 0; i < arguments.length; i++) {
messages.push(arguments[i]);
}
apply(console.error, messages);
}
}...
// so we define the default (plotly.js) behavior here
function defaultSetBackground(gd, bgColor) {
try {
gd._fullLayout._paper.style('background', bgColor);
}
catch(e) {
if(module.exports.logging > 0) {
console.error(e);
}
}
}
...log = function () {
if(config.logging > 1) {
var messages = ['LOG:'];
for(var i = 0; i < arguments.length; i++) {
messages.push(arguments[i]);
}
apply(console.trace || console.log, messages);
}
}...
* @param {string} thisType
* @param {array of strings} categoriesIn all the categories this type is in,
* tested by calls: traceIs(trace, oneCategory)
* @param {object} meta meta information about the trace type
*/
exports.register = function(_module, thisType, categoriesIn, meta) {
if(exports.modules[thisType]) {
Loggers.log('Type ' + thisType + ' already registered');
return;
}
var categoryObj = {};
for(var i = 0; i < categoriesIn.length; i++) {
categoryObj[categoriesIn[i]] = true;
exports.allCategories[categoriesIn[i]] = true;
...warn = function () {
if(config.logging > 0) {
var messages = ['WARN:'];
for(var i = 0; i < arguments.length; i++) {
messages.push(arguments[i]);
}
apply(console.trace || console.log, messages);
}
}...
* @param {object||string} trace
* trace object with prop 'type' or trace type as a string
* @return {object}
* module object corresponding to trace type
*/
exports.getModule = function(trace) {
if(trace.r !== undefined) {
Loggers.warn('Tried to put a polar trace ' +
'on an incompatible graph of cartesian ' +
'data. Ignoring this dataset.', trace
);
return false;
}
var _module = exports.modules[getTraceType(trace)];
...function applyContainerArrayChanges(gd, np, edits, flags) {
var componentType = np.astr,
supplyComponentDefaults = Registry.getComponentMethod(componentType, 'supplyLayoutDefaults'),
draw = Registry.getComponentMethod(componentType, 'draw'),
drawOne = Registry.getComponentMethod(componentType, 'drawOne'),
replotLater = flags.replot || flags.recalc || (supplyComponentDefaults === noop) ||
(draw === noop),
layout = gd.layout,
fullLayout = gd._fullLayout;
if(edits['']) {
if(Object.keys(edits).length > 1) {
Loggers.warn('Full array edits are incompatible with other edits',
componentType);
}
var fullVal = edits[''][''];
if(isRemoveVal(fullVal)) np.set(null);
else if(Array.isArray(fullVal)) np.set(fullVal);
else {
Loggers.warn('Unrecognized full array edit value', componentType, fullVal);
return true;
}
if(replotLater) return false;
supplyComponentDefaults(layout, fullLayout);
draw(gd);
return true;
}
var componentNums = Object.keys(edits).map(Number).sort(),
componentArrayIn = np.get(),
componentArray = componentArrayIn || [],
// componentArrayFull is used just to keep splices in line between
// full and input arrays, so private keys can be copied over after
// redoing supplyDefaults
// TODO: this assumes componentArray is in gd.layout - which will not be
// true after we extend this to restyle
componentArrayFull = nestedProperty(fullLayout, componentType).get();
var deletes = [],
firstIndexChange = -1,
maxIndex = componentArray.length,
i,
j,
componentNum,
objEdits,
objKeys,
objVal,
adding;
// first make the add and edit changes
for(i = 0; i < componentNums.length; i++) {
componentNum = componentNums[i];
objEdits = edits[componentNum];
objKeys = Object.keys(objEdits);
objVal = objEdits[''],
adding = isAddVal(objVal);
if(componentNum < 0 || componentNum > componentArray.length - (adding ? 0 : 1)) {
Loggers.warn('index out of range', componentType, componentNum);
continue;
}
if(objVal !== undefined) {
if(objKeys.length > 1) {
Loggers.warn(
'Insertion & removal are incompatible with edits to the same index.',
componentType, componentNum);
}
if(isRemoveVal(objVal)) {
deletes.push(componentNum);
}
else if(adding) {
if(objVal === 'add') objVal = {};
componentArray.splice(componentNum, 0, objVal);
if(componentArrayFull) componentArrayFull.splice(componentNum, 0, {});
}
else {
Loggers.warn('Unrecognized full object edit value',
componentType, componentNum, objVal);
}
if(firstIndexChange === -1) firstIndexChange = componentNum;
}
else {
for(j = 0; j < objKeys.length; j++) {
nestedProperty(componentArray[componentNum], objKeys[j]).set(objEdits[objKeys[j]]);
}
}
}
// now do deletes
for(i = deletes.length - 1; i >= 0; i--) {
componentArray.splice(deletes[i], 1);
// TODO: this drops private keys that had been stored in componentArrayFull
// does this have any ill effects?
if(componentArrayFull) componentArrayFull.splice(deletes[i], 1);
}
if(!componentArray.length) np.set(null);
else if(!componentArrayIn) np.set(componentArray);
if(replotLater) return false;
supplyComponentDefaults(layout, fullLayout);
// finally draw all the components we need to
// if we added or removed any, redraw all after it
if(drawOne !== noop) {
var indicesToDraw;
if(firstIndexChange === -1) { ......
p.set(vi);
}
}
// now we've collected component edits - execute them all together
for(arrayStr in arrayEdits) {
var finished = manageArrays.applyContainerArrayChanges(gd,
Lib.nestedProperty(layout, arrayStr), arrayEdits[arrayStr], flags);
if(!finished) flags.doplot = true;
}
// figure out if we need to recalculate axis constraints
var constraints = fullLayout._axisConstraintGroups;
for(var axId in rangesAltered) {
...function containerArrayMatch(astr) {
var rootContainers = Registry.layoutArrayContainers,
regexpContainers = Registry.layoutArrayRegexes,
rootPart = astr.split('[')[0],
arrayStr,
match;
// look for regexp matches first, because they may be nested inside root matches
// eg updatemenus[i].buttons is nested inside updatemenus
for(var i = 0; i < regexpContainers.length; i++) {
match = astr.match(regexpContainers[i]);
if(match && match.index === 0) {
arrayStr = match[0];
break;
}
}
// now look for root matches
if(!arrayStr) arrayStr = rootContainers[rootContainers.indexOf(rootPart)];
if(!arrayStr) return false;
var tail = astr.substr(arrayStr.length);
if(!tail) return {array: arrayStr, index: '', property: ''};
match = tail.match(/^\[(0|[1-9][0-9]*)\](\.(.+))?$/);
if(!match) return false;
return {array: arrayStr, index: Number(match[1]), property: match[3] || ''};
}...
// alter gd.layout
// collect array component edits for execution all together
// so we can ensure consistent behavior adding/removing items
// and order-independence for add/remove/edit all together in
// one relayout call
var containerArrayMatch = manageArrays.containerArrayMatch(ai);
if(containerArrayMatch) {
arrayStr = containerArrayMatch.array;
i = containerArrayMatch.index;
var propStr = containerArrayMatch.property,
componentArray = Lib.nestedProperty(layout, arrayStr),
obji = (componentArray || [])[i] || {};
...function isAddVal(val) {
return val === 'add' || isPlainObject(val);
}...
// replacing the entire array: too much going on, force recalc
if(ai.indexOf('updatemenus') === -1) flags.docalc = true;
}
else if(propStr === '') {
// special handling of undoit if we're adding or removing an element
// ie 'annotations[2]' which can be {...} (add) or null (remove)
var toggledObj = vi;
if(manageArrays.isAddVal(vi)) {
undoit[ai] = null;
}
else if(manageArrays.isRemoveVal(vi)) {
undoit[ai] = obji;
toggledObj = obji;
}
else Lib.warn('unrecognized full object value', aobj);
...function isRemoveVal(val) {
return val === null || val === 'remove';
}...
else if(propStr === '') {
// special handling of undoit if we're adding or removing an element
// ie 'annotations[2]' which can be {...} (add) or null (remove)
var toggledObj = vi;
if(manageArrays.isAddVal(vi)) {
undoit[ai] = null;
}
else if(manageArrays.isRemoveVal(vi)) {
undoit[ai] = obji;
toggledObj = obji;
}
else Lib.warn('unrecognized full object value', aobj);
if(refAutorange(toggledObj, 'x') || refAutorange(toggledObj, 'y') &&
ai.indexOf('updatemenus') === -1) {
...apply2DTransform = function (transform) {
return function() {
var args = arguments;
if(args.length === 3) {
args = args[0];
}// from map
var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
};
}...
var xy = arguments.length === 1 ? args[0] : [args[0], args[1]];
return exports.dot(transform, [xy[0], xy[1], 1]).slice(0, 2);
};
};
// applies a 2D transformation matrix to an [x1,y1,x2,y2] array (to transform a segment)
exports.apply2DTransform2 = function(transform) {
var at = exports.apply2DTransform(transform);
return function(xys) {
return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
};
};
...apply2DTransform2 = function (transform) {
var at = exports.apply2DTransform(transform);
return function(xys) {
return at(xys.slice(0, 2)).concat(at(xys.slice(2, 4)));
};
}...
textX = annPosPx.x.text + dx,
textY = annPosPx.y.text + dy,
// find the edge of the text box, where we'll start the arrow:
// create transform matrix to rotate the text box corners
transform = Lib.rotationXYMatrix(textangle, textX, textY),
applyTransform = Lib.apply2DTransform(transform),
applyTransform2 = Lib.apply2DTransform2(transform),
// calculate and transform bounding box
width = +annTextBG.attr('width'),
height = +annTextBG.attr('height'),
xLeft = textX - 0.5 * width,
xRight = xLeft + width,
yTop = textY - 0.5 * height,
...dot = function (x, y) {
if(!(x.length && y.length) || x.length !== y.length) return null;
var len = x.length,
out,
i;
if(x[0].length) {
// mat-vec or mat-mat
out = new Array(len);
for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
}
else if(y[0].length) {
// vec-mat
var yTranspose = exports.transposeRagged(y);
out = new Array(yTranspose.length);
for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
}
else {
// vec-vec
out = 0;
for(i = 0; i < len; i++) out += x[i] * y[i];
}
return out;
}...
var len = x.length,
out,
i;
if(x[0].length) {
// mat-vec or mat-mat
out = new Array(len);
for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
}
else if(y[0].length) {
// vec-mat
var yTranspose = exports.transposeRagged(y);
out = new Array(yTranspose.length);
for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
}
...init2dArray = function (rowLength, colLength) {
var array = new Array(rowLength);
for(var i = 0; i < rowLength; i++) array[i] = new Array(colLength);
return array;
}...
var col1dv = Lib.distinctVals(col1),
col1vals = col1dv.vals,
col2dv = Lib.distinctVals(col2),
col2vals = col2dv.vals,
newArrays = [];
for(i = 0; i < arrayVarNames.length; i++) {
newArrays[i] = Lib.init2dArray(col2vals.length, col1vals.length);
}
var i1, i2, text;
if(hasColumnText) text = Lib.init2dArray(col2vals.length, col1vals.length);
for(i = 0; i < colLen; i++) {
...rotationMatrix = function (alpha) {
var a = alpha * Math.PI / 180;
return [[Math.cos(a), -Math.sin(a), 0],
[Math.sin(a), Math.cos(a), 0],
[0, 0, 1]];
}...
[0, 0, 1]];
};
// rotate by alpha around (x,y)
exports.rotationXYMatrix = function(a, x, y) {
return exports.dot(
exports.dot(exports.translationMatrix(x, y),
exports.rotationMatrix(a)),
exports.translationMatrix(-x, -y));
};
// applies a 2D transformation matrix to either x and y params or an [x,y] array
exports.apply2DTransform = function(transform) {
return function() {
var args = arguments;
...rotationXYMatrix = function (a, x, y) {
return exports.dot(
exports.dot(exports.translationMatrix(x, y),
exports.rotationMatrix(a)),
exports.translationMatrix(-x, -y));
}...
tailX = annPosPx.x.tail + dx,
tailY = annPosPx.y.tail + dy,
textX = annPosPx.x.text + dx,
textY = annPosPx.y.text + dy,
// find the edge of the text box, where we'll start the arrow:
// create transform matrix to rotate the text box corners
transform = Lib.rotationXYMatrix(textangle, textX, textY),
applyTransform = Lib.apply2DTransform(transform),
applyTransform2 = Lib.apply2DTransform2(transform),
// calculate and transform bounding box
width = +annTextBG.attr('width'),
height = +annTextBG.attr('height'),
xLeft = textX - 0.5 * width,
...translationMatrix = function (x, y) {
return [[1, 0, x], [0, 1, y], [0, 0, 1]];
}...
[Math.sin(a), Math.cos(a), 0],
[0, 0, 1]];
};
// rotate by alpha around (x,y)
exports.rotationXYMatrix = function(a, x, y) {
return exports.dot(
exports.dot(exports.translationMatrix(x, y),
exports.rotationMatrix(a)),
exports.translationMatrix(-x, -y));
};
// applies a 2D transformation matrix to either x and y params or an [x,y] array
exports.apply2DTransform = function(transform) {
return function() {
...transposeRagged = function (z) {
var maxlen = 0,
zlen = z.length,
i,
j;
// Maximum row length:
for(i = 0; i < zlen; i++) maxlen = Math.max(maxlen, z[i].length);
var t = new Array(maxlen);
for(i = 0; i < maxlen; i++) {
t[i] = new Array(zlen);
for(j = 0; j < zlen; j++) t[i][j] = z[j][i];
}
return t;
}...
if(x[0].length) {
// mat-vec or mat-mat
out = new Array(len);
for(i = 0; i < len; i++) out[i] = exports.dot(x[i], y);
}
else if(y[0].length) {
// vec-mat
var yTranspose = exports.transposeRagged(y);
out = new Array(yTranspose.length);
for(i = 0; i < yTranspose.length; i++) out[i] = exports.dot(x, yTranspose[i]);
}
else {
// vec-vec
out = 0;
for(i = 0; i < len; i++) out += x[i] * y[i];
...function module() {
return µ.PolyChart();
}n/a
function module() {
var config = {
data: [],
layout: {}
}, inputConfig = {}, liveConfig = {};
var svg, container, dispatch = d3.dispatch('hover'), radialScale, angularScale;
var exports = {};
function render(_container) {
container = _container || container;
var data = config.data;
var axisConfig = config.layout;
if (typeof container == 'string' || container.nodeName) container = d3.select(container);
container.datum(data).each(function(_data, _index) {
var dataOriginal = _data.slice();
liveConfig = {
data: µ.util.cloneJson(dataOriginal),
layout: µ.util.cloneJson(axisConfig)
};
var colorIndex = 0;
dataOriginal.forEach(function(d, i) {
if (!d.color) {
d.color = axisConfig.defaultColorRange[colorIndex];
colorIndex = (colorIndex + 1) % axisConfig.defaultColorRange.length;
}
if (!d.strokeColor) {
d.strokeColor = d.geometry === 'LinePlot' ? d.color : d3.rgb(d.color).darker().toString();
}
liveConfig.data[i].color = d.color;
liveConfig.data[i].strokeColor = d.strokeColor;
liveConfig.data[i].strokeDash = d.strokeDash;
liveConfig.data[i].strokeSize = d.strokeSize;
});
var data = dataOriginal.filter(function(d, i) {
var visible = d.visible;
return typeof visible === 'undefined' || visible === true;
});
var isStacked = false;
var dataWithGroupId = data.map(function(d, i) {
isStacked = isStacked || typeof d.groupId !== 'undefined';
return d;
});
if (isStacked) {
var grouped = d3.nest().key(function(d, i) {
return typeof d.groupId != 'undefined' ? d.groupId : 'unstacked';
}).entries(dataWithGroupId);
var dataYStack = [];
var stacked = grouped.map(function(d, i) {
if (d.key === 'unstacked') return d.values; else {
var prevArray = d.values[0].r.map(function(d, i) {
return 0;
});
d.values.forEach(function(d, i, a) {
d.yStack = [ prevArray ];
dataYStack.push(prevArray);
prevArray = µ.util.sumArrays(d.r, prevArray);
});
return d.values;
}
});
data = d3.merge(stacked);
}
data.forEach(function(d, i) {
d.t = Array.isArray(d.t[0]) ? d.t : [ d.t ];
d.r = Array.isArray(d.r[0]) ? d.r : [ d.r ];
});
var radius = Math.min(axisConfig.width - axisConfig.margin.left - axisConfig.margin.right, axisConfig.height - axisConfig
.margin.top - axisConfig.margin.bottom) / 2;
radius = Math.max(10, radius);
var chartCenter = [ axisConfig.margin.left + radius, axisConfig.margin.top + radius ];
var extent;
if (isStacked) {
var highestStackedValue = d3.max(µ.util.sumArrays(µ.util.arrayLast(data).r[0], µ.util.arrayLast(dataYStack)));
extent = [ 0, highestStackedValue ];
} else extent = d3.extent(µ.util.flattenArray(data.map(function(d, i) {
return d.r;
})));
if (axisConfig.radialAxis.domain != µ.DATAEXTENT) extent[0] = 0;
radialScale = d3.scale.linear().domain(axisConfig.radialAxis.domain != µ.DATAEXTENT && axisConfig.radialAxis.domain ?
axisConfig.radialAxis.domain : extent).range([ 0, radius ]);
liveConfig.layout.radialAxis.domain = radialScale.domain();
var angularDataMerged = µ.util.flattenArray(data.map(function(d, i) {
ret ......
if(_container) container = _container;
d3.select(d3.select(container).node().parentNode).selectAll('.svg-container>*:not(.chart-root)').remove();
config = (!config) ?
_inputConfig :
extendDeepAll(config, _inputConfig);
if(!plot) plot = micropolar.Axis();
convertedInput = micropolar.adapter.plotly().convert(config);
plot.config(convertedInput).render(container);
_gd.data = config.data;
_gd.layout = config.layout;
manager.fillLayout(_gd);
return config;
}
...function module() {
return µ.PolyChart();
}n/a
function module() {
return µ.PolyChart();
}n/a
function module() {
var config = µ.Legend.defaultConfig();
var dispatch = d3.dispatch('hover');
function exports() {
var legendConfig = config.legendConfig;
var flattenData = config.data.map(function(d, i) {
return [].concat(d).map(function(dB, iB) {
var element = extendDeepAll({}, legendConfig.elements[i]);
element.name = dB;
element.color = [].concat(legendConfig.elements[i].color)[iB];
return element;
});
});
var data = d3.merge(flattenData);
data = data.filter(function(d, i) {
return legendConfig.elements[i] && (legendConfig.elements[i].visibleInLegend || typeof legendConfig.elements[i].visibleInLegend
=== 'undefined');
});
if (legendConfig.reverseOrder) data = data.reverse();
var container = legendConfig.container;
if (typeof container == 'string' || container.nodeName) container = d3.select(container);
var colors = data.map(function(d, i) {
return d.color;
});
var lineHeight = legendConfig.fontSize;
var isContinuous = legendConfig.isContinuous == null ? typeof data[0] === 'number' : legendConfig.isContinuous;
var height = isContinuous ? legendConfig.height : lineHeight * data.length;
var legendContainerGroup = container.classed('legend-group', true);
var svg = legendContainerGroup.selectAll('svg').data([ 0 ]);
var svgEnter = svg.enter().append('svg').attr({
width: 300,
height: height + lineHeight,
xmlns: 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
version: '1.1'
});
svgEnter.append('g').classed('legend-axis', true);
svgEnter.append('g').classed('legend-marks', true);
var dataNumbered = d3.range(data.length);
var colorScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered).range(colors);
var dataScale = d3.scale[isContinuous ? 'linear' : 'ordinal']().domain(dataNumbered)[isContinuous ? 'range' : 'rangePoints
']([ 0, height ]);
var shapeGenerator = function(_type, _size) {
var squareSize = _size * 3;
if (_type === 'line') {
return 'M' + [ [ -_size / 2, -_size / 12 ], [ _size / 2, -_size / 12 ], [ _size / 2, _size / 12 ], [ -_size / 2,
_size / 12 ] ] + 'Z';
} else if (d3.svg.symbolTypes.indexOf(_type) != -1) return d3.svg.symbol().type(_type).size(squareSize)(); else return
d3.svg.symbol().type('square').size(squareSize)();
};
if (isContinuous) {
var gradient = svg.select('.legend-marks').append('defs').append('linearGradient').attr({
id: 'grad1',
x1: '0%',
y1: '0%',
x2: '0%',
y2: '100%'
}).selectAll('stop').data(colors);
gradient.enter().append('stop');
gradient.attr({
offset: function(d, i) {
return i / (colors.length - 1) * 100 + '%';
}
}).style({
'stop-color': function(d, i) {
return d;
}
});
svg.append('rect').classed('legend-mark', true).attr({
height: legendConfig.height,
width: legendConfig.colorBandWidth,
fill: 'url(#grad1)'
});
} else {
var legendElement = svg.select('.legend-marks').selectAll('path.legend-mark').data(data);
legendElement.enter().append('path').classed('legend-mark', true);
legendElement.attr({
transform: function(d, i) {
return 'translate(' + [ lineHeight / 2, dataScale(i) + lineHeight / 2 ] + ')';
},
d: function(d, i) {
var symbolType = d.symbol;
return shapeGenerator(symbolType, lineHeight);
}, ......
var datumClone = µ.util.cloneJson(d);
datumClone.symbol = d.geometry === 'DotPlot' ? d.dotType || 'circle' : d.geometry != 'LinePlot'
; ? 'square' : 'line';
datumClone.visibleInLegend = typeof d.visibleInLegend === 'undefined' || d.visibleInLegend;
datumClone.color = d.geometry === 'LinePlot' ? d.strokeColor : d.color;
return datumClone;
});
µ.Legend().config({
data: data.map(function(d, i) {
return d.name || 'Element' + i;
}),
legendConfig: extendDeepAll({},
µ.Legend.defaultConfig().legendConfig,
{
container: legendContainer,
...function module() {
return µ.PolyChart();
}n/a
function module() {
var config = [ µ.PolyChart.defaultConfig() ];
var dispatch = d3.dispatch('hover');
var dashArray = {
solid: 'none',
dash: [ 5, 2 ],
dot: [ 2, 5 ]
};
var colorScale;
function exports() {
var geometryConfig = config[0].geometryConfig;
var container = geometryConfig.container;
if (typeof container == 'string') container = d3.select(container);
container.datum(config).each(function(_config, _index) {
var isStack = !!_config[0].data.yStack;
var data = _config.map(function(d, i) {
if (isStack) return d3.zip(d.data.t[0], d.data.r[0], d.data.yStack[0]); else return d3.zip(d.data.t[0], d.data.r
[0]);
});
var angularScale = geometryConfig.angularScale;
var domainMin = geometryConfig.radialScale.domain()[0];
var generator = {};
generator.bar = function(d, i, pI) {
var dataConfig = _config[pI].data;
var h = geometryConfig.radialScale(d[1]) - geometryConfig.radialScale(0);
var stackTop = geometryConfig.radialScale(d[2] || 0);
var w = dataConfig.barWidth;
d3.select(this).attr({
'class': 'mark bar',
d: 'M' + [ [ h + stackTop, -w / 2 ], [ h + stackTop, w / 2 ], [ stackTop, w / 2 ], [ stackTop, -w / 2 ] ].join
('L') + 'Z',
transform: function(d, i) {
return 'rotate(' + (geometryConfig.orientation + angularScale(d[0])) + ')';
}
});
};
generator.dot = function(d, i, pI) {
var stackedData = d[2] ? [ d[0], d[1] + d[2] ] : d;
var symbol = d3.svg.symbol().size(_config[pI].data.dotSize).type(_config[pI].data.dotType)(d, i);
d3.select(this).attr({
'class': 'mark dot',
d: symbol,
transform: function(d, i) {
var coord = convertToCartesian(getPolarCoordinates(stackedData));
return 'translate(' + [ coord.x, coord.y ] + ')';
}
});
};
var line = d3.svg.line.radial().interpolate(_config[0].data.lineInterpolation).radius(function(d) {
return geometryConfig.radialScale(d[1]);
}).angle(function(d) {
return geometryConfig.angularScale(d[0]) * Math.PI / 180;
});
generator.line = function(d, i, pI) {
var lineData = d[2] ? data[pI].map(function(d, i) {
return [ d[0], d[1] + d[2] ];
}) : data[pI];
d3.select(this).each(generator['dot']).style({
opacity: function(dB, iB) {
return +_config[pI].data.dotVisible;
},
fill: markStyle.stroke(d, i, pI)
}).attr({
'class': 'mark dot'
});
if (i > 0) return;
var lineSelection = d3.select(this.parentNode).selectAll('path.line').data([ 0 ]);
lineSelection.enter().insert('path');
lineSelection.attr({
'class': 'line',
d: line(lineData),
transform: function(dB, iB) {
return 'rotate(' + (geometryConfig.orientation + 90) + ')';
},
'pointer-events': 'none'
}).style({
fill: function(dB, iB) {
return markStyle.fill(d, i, pI);
},
'fill-opacity': 0,
stroke: function(dB, iB) {
return markStyle.stroke(d, i, pI);
},
'stroke-width': function(dB, iB) {
return markStyle['stroke-width'](d, i, pI);
},
'stroke-dasharray': ......
colorScale: d3.scale.category20()
}
};
return config;
};
µ.BarChart = function module() {
return µ.PolyChart();
};
µ.BarChart.defaultConfig = function() {
var config = {
geometryConfig: {
geometryType: 'bar'
}
...tooltipPanel = function () {
var tooltipEl, tooltipTextEl, backgroundEl;
var config = {
container: null,
hasTick: false,
fontSize: 12,
color: 'white',
padding: 5
};
var id = 'tooltip-' + µ.tooltipPanel.uid++;
var tickSize = 10;
var exports = function() {
tooltipEl = config.container.selectAll('g.' + id).data([ 0 ]);
var tooltipEnter = tooltipEl.enter().append('g').classed(id, true).style({
'pointer-events': 'none',
display: 'none'
});
backgroundEl = tooltipEnter.append('path').style({
fill: 'white',
'fill-opacity': .9
}).attr({
d: 'M0 0'
});
tooltipTextEl = tooltipEnter.append('text').attr({
dx: config.padding + tickSize,
dy: +config.fontSize * .3
});
return exports;
};
exports.text = function(_text) {
var l = d3.hsl(config.color).l;
var strokeColor = l >= .5 ? '#aaa' : 'white';
var fillColor = l >= .5 ? 'black' : 'white';
var text = _text || '';
tooltipTextEl.style({
fill: fillColor,
'font-size': config.fontSize + 'px'
}).text(text);
var padding = config.padding;
var bbox = tooltipTextEl.node().getBBox();
var boxStyle = {
fill: config.color,
stroke: strokeColor,
'stroke-width': '2px'
};
var backGroundW = bbox.width + padding * 2 + tickSize;
var backGroundH = bbox.height + padding * 2;
backgroundEl.attr({
d: 'M' + [ [ tickSize, -backGroundH / 2 ], [ tickSize, -backGroundH / 4 ], [ config.hasTick ? 0 : tickSize, 0 ], [ tickSize
, backGroundH / 4 ], [ tickSize, backGroundH / 2 ], [ backGroundW, backGroundH / 2 ], [ backGroundW, -backGroundH / 2 ] ].join('
L') + 'Z'
}).style(boxStyle);
tooltipEl.attr({
transform: 'translate(' + [ tickSize, -backGroundH / 2 + padding * 2 ] + ')'
});
tooltipEl.style({
display: 'block'
});
return exports;
};
exports.move = function(_pos) {
if (!tooltipEl) return;
tooltipEl.attr({
transform: 'translate(' + [ _pos[0], _pos[1] ] + ')'
}).style({
display: 'block'
});
return exports;
};
exports.hide = function() {
if (!tooltipEl) return;
tooltipEl.style({
display: 'none'
});
return exports;
};
exports.show = function() {
if (!tooltipEl) return;
tooltipEl.style({
display: 'block'
});
return exports;
};
exports.config = function(_x) {
extendDeepAll(config, _x);
return exports;
};
return exports;
}...
return extendDeepAll(µ[geometry].defaultConfig(), dB);
});
µ[geometry]().config(finalGeometryConfig)();
});
}
var guides = svg.select('.guides-group');
var tooltipContainer = svg.select('.tooltips-group');
var angularTooltip = µ.tooltipPanel().config({
container: tooltipContainer,
fontSize: 8
})();
var radialTooltip = µ.tooltipPanel().config({
container: tooltipContainer,
fontSize: 8
})();
...fillLayout = function (_gd) {
var container = d3.select(_gd).selectAll('.plot-container'),
paperDiv = container.selectAll('.svg-container'),
paper = _gd.framework && _gd.framework.svg && _gd.framework.svg(),
dflts = {
width: 800,
height: 600,
paper_bgcolor: Color.background,
_container: container,
_paperdiv: paperDiv,
_paper: paper
};
_gd._fullLayout = extendDeepAll(dflts, _gd.layout);
}...
// empty it everytime for now
paperDiv.html('');
// fulfill gd requirements
if(data) gd.data = data;
if(layout) gd.layout = layout;
Polar.manager.fillLayout(gd);
// resize canvas
paperDiv.style({
width: gd._fullLayout.width + 'px',
height: gd._fullLayout.height + 'px'
});
...framework = function (_gd) {
var config, previousConfigClone, plot, convertedInput, container;
var undoManager = new UndoManager();
function exports(_inputConfig, _container) {
if(_container) container = _container;
d3.select(d3.select(container).node().parentNode).selectAll('.svg-container>*:not(.chart-root)').remove();
config = (!config) ?
_inputConfig :
extendDeepAll(config, _inputConfig);
if(!plot) plot = micropolar.Axis();
convertedInput = micropolar.adapter.plotly().convert(config);
plot.config(convertedInput).render(container);
_gd.data = config.data;
_gd.layout = config.layout;
manager.fillLayout(_gd);
return config;
}
exports.isPolar = true;
exports.svg = function() { return plot.svg(); };
exports.getConfig = function() { return config; };
exports.getLiveConfig = function() {
return micropolar.adapter.plotly().convert(plot.getLiveConfig(), true);
};
exports.getLiveScales = function() { return {t: plot.angularScale(), r: plot.radialScale()}; };
exports.setUndoPoint = function() {
var that = this;
var configClone = micropolar.util.cloneJson(config);
(function(_configClone, _previousConfigClone) {
undoManager.add({
undo: function() {
if(_previousConfigClone) that(_previousConfigClone);
},
redo: function() {
that(_configClone);
}
});
})(configClone, previousConfigClone);
previousConfigClone = micropolar.util.cloneJson(configClone);
};
exports.undo = function() { undoManager.undo(); };
exports.redo = function() { undoManager.redo(); };
return exports;
}...
// resize canvas
paperDiv.style({
width: gd._fullLayout.width + 'px',
height: gd._fullLayout.height + 'px'
});
// instantiate framework
gd.framework = Polar.manager.framework(gd);
// plot
gd.framework({data: gd.data, layout: gd.layout}, paperDiv.node());
// set undo point
gd.framework.setUndoPoint();
...density = function (size, total, inc, yinc) {
var nMax = size.length;
yinc = yinc || 1;
for(var n = 0; n < nMax; n++) size[n] *= inc[n] * yinc;
}n/a
percent = function (size, total) {
var nMax = size.length,
norm = 100 / total;
for(var n = 0; n < nMax; n++) size[n] *= norm;
}n/a
probability = function (size, total) {
var nMax = size.length;
for(var n = 0; n < nMax; n++) size[n] /= total;
}n/a
function calc(gd, trace) {
var vals = trace.values,
labels = trace.labels,
cd = [],
fullLayout = gd._fullLayout,
colorMap = fullLayout._piecolormap,
allThisTraceLabels = {},
needDefaults = false,
vTotal = 0,
hiddenLabels = fullLayout.hiddenlabels || [],
i,
v,
label,
color,
hidden,
pt;
if(trace.dlabel) {
labels = new Array(vals.length);
for(i = 0; i < vals.length; i++) {
labels[i] = String(trace.label0 + i * trace.dlabel);
}
}
for(i = 0; i < vals.length; i++) {
v = vals[i];
if(!isNumeric(v)) continue;
v = +v;
if(v < 0) continue;
label = labels[i];
if(label === undefined || label === '') label = i;
label = String(label);
// only take the first occurrence of any given label.
// TODO: perhaps (optionally?) sum values for a repeated label?
if(allThisTraceLabels[label] === undefined) allThisTraceLabels[label] = true;
else continue;
color = tinycolor(trace.marker.colors[i]);
if(color.isValid()) {
color = Color.addOpacity(color, color.getAlpha());
if(!colorMap[label]) {
colorMap[label] = color;
}
}
// have we seen this label and assigned a color to it in a previous trace?
else if(colorMap[label]) color = colorMap[label];
// color needs a default - mark it false, come back after sorting
else {
color = false;
needDefaults = true;
}
hidden = hiddenLabels.indexOf(label) !== -1;
if(!hidden) vTotal += v;
cd.push({
v: v,
label: label,
color: color,
i: i,
hidden: hidden
});
}
if(trace.sort) cd.sort(function(a, b) { return b.v - a.v; });
/**
* now go back and fill in colors we're still missing
* this is done after sorting, so we pick defaults
* in the order slices will be displayed
*/
if(needDefaults) {
for(i = 0; i < cd.length; i++) {
pt = cd[i];
if(pt.color === false) {
colorMap[pt.label] = pt.color = nextDefaultColor(fullLayout._piedefaultcolorcount);
fullLayout._piedefaultcolorcount++;
}
}
}
// include the sum of all values in the first point
if(cd[0]) cd[0].vTotal = vTotal;
// now insert text
if(trace.textinfo && trace.textinfo !== 'none') {
var hasLabel = trace.textinfo.indexOf('label') !== -1,
hasText = trace.textinfo.indexOf('text') !== -1,
hasValue = trace.textinfo.indexOf('value') !== -1,
hasPercent = trace.textinfo.indexOf('percent') !== -1,
separators = fullLayout.separators,
thisText;
for(i = 0; i < cd.length; i++) {
pt = cd[i];
thisText = hasLabel ? [pt.label] : [];
if(hasText && trace.text[pt.i]) thisText.push(trace.text[pt.i]);
if(hasValue) thisText.push(helpers.formatPieValue(pt.v, separators));
if(hasPercent) thisText.push(helpers.formatPiePercent(pt.v / vTotal, separators));
pt.text = thisText.join('<br>');
}
}
return cd;
}...
for(var j = 0; j < modules.length; j++) {
_module = modules[j];
if(_module.setPositions) _module.setPositions(gd, subplotInfo);
}
}
// calc and autorange for errorbars
ErrorBars.calc(gd);
// TODO: autosize extra for text markers and images
// see https://github.com/plotly/plotly.js/issues/1111
return Lib.syncOrAsync([
Registry.getComponentMethod('shapes', 'calcAutorange'),
Registry.getComponentMethod('annotations', 'calcAutorange'),
doAutoRangeAndConstraints,
...function plot(gd, cdpie) {
var fullLayout = gd._fullLayout;
scalePies(cdpie, fullLayout._size);
var pieGroups = fullLayout._pielayer.selectAll('g.trace').data(cdpie);
pieGroups.enter().append('g')
.attr({
'stroke-linejoin': 'round', // TODO: miter might look better but can sometimes cause problems
// maybe miter with a small-ish stroke-miterlimit?
'class': 'trace'
});
pieGroups.exit().remove();
pieGroups.order();
pieGroups.each(function(cd) {
var pieGroup = d3.select(this),
cd0 = cd[0],
trace = cd0.trace,
tiltRads = 0, // trace.tilt * Math.PI / 180,
depthLength = (trace.depth||0) * cd0.r * Math.sin(tiltRads) / 2,
tiltAxis = trace.tiltaxis || 0,
tiltAxisRads = tiltAxis * Math.PI / 180,
depthVector = [
depthLength * Math.sin(tiltAxisRads),
depthLength * Math.cos(tiltAxisRads)
],
rSmall = cd0.r * Math.cos(tiltRads);
var pieParts = pieGroup.selectAll('g.part')
.data(trace.tilt ? ['top', 'sides'] : ['top']);
pieParts.enter().append('g').attr('class', function(d) {
return d + ' part';
});
pieParts.exit().remove();
pieParts.order();
setCoords(cd);
pieGroup.selectAll('.top').each(function() {
var slices = d3.select(this).selectAll('g.slice').data(cd);
slices.enter().append('g')
.classed('slice', true);
slices.exit().remove();
var quadrants = [
[[], []], // y<0: x<0, x>=0
[[], []] // y>=0: x<0, x>=0
],
hasOutsideText = false;
slices.each(function(pt) {
if(pt.hidden) {
d3.select(this).selectAll('path,g').remove();
return;
}
quadrants[pt.pxmid[1] < 0 ? 0 : 1][pt.pxmid[0] < 0 ? 0 : 1].push(pt);
var cx = cd0.cx + depthVector[0],
cy = cd0.cy + depthVector[1],
sliceTop = d3.select(this),
slicePath = sliceTop.selectAll('path.surface').data([pt]),
hasHoverData = false;
function handleMouseOver(evt) {
evt.originalEvent = d3.event;
// in case fullLayout or fullData has changed without a replot
var fullLayout2 = gd._fullLayout,
trace2 = gd._fullData[trace.index],
hoverinfo = trace2.hoverinfo;
if(hoverinfo === 'all') hoverinfo = 'label+text+value+percent+name';
// in case we dragged over the pie from another subplot,
// or if hover is turned off
if(gd._dragging || fullLayout2.hovermode === false ||
hoverinfo === 'none' || hoverinfo === 'skip' || !hoverinfo) {
Fx.hover(gd, evt, 'pie');
return;
}
var rInscribed = getInscribedRadiusFraction(pt, cd0),
hoverCenterX = cx + pt.pxmid[0] * (1 - rInscribed),
hoverCenterY = cy + pt.pxmid[1] * (1 - rInscribed),
separators = fullLayout.separators,
thisText = [];
if(hoverinfo.indexOf('label') !== -1) thisText.push(pt.label);
if(hoverinfo.indexOf('text') !== -1) {
if(trace2.hovertext) {
thisText.push(
Array.isArray(trace2.hovertext) ?
trace2.hovertext[pt.i] :
trace2.hovertext
);
} else if(trace2.text && trace2.text[pt.i]) {
thisText.push(trace2.text[pt.i ......
fullLayout._infolayer.selectAll('.cb' + uid).remove();
}
}
// loop over the base plot modules present on graph
var basePlotModules = fullLayout._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
basePlotModules[i].plot(gd);
}
// keep reference to shape layers in subplots
var layerSubplot = fullLayout._paper.selectAll('.layer-subplot');
fullLayout._shapeSubplotLayers = layerSubplot.selectAll('.shapelayer');
// styling separate from drawing
...function style(gd) {
gd._fullLayout._pielayer.selectAll('.trace').each(function(cd) {
var cd0 = cd[0],
trace = cd0.trace,
traceSelection = d3.select(this);
traceSelection.style({opacity: trace.opacity});
traceSelection.selectAll('.top path.surface').each(function(pt) {
d3.select(this).call(styleOne, pt, trace);
});
});
}...
.classed('plotly-notifier', true);
var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA);
function killNote(transition) {
transition
.duration(700)
.style('opacity', 0)
.each('end', function(thisText) {
var thisIndex = NOTEDATA.indexOf(thisText);
if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1);
d3.select(this).remove();
});
}
...function styleOne(s, pt, trace) {
var lineColor = trace.marker.line.color;
if(Array.isArray(lineColor)) lineColor = lineColor[pt.i] || Color.defaultLine;
var lineWidth = trace.marker.line.width || 0;
if(Array.isArray(lineWidth)) lineWidth = lineWidth[pt.i] || 0;
s.style({'stroke-width': lineWidth})
.call(Color.fill, pt.color)
.call(Color.stroke, lineColor);
}n/a
function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
function coerce(attr, dflt) {
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
}
var coerceFont = Lib.coerceFont;
var vals = coerce('values');
if(!Array.isArray(vals) || !vals.length) {
traceOut.visible = false;
return;
}
var labels = coerce('labels');
if(!Array.isArray(labels)) {
coerce('label0');
coerce('dlabel');
}
var lineWidth = coerce('marker.line.width');
if(lineWidth) coerce('marker.line.color');
var colors = coerce('marker.colors');
if(!Array.isArray(colors)) traceOut.marker.colors = []; // later this will get padded with default colors
coerce('scalegroup');
// TODO: tilt, depth, and hole all need to be coerced to the same values within a scaleegroup
// (ideally actually, depth would get set the same *after* scaling, ie the same absolute depth)
// and if colors aren't specified we should match these up - potentially even if separate pies
// are NOT in the same sharegroup
var textData = coerce('text');
var textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent');
coerce('hovertext');
coerce('hoverinfo', (layout._dataLength === 1) ? 'label+text+value+percent' : undefined);
if(textInfo && textInfo !== 'none') {
var textPosition = coerce('textposition'),
hasBoth = Array.isArray(textPosition) || textPosition === 'auto',
hasInside = hasBoth || textPosition === 'inside',
hasOutside = hasBoth || textPosition === 'outside';
if(hasInside || hasOutside) {
var dfltFont = coerceFont(coerce, 'textfont', layout.font);
if(hasInside) coerceFont(coerce, 'insidetextfont', dfltFont);
if(hasOutside) coerceFont(coerce, 'outsidetextfont', dfltFont);
}
}
coerce('domain.x');
coerce('domain.y');
// 3D attributes commented out until I finish them in a later PR
// var tilt = coerce('tilt');
// if(tilt) {
// coerce('tiltaxis');
// coerce('depth');
// coerce('shading');
// }
coerce('hole');
coerce('sort');
coerce('direction');
coerce('rotation');
coerce('pull');
}...
gd._replotPending = true;
return Promise.reject();
} else {
// we're going ahead with a replot now
gd._replotPending = false;
}
Plots.supplyDefaults(gd);
var fullLayout = gd._fullLayout;
// Polar plots
if(data && data[0] && data[0].r) return plotPolar(gd, data, layout);
// so we don't try to re-call Plotly.plot from inside
...function supplyLayoutDefaults(layoutIn, layoutOut) {
function coerce(attr, dflt) {
return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
}
coerce('hiddenlabels');
}...
}
plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, transitionData) {
var i, _module;
// can't be be part of basePlotModules loop
// in order to handle the orphan axes case
Plotly.Axes.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
// base plot module layout defaults
var basePlotModules = layoutOut._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
_module = basePlotModules[i];
// done above already
...function defaultSetBackground(gd, bgColor) {
try {
gd._fullLayout._paper.style('background', bgColor);
}
catch(e) {
if(module.exports.logging > 0) {
console.error(e);
}
}
}...
return gd;
});
};
function opaqueSetBackground(gd, bgColor) {
gd._fullLayout._paperdiv.style('background', 'white');
Plotly.defaultConfig.setBackground(gd, bgColor);
}
function setPlotContext(gd, config) {
if(!gd._context) gd._context = Lib.extendFlat({}, Plotly.defaultConfig);
var context = gd._context;
if(config) {
...addFrames = function (gd, frameList, indices) {
gd = helpers.getGraphDiv(gd);
var numericNameWarningCount = 0;
if(frameList === null || frameList === undefined) {
return Promise.resolve();
}
if(!Lib.isPlotDiv(gd)) {
throw new Error(
'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
'to create a plot before adding frames. For more details, see ' +
'https://plot.ly/javascript/animations/'
);
}
var i, frame, j, idx;
var _frames = gd._transitionData._frames;
var _hash = gd._transitionData._frameHash;
if(!Array.isArray(frameList)) {
throw new Error('addFrames failure: frameList must be an Array of frame definitions' + frameList);
}
// Create a sorted list of insertions since we run into lots of problems if these
// aren't in ascending order of index:
//
// Strictly for sorting. Make sure this is guaranteed to never collide with any
// already-exisisting indices:
var bigIndex = _frames.length + frameList.length * 2;
var insertions = [];
for(i = frameList.length - 1; i >= 0; i--) {
if(!Lib.isPlainObject(frameList[i])) continue;
var name = (_hash[frameList[i].name] || {}).name;
var newName = frameList[i].name;
if(name && newName && typeof newName === 'number' && _hash[name]) {
numericNameWarningCount++;
Lib.warn('addFrames: overwriting frame "' + _hash[name].name +
'" with a frame whose name of type "number" also equates to "' +
name + '". This is valid but may potentially lead to unexpected ' +
'behavior since all plotly.js frame names are stored internally ' +
'as strings.');
if(numericNameWarningCount > 5) {
Lib.warn('addFrames: This API call has yielded too many warnings. ' +
'For the rest of this call, further warnings about numeric frame ' +
'names will be suppressed.');
}
}
insertions.push({
frame: Plots.supplyFrameDefaults(frameList[i]),
index: (indices && indices[i] !== undefined && indices[i] !== null) ? indices[i] : bigIndex + i
});
}
// Sort this, taking note that undefined insertions end up at the end:
insertions.sort(function(a, b) {
if(a.index > b.index) return -1;
if(a.index < b.index) return 1;
return 0;
});
var ops = [];
var revops = [];
var frameCount = _frames.length;
for(i = insertions.length - 1; i >= 0; i--) {
frame = insertions[i].frame;
if(typeof frame.name === 'number') {
Lib.warn('Warning: addFrames accepts frames with numeric names, but the numbers are' +
'implicitly cast to strings');
}
if(!frame.name) {
// Repeatedly assign a default name, incrementing the counter each time until
// we get a name that's not in the hashed lookup table:
while(_hash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
}
if(_hash[frame.name]) {
// If frame is present, overwrite its definition:
for(j = 0; j < _frames.length; j++) {
if((_frames[j] || {}).name === frame.name) break;
}
ops.push({type: 'replace', index: j, value: frame});
revops.unshift({type: 'replace', index: j, value: _frames[j]});
} else {
// Otherwise insert it at the end of the list:
idx = Math.max(0, Math.min(insertions[i].index, frameCount));
ops.push({type: 'insert', index: idx, value: frame});
revops.unshift({type: 'delete', index: idx});
frameCount++;
}
}
var undoFunc = Plots.modifyFrames,
redoFunc = Plots.modifyFrames,
undoArgs = [gd, revops],
redoArgs = [gd, ops];
if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
return Plots.modifyFrames(gd ......
if(!data && !layout && !Lib.isPlotDiv(gd)) {
Lib.warn('Calling Plotly.plot as if redrawing ' +
'but this container doesn\'t yet have a plot.', gd);
}
function addFrames() {
if(frames) {
return Plotly.addFrames(gd, frames);
}
}
// transfer configuration options to gd until we move over to
// a more OO like model
setPlotContext(gd, config);
...function addTraces(gd, traces, newIndices) {
gd = helpers.getGraphDiv(gd);
var currentIndices = [],
undoFunc = Plotly.deleteTraces,
redoFunc = addTraces,
undoArgs = [gd, currentIndices],
redoArgs = [gd, traces], // no newIndices here
i,
promise;
// all validation is done elsewhere to remove clutter here
checkAddTracesArgs(gd, traces, newIndices);
// make sure traces is an array
if(!Array.isArray(traces)) {
traces = [traces];
}
// make sure traces do not repeat existing ones
traces = traces.map(function(trace) {
return Lib.extendFlat({}, trace);
});
helpers.cleanData(traces, gd.data);
// add the traces to gd.data (no redrawing yet!)
for(i = 0; i < traces.length; i++) {
gd.data.push(traces[i]);
}
// to continue, we need to call moveTraces which requires currentIndices
for(i = 0; i < traces.length; i++) {
currentIndices.push(-traces.length + i);
}
// if the user didn't define newIndices, they just want the traces appended
// i.e., we can simply redraw and be done
if(typeof newIndices === 'undefined') {
promise = Plotly.redraw(gd);
Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
return promise;
}
// make sure indices is property defined
if(!Array.isArray(newIndices)) {
newIndices = [newIndices];
}
try {
// this is redundant, but necessary to not catch later possible errors!
checkMoveTracesArgs(gd, currentIndices, newIndices);
}
catch(error) {
// something went wrong, reset gd to be safe and rethrow error
gd.data.splice(gd.data.length - traces.length, traces.length);
throw error;
}
// if we're here, the user has defined specific places to place the new traces
// this requires some extra work that moveTraces will do
Queue.startSequence(gd);
Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
promise = Plotly.moveTraces(gd, currentIndices, newIndices);
Queue.stopSequence(gd);
return promise;
}n/a
animate = function (gd, frameOrGroupNameOrFrameList, animationOpts) {
gd = helpers.getGraphDiv(gd);
if(!Lib.isPlotDiv(gd)) {
throw new Error(
'This element is not a Plotly plot: ' + gd + '. It\'s likely that you\'ve failed ' +
'to create a plot before animating it. For more details, see ' +
'https://plot.ly/javascript/animations/'
);
}
var trans = gd._transitionData;
// This is the queue of frames that will be animated as soon as possible. They
// are popped immediately upon the *start* of a transition:
if(!trans._frameQueue) {
trans._frameQueue = [];
}
animationOpts = Plots.supplyAnimationDefaults(animationOpts);
var transitionOpts = animationOpts.transition;
var frameOpts = animationOpts.frame;
// Since frames are popped immediately, an empty queue only means all frames have
// *started* to transition, not that the animation is complete. To solve that,
// track a separate counter that increments at the same time as frames are added
// to the queue, but decrements only when the transition is complete.
if(trans._frameWaitingCnt === undefined) {
trans._frameWaitingCnt = 0;
}
function getTransitionOpts(i) {
if(Array.isArray(transitionOpts)) {
if(i >= transitionOpts.length) {
return transitionOpts[0];
} else {
return transitionOpts[i];
}
} else {
return transitionOpts;
}
}
function getFrameOpts(i) {
if(Array.isArray(frameOpts)) {
if(i >= frameOpts.length) {
return frameOpts[0];
} else {
return frameOpts[i];
}
} else {
return frameOpts;
}
}
// Execute a callback after the wrapper function has been called n times.
// This is used to defer the resolution until a transition has resovled *and*
// the frame has completed. If it's not done this way, then we get a race
// condition in which the animation might resolve before a transition is complete
// or vice versa.
function callbackOnNthTime(cb, n) {
var cnt = 0;
return function() {
if(cb && ++cnt === n) {
return cb();
}
};
}
return new Promise(function(resolve, reject) {
function discardExistingFrames() {
if(trans._frameQueue.length === 0) {
return;
}
while(trans._frameQueue.length) {
var next = trans._frameQueue.pop();
if(next.onInterrupt) {
next.onInterrupt();
}
}
gd.emit('plotly_animationinterrupted', []);
}
function queueFrames(frameList) {
if(frameList.length === 0) return;
for(var i = 0; i < frameList.length; i++) {
var computedFrame;
if(frameList[i].type === 'byname') {
// If it's a named frame, compute it:
computedFrame = Plots.computeFrame(gd, frameList[i].name);
} else {
// Otherwise we must have been given a simple object, so treat
// the input itself as the computed frame.
computedFrame = frameList[i].data;
}
var frameOpts = getFrameOpts(i);
var transitionOpts = getTransitionOpts(i);
// It doesn't make much sense for the transition duration to be greater than
// the frame duration, so limit it:
transitionOpts.duration = Math.min(transitionOpts.duration, frameOpts.duration);
var nextFrame = {
frame: computedFrame,
name: frameList[i].name,
frameOpts: frameOpts,
transitionOpts: transitionOpts,
};
if(i === frameList.length - 1) {
// The last fr ...n/a
deleteFrames = function (gd, frameList) {
gd = helpers.getGraphDiv(gd);
if(!Lib.isPlotDiv(gd)) {
throw new Error('This element is not a Plotly plot: ' + gd);
}
var i, idx;
var _frames = gd._transitionData._frames;
var ops = [];
var revops = [];
if(!frameList) {
frameList = [];
for(i = 0; i < _frames.length; i++) {
frameList.push(i);
}
}
frameList = frameList.slice(0);
frameList.sort();
for(i = frameList.length - 1; i >= 0; i--) {
idx = frameList[i];
ops.push({type: 'delete', index: idx});
revops.unshift({type: 'insert', index: idx, value: _frames[idx]});
}
var undoFunc = Plots.modifyFrames,
redoFunc = Plots.modifyFrames,
undoArgs = [gd, revops],
redoArgs = [gd, ops];
if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
return Plots.modifyFrames(gd, ops);
}n/a
function deleteTraces(gd, indices) {
gd = helpers.getGraphDiv(gd);
var traces = [],
undoFunc = Plotly.addTraces,
redoFunc = deleteTraces,
undoArgs = [gd, traces, indices],
redoArgs = [gd, indices],
i,
deletedTrace;
// make sure indices are defined
if(typeof indices === 'undefined') {
throw new Error('indices must be an integer or array of integers.');
} else if(!Array.isArray(indices)) {
indices = [indices];
}
assertIndexArray(gd, indices, 'indices');
// convert negative indices to positive indices
indices = positivifyIndices(indices, gd.data.length - 1);
// we want descending here so that splicing later doesn't affect indexing
indices.sort(Lib.sorterDes);
for(i = 0; i < indices.length; i += 1) {
deletedTrace = gd.data.splice(indices[i], 1)[0];
traces.push(deletedTrace);
}
var promise = Plotly.redraw(gd);
Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
return promise;
}n/a
function extendTraces(gd, update, indices, maxPoints) {
gd = helpers.getGraphDiv(gd);
var undo = spliceTraces(gd, update, indices, maxPoints,
/*
* The Lengthen operation extends trace from end with insert
*/
function(target, insert) {
return target.concat(insert);
},
/*
* Window the trace keeping maxPoints, counting back from the end
*/
function(target, maxPoints) {
return target.splice(0, target.length - maxPoints);
});
var promise = Plotly.redraw(gd);
var undoArgs = [gd, undo.update, indices, undo.maxPoints];
Queue.add(gd, Plotly.prependTraces, undoArgs, extendTraces, arguments);
return promise;
}n/a
function moveTraces(gd, currentIndices, newIndices) {
gd = helpers.getGraphDiv(gd);
var newData = [],
movingTraceMap = [],
undoFunc = moveTraces,
redoFunc = moveTraces,
undoArgs = [gd, newIndices, currentIndices],
redoArgs = [gd, currentIndices, newIndices],
i;
// to reduce complexity here, check args elsewhere
// this throws errors where appropriate
checkMoveTracesArgs(gd, currentIndices, newIndices);
// make sure currentIndices is an array
currentIndices = Array.isArray(currentIndices) ? currentIndices : [currentIndices];
// if undefined, define newIndices to point to the end of gd.data array
if(typeof newIndices === 'undefined') {
newIndices = [];
for(i = 0; i < currentIndices.length; i++) {
newIndices.push(-currentIndices.length + i);
}
}
// make sure newIndices is an array if it's user-defined
newIndices = Array.isArray(newIndices) ? newIndices : [newIndices];
// convert negative indices to positive indices (they're the same length)
currentIndices = positivifyIndices(currentIndices, gd.data.length - 1);
newIndices = positivifyIndices(newIndices, gd.data.length - 1);
// at this point, we've coerced the index arrays into predictable forms
// get the traces that aren't being moved around
for(i = 0; i < gd.data.length; i++) {
// if index isn't in currentIndices, include it in ignored!
if(currentIndices.indexOf(i) === -1) {
newData.push(gd.data[i]);
}
}
// get a mapping of indices to moving traces
for(i = 0; i < currentIndices.length; i++) {
movingTraceMap.push({newIndex: newIndices[i], trace: gd.data[currentIndices[i]]});
}
// reorder this mapping by newIndex, ascending
movingTraceMap.sort(function(a, b) {
return a.newIndex - b.newIndex;
});
// now, add the moving traces back in, in order!
for(i = 0; i < movingTraceMap.length; i += 1) {
newData.splice(movingTraceMap[i].newIndex, 0, movingTraceMap[i].trace);
}
gd.data = newData;
var promise = Plotly.redraw(gd);
Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
return promise;
}...
throw error;
}
// if we're here, the user has defined specific places to place the new traces
// this requires some extra work that moveTraces will do
Queue.startSequence(gd);
Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
promise = Plotly.moveTraces(gd, currentIndices, newIndices);
Queue.stopSequence(gd);
return promise;
};
/**
* Delete traces at `indices` from gd.data array.
*
...newPlot = function (gd, data, layout, config) {
gd = helpers.getGraphDiv(gd);
// remove gl contexts
Plots.cleanPlot([], {}, gd._fullData || {}, gd._fullLayout || {});
Plots.purge(gd);
return Plotly.plot(gd, data, layout, config);
}n/a
plot = function (gd, data, layout, config) {
var frames;
gd = helpers.getGraphDiv(gd);
// Events.init is idempotent and bails early if gd has already been init'd
Events.init(gd);
if(Lib.isPlainObject(data)) {
var obj = data;
data = obj.data;
layout = obj.layout;
config = obj.config;
frames = obj.frames;
}
var okToPlot = Events.triggerHandler(gd, 'plotly_beforeplot', [data, layout, config]);
if(okToPlot === false) return Promise.reject();
// if there's no data or layout, and this isn't yet a plotly plot
// container, log a warning to help plotly.js users debug
if(!data && !layout && !Lib.isPlotDiv(gd)) {
Lib.warn('Calling Plotly.plot as if redrawing ' +
'but this container doesn\'t yet have a plot.', gd);
}
function addFrames() {
if(frames) {
return Plotly.addFrames(gd, frames);
}
}
// transfer configuration options to gd until we move over to
// a more OO like model
setPlotContext(gd, config);
if(!layout) layout = {};
// hook class for plots main container (in case of plotly.js
// this won't be #embedded-graph or .js-tab-contents)
d3.select(gd).classed('js-plotly-plot', true);
// off-screen getBoundingClientRect testing space,
// in #js-plotly-tester (and stored as gd._tester)
// so we can share cached text across tabs
Drawing.makeTester(gd);
// 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
// complete, and empty out the promise list again.
gd._promises = [];
var graphWasEmpty = ((gd.data || []).length === 0 && Array.isArray(data));
// if there is already data on the graph, append the new data
// if you only want to redraw, pass a non-array for data
if(Array.isArray(data)) {
helpers.cleanData(data, gd.data);
if(graphWasEmpty) gd.data = data;
else gd.data.push.apply(gd.data, data);
// for routines outside graph_obj that want a clean tab
// (rather than appending to an existing one) gd.empty
// is used to determine whether to make a new tab
gd.empty = false;
}
if(!gd.layout || graphWasEmpty) gd.layout = helpers.cleanLayout(layout);
// if the user is trying to drag the axes, allow new data and layout
// to come in but don't allow a replot.
if(gd._dragging && !gd._transitioning) {
// signal to drag handler that after everything else is done
// we need to replot, because something has changed
gd._replotPending = true;
return Promise.reject();
} else {
// we're going ahead with a replot now
gd._replotPending = false;
}
Plots.supplyDefaults(gd);
var fullLayout = gd._fullLayout;
// Polar plots
if(data && data[0] && data[0].r) return plotPolar(gd, data, layout);
// so we don't try to re-call Plotly.plot from inside
// legend and colorbar, if margins changed
fullLayout._replotting = true;
// make or remake the framework if we need to
if(graphWasEmpty) makePlotFramework(gd);
// polar need a different framework
if(gd.framework !== makePlotFramework) {
gd.framework = makePlotFramework;
makePlotFramework(gd);
}
// save initial show spikes once per graph
if(graphWasEmpty) Plotly.Axes.saveShowSpikeInitial(gd);
// prepare the data and find the autorange
// generate calcdata, if we need to
// to force redoing calcdata, just delete it before calling Plotly.plot
var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
if(recalc) Plots.doCalcdata(gd);
// in case it has changed, attach fullData traces to calcdata
for(var i = 0; i < gd.calcdata.length; i++) {
gd.calcdata[i][0].trace = gd._fullData[i];
}
/*
* start async-friendly code - now we're actually drawing things
*/
var oldmargins = ......
fullLayout._infolayer.selectAll('.cb' + uid).remove();
}
}
// loop over the base plot modules present on graph
var basePlotModules = fullLayout._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
basePlotModules[i].plot(gd);
}
// keep reference to shape layers in subplots
var layerSubplot = fullLayout._paper.selectAll('.layer-subplot');
fullLayout._shapeSubplotLayers = layerSubplot.selectAll('.shapelayer');
// styling separate from drawing
...function prependTraces(gd, update, indices, maxPoints) {
gd = helpers.getGraphDiv(gd);
var undo = spliceTraces(gd, update, indices, maxPoints,
/*
* The Lengthen operation extends trace by appending insert to start
*/
function(target, insert) {
return insert.concat(target);
},
/*
* Window the trace keeping maxPoints, counting forward from the start
*/
function(target, maxPoints) {
return target.splice(maxPoints, target.length);
});
var promise = Plotly.redraw(gd);
var undoArgs = [gd, undo.update, indices, undo.maxPoints];
Queue.add(gd, Plotly.extendTraces, undoArgs, prependTraces, arguments);
return promise;
}n/a
function purge(gd) {
gd = helpers.getGraphDiv(gd);
var fullLayout = gd._fullLayout || {},
fullData = gd._fullData || [];
// remove gl contexts
Plots.cleanPlot([], {}, fullData, fullLayout);
// purge properties
Plots.purge(gd);
// purge event emitter methods
Events.purge(gd);
// remove plot container
if(fullLayout._container) fullLayout._container.remove();
delete gd._context;
delete gd._replotPending;
delete gd._mouseDownTime;
delete gd._legendMouseDownTime;
delete gd._hmpixcount;
delete gd._hmlumcount;
return gd;
}...
*/
Plotly.newPlot = function(gd, data, layout, config) {
gd = helpers.getGraphDiv(gd);
// remove gl contexts
Plots.cleanPlot([], {}, gd._fullData || {}, gd._fullLayout || {});
Plots.purge(gd);
return Plotly.plot(gd, data, layout, config);
};
/**
* Wrap negative indicies to their positive counterparts.
*
* @param {Number[]} indices An array of indices
...redraw = function (gd) {
gd = helpers.getGraphDiv(gd);
if(!Lib.isPlotDiv(gd)) {
throw new Error('This element is not a Plotly plot: ' + gd);
}
helpers.cleanData(gd.data, gd.data);
helpers.cleanLayout(gd.layout);
gd.calcdata = undefined;
return Plotly.plot(gd).then(function() {
gd.emit('plotly_redraw');
return gd;
});
}...
/*
* Window the trace keeping maxPoints, counting back from the end
*/
function(target, maxPoints) {
return target.splice(0, target.length - maxPoints);
});
var promise = Plotly.redraw(gd);
var undoArgs = [gd, undo.update, indices, undo.maxPoints];
Queue.add(gd, Plotly.prependTraces, undoArgs, extendTraces, arguments);
return promise;
};
...function relayout(gd, astr, val) {
gd = helpers.getGraphDiv(gd);
helpers.clearPromiseQueue(gd);
if(gd.framework && gd.framework.isPolar) {
return Promise.resolve(gd);
}
var aobj = {};
if(typeof astr === 'string') {
aobj[astr] = val;
} else if(Lib.isPlainObject(astr)) {
aobj = Lib.extendFlat({}, astr);
} else {
Lib.warn('Relayout fail.', astr, val);
return Promise.reject();
}
if(Object.keys(aobj).length) gd.changed = true;
var specs = _relayout(gd, aobj),
flags = specs.flags;
// clear calcdata if required
if(flags.docalc) gd.calcdata = undefined;
// fill in redraw sequence
// even if we don't have anything left in aobj,
// something may have happened within relayout that we
// need to wait for
var seq = [Plots.previousPromises];
if(flags.layoutReplot) {
seq.push(subroutines.layoutReplot);
}
else if(Object.keys(aobj).length) {
Plots.supplyDefaults(gd);
if(flags.dolegend) seq.push(subroutines.doLegend);
if(flags.dolayoutstyle) seq.push(subroutines.layoutStyles);
if(flags.doticks) seq.push(subroutines.doTicksRelayout);
if(flags.domodebar) seq.push(subroutines.doModeBar);
if(flags.docamera) seq.push(subroutines.doCamera);
}
seq.push(Plots.rehover);
Queue.add(gd,
relayout, [gd, specs.undoit],
relayout, [gd, specs.redoit]
);
var plotDone = Lib.syncOrAsync(seq, gd);
if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
return plotDone.then(function() {
gd.emit('plotly_relayout', specs.eventData);
return gd;
});
}...
// autosizing doesn't count as a change that needs saving
var oldchanged = gd.changed;
// nor should it be included in the undo queue
gd.autoplay = true;
Plotly.relayout(gd, { autosize: true }).then(function() {
gd.changed = oldchanged;
resolve(gd);
});
}, 100);
});
};
...function restyle(gd, astr, val, traces) {
gd = helpers.getGraphDiv(gd);
helpers.clearPromiseQueue(gd);
var aobj = {};
if(typeof astr === 'string') aobj[astr] = val;
else if(Lib.isPlainObject(astr)) {
// the 3-arg form
aobj = Lib.extendFlat({}, astr);
if(traces === undefined) traces = val;
}
else {
Lib.warn('Restyle fail.', astr, val, traces);
return Promise.reject();
}
if(Object.keys(aobj).length) gd.changed = true;
var specs = _restyle(gd, aobj, traces),
flags = specs.flags;
// clear calcdata if required
if(flags.clearCalc) gd.calcdata = undefined;
// fill in redraw sequence
var seq = [];
if(flags.fullReplot) {
seq.push(Plotly.plot);
} else {
seq.push(Plots.previousPromises);
Plots.supplyDefaults(gd);
if(flags.dostyle) seq.push(subroutines.doTraceStyle);
if(flags.docolorbars) seq.push(subroutines.doColorBars);
}
seq.push(Plots.rehover);
Queue.add(gd,
restyle, [gd, specs.undoit, specs.traces],
restyle, [gd, specs.redoit, specs.traces]
);
var plotDone = Lib.syncOrAsync(seq, gd);
if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
return plotDone.then(function() {
gd.emit('plotly_restyle', specs.eventData);
return gd;
});
}n/a
function update(gd, traceUpdate, layoutUpdate, traces) {
gd = helpers.getGraphDiv(gd);
helpers.clearPromiseQueue(gd);
if(gd.framework && gd.framework.isPolar) {
return Promise.resolve(gd);
}
if(!Lib.isPlainObject(traceUpdate)) traceUpdate = {};
if(!Lib.isPlainObject(layoutUpdate)) layoutUpdate = {};
if(Object.keys(traceUpdate).length) gd.changed = true;
if(Object.keys(layoutUpdate).length) gd.changed = true;
var restyleSpecs = _restyle(gd, Lib.extendFlat({}, traceUpdate), traces),
restyleFlags = restyleSpecs.flags;
var relayoutSpecs = _relayout(gd, Lib.extendFlat({}, layoutUpdate)),
relayoutFlags = relayoutSpecs.flags;
// clear calcdata if required
if(restyleFlags.clearCalc || relayoutFlags.docalc) gd.calcdata = undefined;
// fill in redraw sequence
var seq = [];
if(restyleFlags.fullReplot && relayoutFlags.layoutReplot) {
var data = gd.data,
layout = gd.layout;
// clear existing data/layout on gd
// so that Plotly.plot doesn't try to extend them
gd.data = undefined;
gd.layout = undefined;
seq.push(function() { return Plotly.plot(gd, data, layout); });
}
else if(restyleFlags.fullReplot) {
seq.push(Plotly.plot);
}
else if(relayoutFlags.layoutReplot) {
seq.push(subroutines.layoutReplot);
}
else {
seq.push(Plots.previousPromises);
Plots.supplyDefaults(gd);
if(restyleFlags.dostyle) seq.push(subroutines.doTraceStyle);
if(restyleFlags.docolorbars) seq.push(subroutines.doColorBars);
if(relayoutFlags.dolegend) seq.push(subroutines.doLegend);
if(relayoutFlags.dolayoutstyle) seq.push(subroutines.layoutStyles);
if(relayoutFlags.doticks) seq.push(subroutines.doTicksRelayout);
if(relayoutFlags.domodebar) seq.push(subroutines.doModeBar);
if(relayoutFlags.doCamera) seq.push(subroutines.doCamera);
}
seq.push(Plots.rehover);
Queue.add(gd,
update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces],
update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces]
);
var plotDone = Lib.syncOrAsync(seq, gd);
if(!plotDone || !plotDone.then) plotDone = Promise.resolve(gd);
return plotDone.then(function() {
gd.emit('plotly_update', {
data: restyleSpecs.eventData,
layout: relayoutSpecs.eventData
});
return gd;
});
}...
var pkg = require('../../package.json');
module.exports = function updateVersion(pathToFile) {
fs.readFile(pathToFile, 'utf-8', function(err, code) {
var out = falafel(code, function(node) {
if(isVersionNode(node)) node.update('\'' + pkg.version +
x27;\'');
});
fs.writeFile(pathToFile, out, function(err) {
if(err) throw err;
});
});
};
..._hasPlotType = function (category) {
var basePlotModules = this._basePlotModules || [];
for(var i = 0; i < basePlotModules.length; i++) {
var _module = basePlotModules[i];
if(_module.name === category) return true;
}
return false;
}n/a
addLinks = function (gd) {
// Do not do anything if showLink and showSources are not set to true in config
if(!gd._context.showLink && !gd._context.showSources) return;
var fullLayout = gd._fullLayout;
var linkContainer = fullLayout._paper
.selectAll('text.js-plot-link-container').data([0]);
linkContainer.enter().append('text')
.classed('js-plot-link-container', true)
.style({
'font-family': '"Open Sans", Arial, sans-serif',
'font-size': '12px',
'fill': Color.defaultLine,
'pointer-events': 'all'
})
.each(function() {
var links = d3.select(this);
links.append('tspan').classed('js-link-to-tool', true);
links.append('tspan').classed('js-link-spacer', true);
links.append('tspan').classed('js-sourcelinks', true);
});
// The text node inside svg
var text = linkContainer.node(),
attrs = {
y: fullLayout._paper.attr('height') - 9
};
// If text's width is bigger than the layout
// Check that text is a child node or document.body
// because otherwise IE/Edge might throw an exception
// when calling getComputedTextLength().
// Apparently offsetParent is null for invisibles.
if(document.body.contains(text) && text.getComputedTextLength() >= (fullLayout.width - 20)) {
// Align the text at the left
attrs['text-anchor'] = 'start';
attrs.x = 5;
}
else {
// Align the text at the right
attrs['text-anchor'] = 'end';
attrs.x = fullLayout._paper.attr('width') - 7;
}
linkContainer.attr(attrs);
var toolspan = linkContainer.select('.js-link-to-tool'),
spacespan = linkContainer.select('.js-link-spacer'),
sourcespan = linkContainer.select('.js-sourcelinks');
if(gd._context.showSources) gd._context.showSources(gd);
// 'view in plotly' link for embedded plots
if(gd._context.showLink) positionPlayWithData(gd, toolspan);
// separator if we have both sources and tool link
spacespan.text((toolspan.text() && sourcespan.text()) ? ' - ' : '');
}...
Plots.style(gd);
// show annotations and shapes
Registry.getComponentMethod('shapes', 'draw')(gd);
Registry.getComponentMethod('annotations', 'draw')(gd);
// source links
Plots.addLinks(gd);
// Mark the first render as complete
fullLayout._replotting = false;
return Plots.previousPromises(gd);
}
...autoMargin = function (gd, id, o) {
var fullLayout = gd._fullLayout;
if(!fullLayout._pushmargin) fullLayout._pushmargin = {};
if(fullLayout.margin.autoexpand !== false) {
if(!o) delete fullLayout._pushmargin[id];
else {
var pad = o.pad === undefined ? 12 : o.pad;
// if the item is too big, just give it enough automargin to
// make sure you can still grab it and bring it back
if(o.l + o.r > fullLayout.width * 0.5) o.l = o.r = 0;
if(o.b + o.t > fullLayout.height * 0.5) o.b = o.t = 0;
fullLayout._pushmargin[id] = {
l: {val: o.x, size: o.l + pad},
r: {val: o.x, size: o.r + pad},
b: {val: o.y, size: o.b + pad},
t: {val: o.y, size: o.t + pad}
};
}
if(!fullLayout._replotting) plots.doAutoMargin(gd);
}
}...
Registry.getComponentMethod('sliders', 'draw')(gd);
Registry.getComponentMethod('updatemenus', 'draw')(gd);
for(i = 0; i < calcdata.length; i++) {
cd = calcdata[i];
trace = cd[0].trace;
if(trace.visible !== true || !trace._module.colorbar) {
Plots.autoMargin(gd, 'cb' + trace.uid);
}
else trace._module.colorbar(gd, cd);
}
Plots.doAutoMargin(gd);
return Plots.previousPromises(gd);
}
...cleanPlot = function (newFullData, newFullLayout, oldFullData, oldFullLayout) {
var i, j;
var basePlotModules = oldFullLayout._basePlotModules || [];
for(i = 0; i < basePlotModules.length; i++) {
var _module = basePlotModules[i];
if(_module.clean) {
_module.clean(newFullData, newFullLayout, oldFullData, oldFullLayout);
}
}
var hasPaper = !!oldFullLayout._paper;
var hasInfoLayer = !!oldFullLayout._infolayer;
oldLoop:
for(i = 0; i < oldFullData.length; i++) {
var oldTrace = oldFullData[i],
oldUid = oldTrace.uid;
for(j = 0; j < newFullData.length; j++) {
var newTrace = newFullData[j];
if(oldUid === newTrace.uid) continue oldLoop;
}
var query = (
'.hm' + oldUid +
',.contour' + oldUid +
',.carpet' + oldUid +
',#clip' + oldUid +
',.trace' + oldUid
);
// clean old heatmap, contour traces and clip paths
// that rely on uid identifiers
if(hasPaper) {
oldFullLayout._paper.selectAll(query).remove();
}
// clean old colorbars and range slider plot
if(hasInfoLayer) {
oldFullLayout._infolayer.selectAll('.cb' + oldUid).remove();
oldFullLayout._infolayer.selectAll('g.rangeslider-container')
.selectAll(query).remove();
}
}
}...
* @param {Object} layout
* @param {Object} config
*/
Plotly.newPlot = function(gd, data, layout, config) {
gd = helpers.getGraphDiv(gd);
// remove gl contexts
Plots.cleanPlot([], {}, gd._fullData || {}, gd._fullLayout || {});
Plots.purge(gd);
return Plotly.plot(gd, data, layout, config);
};
/**
* Wrap negative indicies to their positive counterparts.
...computeAPICommandBindings = function (gd, method, args) {
var bindings;
if(!Array.isArray(args)) args = [];
switch(method) {
case 'restyle':
bindings = computeDataBindings(gd, args);
break;
case 'relayout':
bindings = computeLayoutBindings(gd, args);
break;
case 'update':
bindings = computeDataBindings(gd, [args[0], args[2]])
.concat(computeLayoutBindings(gd, [args[1]]));
break;
case 'animate':
bindings = computeAnimateBindings(gd, args);
break;
default:
// This is the case where intelligent logic about what affects
// this command is not implemented. It causes no ill effects.
// For example, addFrames simply won't bind to a control component.
bindings = [];
}
return bindings;
}...
if(!Array.isArray(args)) args = [];
// If any command has no method, refuse to bind:
if(!method) {
return false;
}
var bindings = exports.computeAPICommandBindings(gd, method, args);
// Right now, handle one and *only* one property being set:
if(bindings.length !== 1) {
return false;
}
if(!refBinding) {
...computeFrame = function (gd, frameName) {
var frameLookup = gd._transitionData._frameHash;
var i, traceIndices, traceIndex, destIndex;
// Null or undefined will fail on .toString(). We'll allow numbers since we
// make it clear frames must be given string names, but we'll allow numbers
// here since they're otherwise fine for looking up frames as long as they're
// properly cast to strings. We really just want to ensure here that this
// 1) doesn't fail, and
// 2) doens't give an incorrect answer (which String(frameName) would)
if(!frameName) {
throw new Error('computeFrame must be given a string frame name');
}
var framePtr = frameLookup[frameName.toString()];
// Return false if the name is invalid:
if(!framePtr) {
return false;
}
var frameStack = [framePtr];
var frameNameStack = [framePtr.name];
// Follow frame pointers:
while(framePtr.baseframe && (framePtr = frameLookup[framePtr.baseframe.toString()])) {
// Avoid infinite loops:
if(frameNameStack.indexOf(framePtr.name) !== -1) break;
frameStack.push(framePtr);
frameNameStack.push(framePtr.name);
}
// A new object for the merged result:
var result = {};
// Merge, starting with the last and ending with the desired frame:
while((framePtr = frameStack.pop())) {
if(framePtr.layout) {
result.layout = plots.extendLayout(result.layout, framePtr.layout);
}
if(framePtr.data) {
if(!result.data) {
result.data = [];
}
traceIndices = framePtr.traces;
if(!traceIndices) {
// If not defined, assume serial order starting at zero
traceIndices = [];
for(i = 0; i < framePtr.data.length; i++) {
traceIndices[i] = i;
}
}
if(!result.traces) {
result.traces = [];
}
for(i = 0; i < framePtr.data.length; i++) {
// Loop through this frames data, find out where it should go,
// and merge it!
traceIndex = traceIndices[i];
if(traceIndex === undefined || traceIndex === null) {
continue;
}
destIndex = result.traces.indexOf(traceIndex);
if(destIndex === -1) {
destIndex = result.data.length;
result.traces[destIndex] = traceIndex;
}
result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr.data[i]);
}
}
}
return result;
}...
if(frameList.length === 0) return;
for(var i = 0; i < frameList.length; i++) {
var computedFrame;
if(frameList[i].type === 'byname') {
// If it's a named frame, compute it:
computedFrame = Plots.computeFrame(gd, frameList[i].name);
} else {
// Otherwise we must have been given a simple object, so treat
// the input itself as the computed frame.
computedFrame = frameList[i].data;
}
var frameOpts = getFrameOpts(i);
...createTransitionData = function (gd) {
// Set up the default keyframe if it doesn't exist:
if(!gd._transitionData) {
gd._transitionData = {};
}
if(!gd._transitionData._frames) {
gd._transitionData._frames = [];
}
if(!gd._transitionData._frameHash) {
gd._transitionData._frameHash = {};
}
if(!gd._transitionData._counter) {
gd._transitionData._counter = 0;
}
if(!gd._transitionData._interruptCallbacks) {
gd._transitionData._interruptCallbacks = [];
}
}...
var oldFullData = gd._fullData || [],
newFullData = gd._fullData = [],
newData = gd.data || [];
var i;
// Create all the storage space for frames, but only if doesn't already exist
if(!gd._transitionData) plots.createTransitionData(gd);
// first fill in what we can of layout without looking at data
// because fullData needs a few things from layout
if(oldFullLayout._initialAutoSizeIsDone) {
// coerce the updated layout while preserving width and height
...doAutoMargin = function (gd) {
var fullLayout = gd._fullLayout;
if(!fullLayout._size) fullLayout._size = {};
if(!fullLayout._pushmargin) fullLayout._pushmargin = {};
var gs = fullLayout._size,
oldmargins = JSON.stringify(gs);
// adjust margins for outside components
// fullLayout.margin is the requested margin,
// fullLayout._size has margins and plotsize after adjustment
var ml = Math.max(fullLayout.margin.l || 0, 0),
mr = Math.max(fullLayout.margin.r || 0, 0),
mt = Math.max(fullLayout.margin.t || 0, 0),
mb = Math.max(fullLayout.margin.b || 0, 0),
pm = fullLayout._pushmargin;
if(fullLayout.margin.autoexpand !== false) {
// fill in the requested margins
pm.base = {
l: {val: 0, size: ml},
r: {val: 1, size: mr},
t: {val: 1, size: mt},
b: {val: 0, size: mb}
};
// now cycle through all the combinations of l and r
// (and t and b) to find the required margins
var pmKeys = Object.keys(pm);
for(var i = 0; i < pmKeys.length; i++) {
var k1 = pmKeys[i];
var pushleft = pm[k1].l || {},
pushbottom = pm[k1].b || {},
fl = pushleft.val,
pl = pushleft.size,
fb = pushbottom.val,
pb = pushbottom.size;
for(var j = 0; j < pmKeys.length; j++) {
var k2 = pmKeys[j];
if(isNumeric(pl) && pm[k2].r) {
var fr = pm[k2].r.val,
pr = pm[k2].r.size;
if(fr > fl) {
var newl = (pl * fr +
(pr - fullLayout.width) * fl) / (fr - fl),
newr = (pr * (1 - fl) +
(pl - fullLayout.width) * (1 - fr)) / (fr - fl);
if(newl >= 0 && newr >= 0 && newl + newr > ml + mr) {
ml = newl;
mr = newr;
}
}
}
if(isNumeric(pb) && pm[k2].t) {
var ft = pm[k2].t.val,
pt = pm[k2].t.size;
if(ft > fb) {
var newb = (pb * ft +
(pt - fullLayout.height) * fb) / (ft - fb),
newt = (pt * (1 - fb) +
(pb - fullLayout.height) * (1 - ft)) / (ft - fb);
if(newb >= 0 && newt >= 0 && newb + newt > mb + mt) {
mb = newb;
mt = newt;
}
}
}
}
}
}
gs.l = Math.round(ml);
gs.r = Math.round(mr);
gs.t = Math.round(mt);
gs.b = Math.round(mb);
gs.p = Math.round(fullLayout.margin.pad);
gs.w = Math.round(fullLayout.width) - gs.l - gs.r;
gs.h = Math.round(fullLayout.height) - gs.t - gs.b;
// if things changed and we're not already redrawing, trigger a redraw
if(!fullLayout._replotting && oldmargins !== '{}' &&
oldmargins !== JSON.stringify(fullLayout._size)) {
return Plotly.plot(gd);
}
}...
trace = cd[0].trace;
if(trace.visible !== true || !trace._module.colorbar) {
Plots.autoMargin(gd, 'cb' + trace.uid);
}
else trace._module.colorbar(gd, cd);
}
Plots.doAutoMargin(gd);
return Plots.previousPromises(gd);
}
// in case the margins changed, draw margin pushers again
function marginPushersAgain() {
var seq = JSON.stringify(fullLayout._size) === oldmargins ?
[] :
...doCalcdata = function (gd, traces) {
var axList = Plotly.Axes.list(gd),
fullData = gd._fullData,
fullLayout = gd._fullLayout;
var trace, _module, i, j;
var hasCategoryAxis = false;
// XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without
// *all* needing doCalcdata:
var calcdata = new Array(fullData.length);
var oldCalcdata = (gd.calcdata || []).slice(0);
gd.calcdata = calcdata;
// extra helper variables
// firstscatter: fill-to-next on the first trace goes to zero
gd.firstscatter = true;
// how many box plots do we have (in case they're grouped)
gd.numboxes = 0;
// for calculating avg luminosity of heatmaps
gd._hmpixcount = 0;
gd._hmlumcount = 0;
// for sharing colors across pies (and for legend)
fullLayout._piecolormap = {};
fullLayout._piedefaultcolorcount = 0;
// initialize the category list, if there is one, so we start over
// to be filled in later by ax.d2c
for(i = 0; i < axList.length; i++) {
axList[i]._categories = axList[i]._initialCategories.slice();
// Build the lookup map for initialized categories
axList[i]._categoriesMap = {};
for(j = 0; j < axList[i]._categories.length; j++) {
axList[i]._categoriesMap[axList[i]._categories[j]] = j;
}
if(axList[i].type === 'category') hasCategoryAxis = true;
}
// If traces were specified and this trace was not included,
// then transfer it over from the old calcdata:
for(i = 0; i < fullData.length; i++) {
if(Array.isArray(traces) && traces.indexOf(i) === -1) {
calcdata[i] = oldCalcdata[i];
continue;
}
}
var hasCalcTransform = false;
// transform loop
for(i = 0; i < fullData.length; i++) {
trace = fullData[i];
if(trace.visible === true && trace.transforms) {
_module = trace._module;
// we need one round of trace module calc before
// the calc transform to 'fill in' the categories list
// used for example in the data-to-coordinate method
if(_module && _module.calc) _module.calc(gd, trace);
for(j = 0; j < trace.transforms.length; j++) {
var transform = trace.transforms[j];
_module = transformsRegistry[transform.type];
if(_module && _module.calcTransform) {
hasCalcTransform = true;
_module.calcTransform(gd, trace, transform);
}
}
}
}
// clear stuff that should recomputed in 'regular' loop
if(hasCalcTransform) {
for(i = 0; i < axList.length; i++) {
axList[i]._min = [];
axList[i]._max = [];
axList[i]._categories = [];
// Reset the look up map
axList[i]._categoriesMap = {};
}
}
// 'regular' loop
for(i = 0; i < fullData.length; i++) {
var cd = [];
trace = fullData[i];
if(trace.visible === true) {
_module = trace._module;
if(_module && _module.calc) cd = _module.calc(gd, trace);
}
// Make sure there is a first point.
//
// This ensures there is a calcdata item for every trace,
// even if cartesian logic doesn't handle it (for things like legends).
if(!Array.isArray(cd) || !cd[0]) {
cd = [{x: BADNUM, y: BADNUM}];
}
// add the trace-wide properties to the first point,
// per point properties to every point
// t is the holder for trace-wide properties
if(!cd[0].t) cd[0].t = {};
cd[0].trace = trace;
calcdata[i] = cd;
}
// To handle the case of components using category names as coordinates, we
// need to re-supply defaults for these objects now, after calc has
// finished populating the category mappings
// Any component that uses `Axes.coercePosition` falls into this category
if(hasCategoryAxis) {
var dataRe ......
if(graphWasEmpty) Plotly.Axes.saveShowSpikeInitial(gd);
// prepare the data and find the autorange
// generate calcdata, if we need to
// to force redoing calcdata, just delete it before calling Plotly.plot
var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
if(recalc) Plots.doCalcdata(gd);
// in case it has changed, attach fullData traces to calcdata
for(var i = 0; i < gd.calcdata.length; i++) {
gd.calcdata[i][0].trace = gd._fullData[i];
}
/*
...executeAPICommand = function (gd, method, args) {
var apiMethod = Plotly[method];
var allArgs = [gd];
if(!Array.isArray(args)) args = [];
for(var i = 0; i < args.length; i++) {
allArgs.push(args[i]);
}
return apiMethod.apply(null, allArgs).catch(function(err) {
Lib.warn('API call to Plotly.' + method + ' rejected.', err);
return Promise.reject(err);
});
}n/a
extendLayout = function (destLayout, srcLayout) {
return plots.extendObjectWithContainers(destLayout, srcLayout, plots.layoutArrayContainers);
}...
// A new object for the merged result:
var result = {};
// Merge, starting with the last and ending with the desired frame:
while((framePtr = frameStack.pop())) {
if(framePtr.layout) {
result.layout = plots.extendLayout(result.layout, framePtr.layout);
}
if(framePtr.data) {
if(!result.data) {
result.data = [];
}
traceIndices = framePtr.traces;
...extendObjectWithContainers = function (dest, src, containerPaths) {
var containerProp, containerVal, i, j, srcProp, destProp, srcContainer, destContainer;
var copy = Lib.extendDeepNoArrays({}, src || {});
var expandedObj = Lib.expandObjectPaths(copy);
var containerObj = {};
// Step through and extract any container properties. Otherwise extendDeepNoArrays
// will clobber any existing properties with an empty array and then supplyDefaults
// will reset everything to defaults.
if(containerPaths && containerPaths.length) {
for(i = 0; i < containerPaths.length; i++) {
containerProp = Lib.nestedProperty(expandedObj, containerPaths[i]);
containerVal = containerProp.get();
if(containerVal === undefined) {
Lib.nestedProperty(containerObj, containerPaths[i]).set(null);
}
else {
containerProp.set(null);
Lib.nestedProperty(containerObj, containerPaths[i]).set(containerVal);
}
}
}
dest = Lib.extendDeepNoArrays(dest || {}, expandedObj);
if(containerPaths && containerPaths.length) {
for(i = 0; i < containerPaths.length; i++) {
srcProp = Lib.nestedProperty(containerObj, containerPaths[i]);
srcContainer = srcProp.get();
if(!srcContainer) continue;
destProp = Lib.nestedProperty(dest, containerPaths[i]);
destContainer = destProp.get();
if(!Array.isArray(destContainer)) {
destContainer = [];
destProp.set(destContainer);
}
for(j = 0; j < srcContainer.length; j++) {
var srcObj = srcContainer[j];
if(srcObj === null) destContainer[j] = null;
else {
destContainer[j] = plots.extendObjectWithContainers(destContainer[j], srcObj);
}
}
destProp.set(destContainer);
}
}
return dest;
}...
}
for(j = 0; j < srcContainer.length; j++) {
var srcObj = srcContainer[j];
if(srcObj === null) destContainer[j] = null;
else {
destContainer[j] = plots.extendObjectWithContainers(destContainer[j],
srcObj);
}
}
destProp.set(destContainer);
}
}
...extendTrace = function (destTrace, srcTrace) {
return plots.extendObjectWithContainers(destTrace, srcTrace, plots.dataArrayContainers);
}...
destIndex = result.traces.indexOf(traceIndex);
if(destIndex === -1) {
destIndex = result.data.length;
result.traces[destIndex] = traceIndex;
}
result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr
.data[i]);
}
}
}
return result;
};
...function findSubplotIds(data, type) {
var subplotIds = [];
if(!plots.subplotsRegistry[type]) return subplotIds;
var attr = plots.subplotsRegistry[type].attr;
for(var i = 0; i < data.length; i++) {
var trace = data[i];
if(plots.traceIs(trace, type) && subplotIds.indexOf(trace[attr]) === -1) {
subplotIds.push(trace[attr]);
}
}
return subplotIds;
}...
*/
module.exports = function handleSubplotDefaults(layoutIn, layoutOut, fullData, opts) {
var subplotType = opts.type,
subplotAttributes = opts.attributes,
handleDefaults = opts.handleDefaults,
partition = opts.partition || 'x';
var ids = Plots.findSubplotIds(fullData, subplotType),
idsLength = ids.length;
var subplotLayoutIn, subplotLayoutOut;
function coerce(attr, dflt) {
return Lib.coerce(subplotLayoutIn, subplotLayoutOut, subplotAttributes, attr, dflt);
}
...generalUpdatePerTraceModule = function (subplot, subplotCalcData, subplotLayout) {
var traceHashOld = subplot.traceHash,
traceHash = {},
i;
function filterVisible(calcDataIn) {
var calcDataOut = [];
for(var i = 0; i < calcDataIn.length; i++) {
var calcTrace = calcDataIn[i],
trace = calcTrace[0].trace;
if(trace.visible === true) calcDataOut.push(calcTrace);
}
return calcDataOut;
}
// build up moduleName -> calcData hash
for(i = 0; i < subplotCalcData.length; i++) {
var calcTraces = subplotCalcData[i],
trace = calcTraces[0].trace;
// skip over visible === false traces
// as they don't have `_module` ref
if(trace.visible) {
traceHash[trace.type] = traceHash[trace.type] || [];
traceHash[trace.type].push(calcTraces);
}
}
var moduleNamesOld = Object.keys(traceHashOld);
var moduleNames = Object.keys(traceHash);
// when a trace gets deleted, make sure that its module's
// plot method is called so that it is properly
// removed from the DOM.
for(i = 0; i < moduleNamesOld.length; i++) {
var moduleName = moduleNamesOld[i];
if(moduleNames.indexOf(moduleName) === -1) {
var fakeCalcTrace = traceHashOld[moduleName][0],
fakeTrace = fakeCalcTrace[0].trace;
fakeTrace.visible = false;
traceHash[moduleName] = [fakeCalcTrace];
}
}
// update list of module names to include 'fake' traces added above
moduleNames = Object.keys(traceHash);
// call module plot method
for(i = 0; i < moduleNames.length; i++) {
var moduleCalcData = traceHash[moduleNames[i]],
_module = moduleCalcData[0][0].trace._module;
_module.plot(subplot, filterVisible(moduleCalcData), subplotLayout);
}
// update moduleName -> calcData hash
subplot.traceHash = traceHash;
}...
// TODO handle topojson-is-loading case
// to avoid making multiple request while streaming
};
proto.onceTopojsonIsLoaded = function(geoCalcData, geoLayout) {
this.drawLayout(geoLayout);
Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);
this.render();
};
proto.makeProjection = function(geoLayout) {
var projLayout = geoLayout.projection,
projType = projLayout.type,
...getComponentMethod = function (name, method) {
var _module = exports.componentsRegistry[name];
if(!_module) return noop;
return _module[method] || noop;
}...
* dateTick0: get the canonical tick for this calendar
*
* bool sunday is for week ticks, shift it to a Sunday.
*/
exports.dateTick0 = function(calendar, sunday) {
if(isWorldCalendar(calendar)) {
return sunday ?
Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY
')[calendar] :
Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
}
else {
return sunday ? '2000-01-02' : '2000-01-01';
}
};
...getModule = function (trace) {
if(trace.r !== undefined) {
Loggers.warn('Tried to put a polar trace ' +
'on an incompatible graph of cartesian ' +
'data. Ignoring this dataset.', trace
);
return false;
}
var _module = exports.modules[getTraceType(trace)];
if(!_module) return false;
return _module._module;
}...
var attr = subplotsRegistry[subplotType].attr;
if(attr) coerceSubplotAttr(subplotType, attr);
}
if(visible) {
var _module = plots.getModule(traceOut);
traceOut._module = _module;
// gets overwritten in pie, geo and ternary modules
coerce('hoverinfo', (layout._dataLength === 1) ? 'x+y+z+text' : undefined);
if(plots.traceIs(traceOut, 'showLegend')) {
coerce('showlegend');
...getSubplotCalcData = function (calcData, type, subplotId) {
if(!plots.subplotsRegistry[type]) return [];
var attr = plots.subplotsRegistry[type].attr;
var subplotCalcData = [];
for(var i = 0; i < calcData.length; i++) {
var calcTrace = calcData[i],
trace = calcTrace[0].trace;
if(trace[attr] === subplotId) subplotCalcData.push(calcTrace);
}
return subplotCalcData;
}...
var c = require('./constants');
exports.name = 'parcoords';
exports.attr = 'type';
exports.plot = function(gd) {
var calcData = Plots.getSubplotCalcData(gd.calcdata, 'parcoords', 'parcoords
');
if(calcData.length) parcoordsPlot(gd, calcData);
};
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
var hadParcoords = (oldFullLayout._has && oldFullLayout._has('parcoords'));
var hasParcoords = (newFullLayout._has && newFullLayout._has('parcoords'));
...function getSubplotData(data, type, subplotId) {
if(!plots.subplotsRegistry[type]) return [];
var attr = plots.subplotsRegistry[type].attr,
subplotData = [],
trace;
for(var i = 0; i < data.length; i++) {
trace = data[i];
if(type === 'gl2d' && plots.traceIs(trace, 'gl2d')) {
var spmatch = Plotly.Axes.subplotMatch,
subplotX = 'x' + subplotId.match(spmatch)[1],
subplotY = 'y' + subplotId.match(spmatch)[2];
if(trace[attr[0]] === subplotX && trace[attr[1]] === subplotY) {
subplotData.push(trace);
}
}
else {
if(trace[attr] === subplotId) subplotData.push(trace);
}
}
return subplotData;
}n/a
function getSubplotIds(layout, type) {
var _module = plots.subplotsRegistry[type];
if(!_module) return [];
// layout must be 'fullLayout' here
if(type === 'cartesian' && (!layout._has || !layout._has('cartesian'))) return [];
if(type === 'gl2d' && (!layout._has || !layout._has('gl2d'))) return [];
if(type === 'cartesian' || type === 'gl2d') {
return Object.keys(layout._plots || {});
}
var idRegex = _module.idRegex,
layoutKeys = Object.keys(layout),
subplotIds = [];
for(var i = 0; i < layoutKeys.length; i++) {
var layoutKey = layoutKeys[i];
if(idRegex.test(layoutKey)) subplotIds.push(layoutKey);
}
// order the ids
var idLen = _module.idRoot.length;
subplotIds.sort(function(a, b) {
var aNum = +(a.substr(idLen) || 1),
bNum = +(b.substr(idLen) || 1);
return aNum - bNum;
});
return subplotIds;
}...
if(!layout.scene) layout.scene = layout.scene1;
delete layout.scene1;
}
/*
* Clean up Scene layouts
*/
var sceneIds = Plots.getSubplotIds(layout, 'gl3d');
for(i = 0; i < sceneIds.length; i++) {
var scene = layout[sceneIds[i]];
// clean old Camera coords
var cameraposition = scene.cameraposition;
if(Array.isArray(cameraposition) && cameraposition[0].length === 4) {
var rotation = cameraposition[0],
...graphJson = function (gd, dataonly, mode, output, useDefaults) {
// if the defaults aren't supplied yet, we need to do that...
if((useDefaults && dataonly && !gd._fullData) ||
(useDefaults && !dataonly && !gd._fullLayout)) {
plots.supplyDefaults(gd);
}
var data = (useDefaults) ? gd._fullData : gd.data,
layout = (useDefaults) ? gd._fullLayout : gd.layout,
frames = (gd._transitionData || {})._frames;
function stripObj(d) {
if(typeof d === 'function') {
return null;
}
if(Lib.isPlainObject(d)) {
var o = {}, v, src;
for(v in d) {
// remove private elements and functions
// _ is for private, [ is a mistake ie [object Object]
if(typeof d[v] === 'function' ||
['_', '['].indexOf(v.charAt(0)) !== -1) {
continue;
}
// look for src/data matches and remove the appropriate one
if(mode === 'keepdata') {
// keepdata: remove all ...src tags
if(v.substr(v.length - 3) === 'src') {
continue;
}
}
else if(mode === 'keepstream') {
// keep sourced data if it's being streamed.
// similar to keepref, but if the 'stream' object exists
// in a trace, we will keep the data array.
src = d[v + 'src'];
if(typeof src === 'string' && src.indexOf(':') > 0) {
if(!Lib.isPlainObject(d.stream)) {
continue;
}
}
}
else if(mode !== 'keepall') {
// keepref: remove sourced data but only
// if the source tag is well-formed
src = d[v + 'src'];
if(typeof src === 'string' && src.indexOf(':') > 0) {
continue;
}
}
// OK, we're including this... recurse into it
o[v] = stripObj(d[v]);
}
return o;
}
if(Array.isArray(d)) {
return d.map(stripObj);
}
// convert native dates to date strings...
// mostly for external users exporting to plotly
if(Lib.isJSDate(d)) return Lib.ms2DateTimeLocal(+d);
return d;
}
var obj = {
data: (data || []).map(function(v) {
var d = stripObj(v);
// fit has some little arrays in it that don't contain data,
// just fit params and meta
if(dataonly) { delete d.fit; }
return d;
})
};
if(!dataonly) { obj.layout = stripObj(layout); }
if(gd.framework && gd.framework.isPolar) obj = gd.framework.getConfig();
if(frames) obj.frames = stripObj(frames);
return (output === 'object') ? obj : JSON.stringify(obj);
}...
var hiddenformInput = hiddenform
.append('input')
.attr({
type: 'text',
name: 'data'
});
hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata');
hiddenform.node().submit();
hiddenformDiv.remove();
gd.emit('plotly_afterexport');
return false;
};
...hasSimpleAPICommandBindings = function (gd, commandList, bindingsByValue) {
var i;
var n = commandList.length;
var refBinding;
for(i = 0; i < n; i++) {
var binding;
var command = commandList[i];
var method = command.method;
var args = command.args;
if(!Array.isArray(args)) args = [];
// If any command has no method, refuse to bind:
if(!method) {
return false;
}
var bindings = exports.computeAPICommandBindings(gd, method, args);
// Right now, handle one and *only* one property being set:
if(bindings.length !== 1) {
return false;
}
if(!refBinding) {
refBinding = bindings[0];
if(Array.isArray(refBinding.traces)) {
refBinding.traces.sort();
}
} else {
binding = bindings[0];
if(binding.type !== refBinding.type) {
return false;
}
if(binding.prop !== refBinding.prop) {
return false;
}
if(Array.isArray(refBinding.traces)) {
if(Array.isArray(binding.traces)) {
binding.traces.sort();
for(var j = 0; j < refBinding.traces.length; j++) {
if(refBinding.traces[j] !== binding.traces[j]) {
return false;
}
}
} else {
return false;
}
} else {
if(binding.prop !== refBinding.prop) {
return false;
}
}
}
binding = bindings[0];
var value = binding.value;
if(Array.isArray(value)) {
if(value.length === 1) {
value = value[0];
} else {
return false;
}
}
if(bindingsByValue) {
bindingsByValue[value] = i;
}
}
return refBinding;
}...
if(!ret.cache) {
ret.cache = {};
}
// Either create or just recompute this:
ret.lookupTable = {};
var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
if(container && container._commandObserver) {
if(!binding) {
// If container exists and there are no longer any bindings,
// remove existing:
if(container._commandObserver.remove) {
container._commandObserver.remove();
...linkSubplots = function (newFullData, newFullLayout, oldFullData, oldFullLayout) {
var oldSubplots = oldFullLayout._plots || {},
newSubplots = newFullLayout._plots = {};
var mockGd = {
_fullData: newFullData,
_fullLayout: newFullLayout
};
var ids = Plotly.Axes.getSubplots(mockGd);
for(var i = 0; i < ids.length; i++) {
var id = ids[i],
oldSubplot = oldSubplots[id],
plotinfo;
if(oldSubplot) {
plotinfo = newSubplots[id] = oldSubplot;
if(plotinfo._scene2d) {
plotinfo._scene2d.updateRefs(newFullLayout);
}
}
else {
plotinfo = newSubplots[id] = {};
plotinfo.id = id;
}
plotinfo.xaxis = Plotly.Axes.getFromId(mockGd, id, 'x');
plotinfo.yaxis = Plotly.Axes.getFromId(mockGd, id, 'y');
}
}...
newFullLayout._hasTernary = newFullLayout._has('ternary');
newFullLayout._hasPie = newFullLayout._has('pie');
// clean subplots and other artifacts from previous plot calls
plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);
// relink / initialize subplot axis objects
plots.linkSubplots(newFullData, newFullLayout, oldFullData, oldFullLayout);
// relink functions and _ attributes to promote consistency between plots
relinkPrivateKeys(newFullLayout, oldFullLayout);
// TODO may return a promise
plots.doAutoMargin(gd);
...manageCommandObserver = function (gd, container, commandList, onchange) {
var ret = {};
var enabled = true;
if(container && container._commandObserver) {
ret = container._commandObserver;
}
if(!ret.cache) {
ret.cache = {};
}
// Either create or just recompute this:
ret.lookupTable = {};
var binding = exports.hasSimpleAPICommandBindings(gd, commandList, ret.lookupTable);
if(container && container._commandObserver) {
if(!binding) {
// If container exists and there are no longer any bindings,
// remove existing:
if(container._commandObserver.remove) {
container._commandObserver.remove();
container._commandObserver = null;
return ret;
}
} else {
// If container exists and there *are* bindings, then the lookup
// table should have been updated and check is already attached,
// so there's nothing to be done:
return ret;
}
}
// Determine whether there's anything to do for this binding:
if(binding) {
// Build the cache:
bindingValueHasChanged(gd, binding, ret.cache);
ret.check = function check() {
if(!enabled) return;
var update = bindingValueHasChanged(gd, binding, ret.cache);
if(update.changed && onchange) {
// Disable checks for the duration of this command in order to avoid
// infinite loops:
if(ret.lookupTable[update.value] !== undefined) {
ret.disable();
Promise.resolve(onchange({
value: update.value,
type: binding.type,
prop: binding.prop,
traces: binding.traces,
index: ret.lookupTable[update.value]
})).then(ret.enable, ret.enable);
}
}
return update.changed;
};
var checkEvents = [
'plotly_relayout',
'plotly_redraw',
'plotly_restyle',
'plotly_update',
'plotly_animatingframe',
'plotly_afterplot'
];
for(var i = 0; i < checkEvents.length; i++) {
gd._internalOn(checkEvents[i], ret.check);
}
ret.remove = function() {
for(var i = 0; i < checkEvents.length; i++) {
gd._removeInternalListener(checkEvents[i], ret.check);
}
};
} else {
// TODO: It'd be really neat to actually give a *reason* for this, but at least a warning
// is a start
Lib.warn('Unable to automatically bind plot updates to API command');
ret.lookupTable = {};
ret.remove = function() {};
}
ret.disable = function disable() {
enabled = false;
};
ret.enable = function enable() {
enabled = true;
};
if(container) {
container._commandObserver = ret;
}
return ret;
}n/a
modifyFrames = function (gd, operations) {
var i, op, frame;
var _frames = gd._transitionData._frames;
var _hash = gd._transitionData._frameHash;
for(i = 0; i < operations.length; i++) {
op = operations[i];
switch(op.type) {
// No reason this couldn't exist, but is currently unused/untested:
/* case 'rename':
frame = _frames[op.index];
delete _hash[frame.name];
_hash[op.name] = frame;
frame.name = op.name;
break;*/
case 'replace':
frame = op.value;
var oldName = (_frames[op.index] || {}).name;
var newName = frame.name;
_frames[op.index] = _hash[newName] = frame;
if(newName !== oldName) {
// If name has changed in addition to replacement, then update
// the lookup table:
delete _hash[oldName];
_hash[newName] = frame;
}
break;
case 'insert':
frame = op.value;
_hash[frame.name] = frame;
_frames.splice(op.index, 0, frame);
break;
case 'delete':
frame = _frames[op.index];
delete _hash[frame.name];
_frames.splice(op.index, 1);
break;
}
}
return Promise.resolve();
}...
var undoFunc = Plots.modifyFrames,
redoFunc = Plots.modifyFrames,
undoArgs = [gd, revops],
redoArgs = [gd, ops];
if(Queue) Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
return Plots.modifyFrames(gd, ops);
};
/**
* Delete frame
*
* @param {string id or DOM element} gd
* the id or DOM element of the graph container div
...function plotAutoSize(gd, layout, fullLayout) {
var context = gd._context || {},
frameMargins = context.frameMargins,
newWidth,
newHeight;
var isPlotDiv = Lib.isPlotDiv(gd);
if(isPlotDiv) gd.emit('plotly_autosize');
// embedded in an iframe - just take the full iframe size
// if we get to this point, with no aspect ratio restrictions
if(context.fillFrame) {
newWidth = window.innerWidth;
newHeight = window.innerHeight;
// somehow we get a few extra px height sometimes...
// just hide it
document.body.style.overflow = 'hidden';
}
else if(isNumeric(frameMargins) && frameMargins > 0) {
var reservedMargins = calculateReservedMargins(gd._boundingBoxMargins),
reservedWidth = reservedMargins.left + reservedMargins.right,
reservedHeight = reservedMargins.bottom + reservedMargins.top,
factor = 1 - 2 * frameMargins;
var gdBB = fullLayout._container && fullLayout._container.node ?
fullLayout._container.node().getBoundingClientRect() : {
width: fullLayout.width,
height: fullLayout.height
};
newWidth = Math.round(factor * (gdBB.width - reservedWidth));
newHeight = Math.round(factor * (gdBB.height - reservedHeight));
}
else {
// plotly.js - let the developers do what they want, either
// provide height and width for the container div,
// specify size in layout, or take the defaults,
// but don't enforce any ratio restrictions
var computedStyle = isPlotDiv ? window.getComputedStyle(gd) : {};
newWidth = parseFloat(computedStyle.width) || fullLayout.width;
newHeight = parseFloat(computedStyle.height) || fullLayout.height;
}
var minWidth = plots.layoutAttributes.width.min,
minHeight = plots.layoutAttributes.height.min;
if(newWidth < minWidth) newWidth = minWidth;
if(newHeight < minHeight) newHeight = minHeight;
var widthHasChanged = !layout.width &&
(Math.abs(fullLayout.width - newWidth) > 1),
heightHasChanged = !layout.height &&
(Math.abs(fullLayout.height - newHeight) > 1);
if(heightHasChanged || widthHasChanged) {
if(widthHasChanged) fullLayout.width = newWidth;
if(heightHasChanged) fullLayout.height = newHeight;
}
// cache initial autosize value, used in relayout when
// width or height values are set to null
if(!gd._initialAutoSize) {
gd._initialAutoSize = { width: newWidth, height: newHeight };
}
plots.sanitizeMargins(fullLayout);
}...
}
}
var oldWidth = fullLayout.width,
oldHeight = fullLayout.height;
// calculate autosizing
if(gd.layout.autosize) Plots.plotAutoSize(gd, gd.layout, fullLayout);
// avoid unnecessary redraws
var hasSizechanged = aobj.height || aobj.width ||
(fullLayout.width !== oldWidth) ||
(fullLayout.height !== oldHeight);
if(hasSizechanged) flags.docalc = true;
...previousPromises = function (gd) {
if((gd._promises || []).length) {
return Promise.all(gd._promises)
.then(function() { gd._promises = []; });
}
}...
if(trace.visible !== true || !trace._module.colorbar) {
Plots.autoMargin(gd, 'cb' + trace.uid);
}
else trace._module.colorbar(gd, cd);
}
Plots.doAutoMargin(gd);
return Plots.previousPromises(gd);
}
// in case the margins changed, draw margin pushers again
function marginPushersAgain() {
var seq = JSON.stringify(fullLayout._size) === oldmargins ?
[] :
[marginPushers, subroutines.layoutStyles];
...purge = function (gd) {
// note: we DO NOT remove _context because it doesn't change when we insert
// a new plot, and may have been set outside of our scope.
var fullLayout = gd._fullLayout || {};
if(fullLayout._glcontainer !== undefined) fullLayout._glcontainer.remove();
if(fullLayout._geocontainer !== undefined) fullLayout._geocontainer.remove();
// remove modebar
if(fullLayout._modeBar) fullLayout._modeBar.destroy();
if(gd._transitionData) {
// Ensure any dangling callbacks are simply dropped if the plot is purged.
// This is more or less only actually important for testing.
if(gd._transitionData._interruptCallbacks) {
gd._transitionData._interruptCallbacks.length = 0;
}
if(gd._transitionData._animationRaf) {
window.cancelAnimationFrame(gd._transitionData._animationRaf);
}
}
// data and layout
delete gd.data;
delete gd.layout;
delete gd._fullData;
delete gd._fullLayout;
delete gd.calcdata;
delete gd.framework;
delete gd.empty;
delete gd.fid;
delete gd.undoqueue; // action queue
delete gd.undonum;
delete gd.autoplay; // are we doing an action that doesn't go in undo queue?
delete gd.changed;
// these get recreated on Plotly.plot anyway, but just to be safe
// (and to have a record of them...)
delete gd._tester;
delete gd._testref;
delete gd._promises;
delete gd._redrawTimer;
delete gd.firstscatter;
delete gd.hmlumcount;
delete gd.hmpixcount;
delete gd.numboxes;
delete gd._hoverTimer;
delete gd._lastHoverTime;
delete gd._transitionData;
delete gd._transitioning;
delete gd._initialAutoSize;
// remove all event listeners
if(gd.removeAllListeners) gd.removeAllListeners();
}...
*/
Plotly.newPlot = function(gd, data, layout, config) {
gd = helpers.getGraphDiv(gd);
// remove gl contexts
Plots.cleanPlot([], {}, gd._fullData || {}, gd._fullLayout || {});
Plots.purge(gd);
return Plotly.plot(gd, data, layout, config);
};
/**
* Wrap negative indicies to their positive counterparts.
*
* @param {Number[]} indices An array of indices
...recomputeFrameHash = function (gd) {
var hash = gd._transitionData._frameHash = {};
var frames = gd._transitionData._frames;
for(var i = 0; i < frames.length; i++) {
var frame = frames[i];
if(frame && frame.name) {
hash[frame.name] = frame;
}
}
}n/a
redrawText = function (gd) {
// do not work if polar is present
if((gd.data && gd.data[0] && gd.data[0].r)) return;
return new Promise(function(resolve) {
setTimeout(function() {
Registry.getComponentMethod('annotations', 'draw')(gd);
Registry.getComponentMethod('legend', 'draw')(gd);
(gd.calcdata || []).forEach(function(d) {
if(d[0] && d[0].t && d[0].t.cb) d[0].t.cb();
});
resolve(plots.previousPromises(gd));
}, 300);
});
}n/a
register = function (_module, thisType, categoriesIn, meta) {
if(exports.modules[thisType]) {
Loggers.log('Type ' + thisType + ' already registered');
return;
}
var categoryObj = {};
for(var i = 0; i < categoriesIn.length; i++) {
categoryObj[categoriesIn[i]] = true;
exports.allCategories[categoriesIn[i]] = true;
}
exports.modules[thisType] = {
_module: _module,
categories: categoryObj
};
if(meta && Object.keys(meta).length) {
exports.modules[thisType].meta = meta;
}
exports.allTypes.push(thisType);
}...
If you would like to manually pick which plotly.js modules to include, you can create a *custom* bundle by using `plotly.js/lib/
core`, and loading only the trace types that you need (e.g. `pie` or `choropleth`). The recommended way to do this is by creating
a *bundling file*:
```javascript
// in custom-plotly.js
var Plotly = require('plotly.js/lib/core');
// Load in the trace types for pie, and choropleth
Plotly.register([
require('plotly.js/lib/pie'),
require('plotly.js/lib/choropleth')
]);
module.exports = Plotly;
```
...registerComponent = function (_module) {
var name = _module.name;
exports.componentsRegistry[name] = _module;
if(_module.layoutAttributes) {
if(_module.layoutAttributes._isLinkedToArray) {
pushUnique(exports.layoutArrayContainers, name);
}
findArrayRegexps(_module);
}
}...
}
function registerComponentModule(newModule) {
if(typeof newModule.name !== 'string') {
throw new Error('Component module *name* must be a string.');
}
Registry.registerComponent(newModule);
}
...registerSubplot = function (_module) {
var plotType = _module.name;
if(exports.subplotsRegistry[plotType]) {
Loggers.log('Plot type ' + plotType + ' already registered.');
return;
}
// relayout array handling will look for component module methods with this
// name and won't find them because this is a subplot module... but that
// should be fine, it will just fall back on redrawing the plot.
findArrayRegexps(_module);
// not sure what's best for the 'cartesian' type at this point
exports.subplotsRegistry[plotType] = _module;
}...
}
};
function registerTraceModule(newModule) {
Registry.register(newModule, newModule.name, newModule.categories, newModule.meta);
if(!Registry.subplotsRegistry[newModule.basePlotModule.name]) {
Registry.registerSubplot(newModule.basePlotModule);
}
}
function registerTransformModule(newModule) {
if(typeof newModule.name !== 'string') {
throw new Error('Transform module *name* must be a string.');
}
...rehover = function (gd) {
if(gd._fullLayout._rehover) {
gd._fullLayout._rehover();
}
}n/a
resize = function (gd) {
return new Promise(function(resolve, reject) {
if(!gd || d3.select(gd).style('display') === 'none') {
reject(new Error('Resize must be passed a plot div element.'));
}
if(gd._redrawTimer) clearTimeout(gd._redrawTimer);
gd._redrawTimer = setTimeout(function() {
// return if there is nothing to resize
if(gd.layout.width && gd.layout.height) {
resolve(gd);
return;
}
delete gd.layout.width;
delete gd.layout.height;
// autosizing doesn't count as a change that needs saving
var oldchanged = gd.changed;
// nor should it be included in the undo queue
gd.autoplay = true;
Plotly.relayout(gd, { autosize: true }).then(function() {
gd.changed = oldchanged;
resolve(gd);
});
}, 100);
});
}n/a
sanitizeMargins = function (fullLayout) {
// polar doesn't do margins...
if(!fullLayout || !fullLayout.margin) return;
var width = fullLayout.width,
height = fullLayout.height,
margin = fullLayout.margin,
plotWidth = width - (margin.l + margin.r),
plotHeight = height - (margin.t + margin.b),
correction;
// if margin.l + margin.r = 0 then plotWidth > 0
// as width >= 10 by supplyDefaults
// similarly for margin.t + margin.b
if(plotWidth < 0) {
correction = (width - 1) / (margin.l + margin.r);
margin.l = Math.floor(correction * margin.l);
margin.r = Math.floor(correction * margin.r);
}
if(plotHeight < 0) {
correction = (height - 1) / (margin.t + margin.b);
margin.t = Math.floor(correction * margin.t);
margin.b = Math.floor(correction * margin.b);
}
}...
var missingWidthOrHeight = (!newLayout.width || !newLayout.height),
autosize = newFullLayout.autosize,
autosizable = gd._context && gd._context.autosizable,
initialAutoSize = missingWidthOrHeight && (autosize || autosizable);
if(initialAutoSize) plots.plotAutoSize(gd, newLayout, newFullLayout);
else if(missingWidthOrHeight) plots.sanitizeMargins(gd);
// for backwards-compatibility with Plotly v1.x.x
if(!autosize && missingWidthOrHeight) {
newLayout.width = newFullLayout.width;
newLayout.height = newFullLayout.height;
}
}
...sendDataToCloud = function (gd) {
gd.emit('plotly_beforeexport');
var baseUrl = (window.PLOTLYENV && window.PLOTLYENV.BASE_URL) || 'https://plot.ly';
var hiddenformDiv = d3.select(gd)
.append('div')
.attr('id', 'hiddenform')
.style('display', 'none');
var hiddenform = hiddenformDiv
.append('form')
.attr({
action: baseUrl + '/external',
method: 'post',
target: '_blank'
});
var hiddenformInput = hiddenform
.append('input')
.attr({
type: 'text',
name: 'data'
});
hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata');
hiddenform.node().submit();
hiddenformDiv.remove();
gd.emit('plotly_afterexport');
return false;
}...
'class': 'link--impt link--embedview',
'font-weight': 'bold'
})
.text(gd._context.linkText + ' ' + String.fromCharCode(187));
if(gd._context.sendData) {
link.on('click', function() {
plots.sendDataToCloud(gd);
});
}
else {
var path = window.location.pathname.split('/');
var query = window.location.search;
link.attr({
'xlink:xlink:show': 'new',
...style = function (gd) {
var _modules = gd._fullLayout._modules;
for(var i = 0; i < _modules.length; i++) {
var _module = _modules[i];
if(_module.style) _module.style(gd);
}
}...
.classed('plotly-notifier', true);
var notes = notifierContainer.selectAll('.notifier-note').data(NOTEDATA);
function killNote(transition) {
transition
.duration(700)
.style('opacity', 0)
.each('end', function(thisText) {
var thisIndex = NOTEDATA.indexOf(thisText);
if(thisIndex !== -1) NOTEDATA.splice(thisIndex, 1);
d3.select(this).remove();
});
}
...supplyAnimationDefaults = function (opts) {
opts = opts || {};
var i;
var optsOut = {};
function coerce(attr, dflt) {
return Lib.coerce(opts || {}, optsOut, animationAttrs, attr, dflt);
}
coerce('mode');
coerce('direction');
coerce('fromcurrent');
if(Array.isArray(opts.frame)) {
optsOut.frame = [];
for(i = 0; i < opts.frame.length; i++) {
optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {});
}
} else {
optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {});
}
if(Array.isArray(opts.transition)) {
optsOut.transition = [];
for(i = 0; i < opts.transition.length; i++) {
optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition[i] || {});
}
} else {
optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {});
}
return optsOut;
}...
// This is the queue of frames that will be animated as soon as possible. They
// are popped immediately upon the *start* of a transition:
if(!trans._frameQueue) {
trans._frameQueue = [];
}
animationOpts = Plots.supplyAnimationDefaults(animationOpts);
var transitionOpts = animationOpts.transition;
var frameOpts = animationOpts.frame;
// Since frames are popped immediately, an empty queue only means all frames have
// *started* to transition, not that the animation is complete. To solve that,
// track a separate counter that increments at the same time as frames are added
// to the queue, but decrements only when the transition is complete.
...supplyAnimationFrameDefaults = function (opts) {
var optsOut = {};
function coerce(attr, dflt) {
return Lib.coerce(opts || {}, optsOut, animationAttrs.frame, attr, dflt);
}
coerce('duration');
coerce('redraw');
return optsOut;
}...
coerce('mode');
coerce('direction');
coerce('fromcurrent');
if(Array.isArray(opts.frame)) {
optsOut.frame = [];
for(i = 0; i < opts.frame.length; i++) {
optsOut.frame[i] = plots.supplyAnimationFrameDefaults(opts.frame[i] || {});
}
} else {
optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {});
}
if(Array.isArray(opts.transition)) {
optsOut.transition = [];
...supplyAnimationTransitionDefaults = function (opts) {
var optsOut = {};
function coerce(attr, dflt) {
return Lib.coerce(opts || {}, optsOut, animationAttrs.transition, attr, dflt);
}
coerce('duration');
coerce('easing');
return optsOut;
}...
} else {
optsOut.frame = plots.supplyAnimationFrameDefaults(opts.frame || {});
}
if(Array.isArray(opts.transition)) {
optsOut.transition = [];
for(i = 0; i < opts.transition.length; i++) {
optsOut.transition[i] = plots.supplyAnimationTransitionDefaults(opts.transition
[i] || {});
}
} else {
optsOut.transition = plots.supplyAnimationTransitionDefaults(opts.transition || {});
}
return optsOut;
};
...supplyDataDefaults = function (dataIn, dataOut, layout, fullLayout) {
var i, fullTrace, trace;
var modules = fullLayout._modules = [],
basePlotModules = fullLayout._basePlotModules = [],
cnt = 0;
fullLayout._transformModules = [];
function pushModule(fullTrace) {
dataOut.push(fullTrace);
var _module = fullTrace._module;
if(!_module) return;
Lib.pushUnique(modules, _module);
Lib.pushUnique(basePlotModules, fullTrace._module.basePlotModule);
cnt++;
}
var carpetIndex = {};
var carpetDependents = [];
for(i = 0; i < dataIn.length; i++) {
trace = dataIn[i];
fullTrace = plots.supplyTraceDefaults(trace, cnt, fullLayout, i);
fullTrace.index = i;
fullTrace._input = trace;
fullTrace._expandedIndex = cnt;
if(fullTrace.transforms && fullTrace.transforms.length) {
var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
for(var j = 0; j < expandedTraces.length; j++) {
var expandedTrace = expandedTraces[j],
fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout, i);
// mutate uid here using parent uid and expanded index
// to promote consistency between update calls
expandedTrace.uid = fullExpandedTrace.uid = fullTrace.uid + j;
// add info about parent data trace
fullExpandedTrace.index = i;
fullExpandedTrace._input = trace;
fullExpandedTrace._fullInput = fullTrace;
// add info about the expanded data
fullExpandedTrace._expandedIndex = cnt;
fullExpandedTrace._expandedInput = expandedTrace;
pushModule(fullExpandedTrace);
}
}
else {
// add identify refs for consistency with transformed traces
fullTrace._fullInput = fullTrace;
fullTrace._expandedInput = fullTrace;
pushModule(fullTrace);
}
if(Registry.traceIs(fullTrace, 'carpetAxis')) {
carpetIndex[fullTrace.carpet] = fullTrace;
}
if(Registry.traceIs(fullTrace, 'carpetDependent')) {
carpetDependents.push(i);
}
}
for(i = 0; i < carpetDependents.length; i++) {
fullTrace = dataOut[carpetDependents[i]];
if(!fullTrace.visible) continue;
var carpetAxis = carpetIndex[fullTrace.carpet];
fullTrace._carpet = carpetAxis;
if(!carpetAxis || !carpetAxis.visible) {
fullTrace.visible = false;
continue;
}
fullTrace.xaxis = carpetAxis.xaxis;
fullTrace.yaxis = carpetAxis.yaxis;
}
}...
newFullLayout._initialAutoSizeIsDone = true;
// keep track of how many traces are inputted
newFullLayout._dataLength = newData.length;
// then do the data
newFullLayout._globalTransforms = (gd._context || {}).globalTransforms;
plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout);
// attach helper method to check whether a plot type is present on graph
newFullLayout._has = plots._hasPlotType.bind(newFullLayout);
// special cases that introduce interactions between traces
var _modules = newFullLayout._modules;
for(i = 0; i < _modules.length; i++) {
...supplyDefaults = function (gd) {
var oldFullLayout = gd._fullLayout || {},
newFullLayout = gd._fullLayout = {},
newLayout = gd.layout || {};
var oldFullData = gd._fullData || [],
newFullData = gd._fullData = [],
newData = gd.data || [];
var i;
// Create all the storage space for frames, but only if doesn't already exist
if(!gd._transitionData) plots.createTransitionData(gd);
// first fill in what we can of layout without looking at data
// because fullData needs a few things from layout
if(oldFullLayout._initialAutoSizeIsDone) {
// coerce the updated layout while preserving width and height
var oldWidth = oldFullLayout.width,
oldHeight = oldFullLayout.height;
plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout);
if(!newLayout.width) newFullLayout.width = oldWidth;
if(!newLayout.height) newFullLayout.height = oldHeight;
}
else {
// coerce the updated layout and autosize if needed
plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout);
var missingWidthOrHeight = (!newLayout.width || !newLayout.height),
autosize = newFullLayout.autosize,
autosizable = gd._context && gd._context.autosizable,
initialAutoSize = missingWidthOrHeight && (autosize || autosizable);
if(initialAutoSize) plots.plotAutoSize(gd, newLayout, newFullLayout);
else if(missingWidthOrHeight) plots.sanitizeMargins(gd);
// for backwards-compatibility with Plotly v1.x.x
if(!autosize && missingWidthOrHeight) {
newLayout.width = newFullLayout.width;
newLayout.height = newFullLayout.height;
}
}
newFullLayout._initialAutoSizeIsDone = true;
// keep track of how many traces are inputted
newFullLayout._dataLength = newData.length;
// then do the data
newFullLayout._globalTransforms = (gd._context || {}).globalTransforms;
plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout);
// attach helper method to check whether a plot type is present on graph
newFullLayout._has = plots._hasPlotType.bind(newFullLayout);
// special cases that introduce interactions between traces
var _modules = newFullLayout._modules;
for(i = 0; i < _modules.length; i++) {
var _module = _modules[i];
if(_module.cleanData) _module.cleanData(newFullData);
}
if(oldFullData.length === newData.length) {
for(i = 0; i < newFullData.length; i++) {
relinkPrivateKeys(newFullData[i], oldFullData[i]);
}
}
// finally, fill in the pieces of layout that may need to look at data
plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData);
// TODO remove in v2.0.0
// add has-plot-type refs to fullLayout for backward compatibility
newFullLayout._hasCartesian = newFullLayout._has('cartesian');
newFullLayout._hasGeo = newFullLayout._has('geo');
newFullLayout._hasGL3D = newFullLayout._has('gl3d');
newFullLayout._hasGL2D = newFullLayout._has('gl2d');
newFullLayout._hasTernary = newFullLayout._has('ternary');
newFullLayout._hasPie = newFullLayout._has('pie');
// clean subplots and other artifacts from previous plot calls
plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);
// relink / initialize subplot axis objects
plots.linkSubplots(newFullData, newFullLayout, oldFullData, oldFullLayout);
// relink functions and _ attributes to promote consistency between plots
relinkPrivateKeys(newFullLayout, oldFullLayout);
// TODO may return a promise
plots.doAutoMargin(gd);
// set scale after auto margin routine
var axList = Plotly.Axes.list(gd);
for(i = 0; i < axList.length; i++) {
var ax = axList[i];
ax.setScale();
}
// update object references in calcdata
if((gd.calcdata || []).length === newFullData.length) {
for(i = 0; i < newFullData.length; i++) {
var trace = newFu ......
gd._replotPending = true;
return Promise.reject();
} else {
// we're going ahead with a replot now
gd._replotPending = false;
}
Plots.supplyDefaults(gd);
var fullLayout = gd._fullLayout;
// Polar plots
if(data && data[0] && data[0].r) return plotPolar(gd, data, layout);
// so we don't try to re-call Plotly.plot from inside
...supplyFrameDefaults = function (frameIn) {
var frameOut = {};
function coerce(attr, dflt) {
return Lib.coerce(frameIn, frameOut, frameAttrs, attr, dflt);
}
coerce('group');
coerce('name');
coerce('traces');
coerce('baseframe');
coerce('data');
coerce('layout');
return frameOut;
}...
Lib.warn('addFrames: This API call has yielded too many warnings. ' +
'For the rest of this call, further warnings about numeric frame ' +
'names will be suppressed.');
}
}
insertions.push({
frame: Plots.supplyFrameDefaults(frameList[i]),
index: (indices && indices[i] !== undefined && indices[i] !== null) ? indices[i] : bigIndex + i
});
}
// Sort this, taking note that undefined insertions end up at the end:
insertions.sort(function(a, b) {
if(a.index > b.index) return -1;
...supplyLayoutGlobalDefaults = function (layoutIn, layoutOut) {
function coerce(attr, dflt) {
return Lib.coerce(layoutIn, layoutOut, plots.layoutAttributes, attr, dflt);
}
var globalFont = Lib.coerceFont(coerce, 'font');
coerce('title');
Lib.coerceFont(coerce, 'titlefont', {
family: globalFont.family,
size: Math.round(globalFont.size * 1.4),
color: globalFont.color
});
// Make sure that autosize is defaulted to *true*
// on layouts with no set width and height for backward compatibly,
// in particular https://plot.ly/javascript/responsive-fluid-layout/
//
// Before https://github.com/plotly/plotly.js/pull/635 ,
// layouts with no set width and height were set temporary set to 'initial'
// to pass through the autosize routine
//
// This behavior is subject to change in v2.
coerce('autosize', !(layoutIn.width && layoutIn.height));
coerce('width');
coerce('height');
coerce('margin.l');
coerce('margin.r');
coerce('margin.t');
coerce('margin.b');
coerce('margin.pad');
coerce('margin.autoexpand');
if(layoutIn.width && layoutIn.height) plots.sanitizeMargins(layoutOut);
coerce('paper_bgcolor');
coerce('separators');
coerce('hidesources');
coerce('smith');
var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults');
handleCalendarDefaults(layoutIn, layoutOut, 'calendar');
}...
if(oldFullLayout._initialAutoSizeIsDone) {
// coerce the updated layout while preserving width and height
var oldWidth = oldFullLayout.width,
oldHeight = oldFullLayout.height;
plots.supplyLayoutGlobalDefaults(newLayout, newFullLayout);
if(!newLayout.width) newFullLayout.width = oldWidth;
if(!newLayout.height) newFullLayout.height = oldHeight;
}
else {
// coerce the updated layout and autosize if needed
...supplyLayoutModuleDefaults = function (layoutIn, layoutOut, fullData, transitionData) {
var i, _module;
// can't be be part of basePlotModules loop
// in order to handle the orphan axes case
Plotly.Axes.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
// base plot module layout defaults
var basePlotModules = layoutOut._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
_module = basePlotModules[i];
// done above already
if(_module.name === 'cartesian') continue;
// e.g. gl2d does not have a layout-defaults step
if(_module.supplyLayoutDefaults) {
_module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
}
}
// trace module layout defaults
var modules = layoutOut._modules;
for(i = 0; i < modules.length; i++) {
_module = modules[i];
if(_module.supplyLayoutDefaults) {
_module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
}
}
// transform module layout defaults
var transformModules = layoutOut._transformModules;
for(i = 0; i < transformModules.length; i++) {
_module = transformModules[i];
if(_module.supplyLayoutDefaults) {
_module.supplyLayoutDefaults(layoutIn, layoutOut, fullData, transitionData);
}
}
// should FX be a component?
Plotly.Fx.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
var components = Object.keys(Registry.componentsRegistry);
for(i = 0; i < components.length; i++) {
_module = Registry.componentsRegistry[components[i]];
if(_module.supplyLayoutDefaults) {
_module.supplyLayoutDefaults(layoutIn, layoutOut, fullData);
}
}
}...
if(oldFullData.length === newData.length) {
for(i = 0; i < newFullData.length; i++) {
relinkPrivateKeys(newFullData[i], oldFullData[i]);
}
}
// finally, fill in the pieces of layout that may need to look at data
plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData, gd._transitionData
);
// TODO remove in v2.0.0
// add has-plot-type refs to fullLayout for backward compatibility
newFullLayout._hasCartesian = newFullLayout._has('cartesian');
newFullLayout._hasGeo = newFullLayout._has('geo');
newFullLayout._hasGL3D = newFullLayout._has('gl3d');
newFullLayout._hasGL2D = newFullLayout._has('gl2d');
...supplyTraceDefaults = function (traceIn, traceOutIndex, layout, traceInIndex) {
var traceOut = {},
defaultColor = Color.defaults[traceOutIndex % Color.defaults.length];
function coerce(attr, dflt) {
return Lib.coerce(traceIn, traceOut, plots.attributes, attr, dflt);
}
function coerceSubplotAttr(subplotType, subplotAttr) {
if(!plots.traceIs(traceOut, subplotType)) return;
return Lib.coerce(traceIn, traceOut,
plots.subplotsRegistry[subplotType].attributes, subplotAttr);
}
var visible = coerce('visible');
coerce('type');
coerce('uid');
coerce('name', 'trace ' + traceInIndex);
// coerce subplot attributes of all registered subplot types
var subplotTypes = Object.keys(subplotsRegistry);
for(var i = 0; i < subplotTypes.length; i++) {
var subplotType = subplotTypes[i];
// done below (only when visible is true)
// TODO unified this pattern
if(['cartesian', 'gl2d'].indexOf(subplotType) !== -1) continue;
var attr = subplotsRegistry[subplotType].attr;
if(attr) coerceSubplotAttr(subplotType, attr);
}
if(visible) {
var _module = plots.getModule(traceOut);
traceOut._module = _module;
// gets overwritten in pie, geo and ternary modules
coerce('hoverinfo', (layout._dataLength === 1) ? 'x+y+z+text' : undefined);
if(plots.traceIs(traceOut, 'showLegend')) {
coerce('showlegend');
coerce('legendgroup');
}
// TODO add per-base-plot-module trace defaults step
if(_module) _module.supplyDefaults(traceIn, traceOut, defaultColor, layout);
if(!plots.traceIs(traceOut, 'noOpacity')) coerce('opacity');
coerceSubplotAttr('cartesian', 'xaxis');
coerceSubplotAttr('cartesian', 'yaxis');
coerceSubplotAttr('gl2d', 'xaxis');
coerceSubplotAttr('gl2d', 'yaxis');
if(plots.traceIs(traceOut, 'notLegendIsolatable')) {
// This clears out the legendonly state for traces like carpet that
// cannot be isolated in the legend
traceOut.visible = !!traceOut.visible;
}
plots.supplyTransformDefaults(traceIn, traceOut, layout);
}
return traceOut;
}...
}
var carpetIndex = {};
var carpetDependents = [];
for(i = 0; i < dataIn.length; i++) {
trace = dataIn[i];
fullTrace = plots.supplyTraceDefaults(trace, cnt, fullLayout, i);
fullTrace.index = i;
fullTrace._input = trace;
fullTrace._expandedIndex = cnt;
if(fullTrace.transforms && fullTrace.transforms.length) {
var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
...supplyTransformDefaults = function (traceIn, traceOut, layout) {
var globalTransforms = layout._globalTransforms || [];
var transformModules = layout._transformModules || [];
if(!Array.isArray(traceIn.transforms) && globalTransforms.length === 0) return;
var containerIn = traceIn.transforms || [],
transformList = globalTransforms.concat(containerIn),
containerOut = traceOut.transforms = [];
for(var i = 0; i < transformList.length; i++) {
var transformIn = transformList[i],
type = transformIn.type,
_module = transformsRegistry[type],
transformOut;
if(!_module) Lib.warn('Unrecognized transform type ' + type + '.');
if(_module && _module.supplyDefaults) {
transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn);
transformOut.type = type;
transformOut._module = _module;
Lib.pushUnique(transformModules, _module);
}
else {
transformOut = Lib.extendFlat({}, transformIn);
}
containerOut.push(transformOut);
}
}...
if(plots.traceIs(traceOut, 'notLegendIsolatable')) {
// This clears out the legendonly state for traces like carpet that
// cannot be isolated in the legend
traceOut.visible = !!traceOut.visible;
}
plots.supplyTransformDefaults(traceIn, traceOut, layout);
}
return traceOut;
};
plots.supplyTransformDefaults = function(traceIn, traceOut, layout) {
var globalTransforms = layout._globalTransforms || [];
...traceIs = function (traceType, category) {
traceType = getTraceType(traceType);
// old plot.ly workspace hack, nothing to see here
if(traceType === 'various') return false;
var _module = exports.modules[traceType];
if(!_module) {
if(traceType && traceType !== 'area') {
Loggers.log('Unrecognized trace type ' + traceType + '.');
}
_module = exports.modules[basePlotAttributes.type.dflt];
}
return !!_module.categories[category];
}...
delete trace.xbins;
}
// error_y.opacity is obsolete - merge into color
if(trace.error_y && 'opacity' in trace.error_y) {
var dc = Color.defaults,
yeColor = trace.error_y.color ||
(Registry.traceIs(trace, 'bar') ? Color.defaultLine : dc[tracei % dc
.length]);
trace.error_y.color = Color.addOpacity(
Color.rgb(yeColor),
Color.opacity(yeColor) * trace.error_y.opacity);
delete trace.error_y.opacity;
}
// convert bardir to orientation, and put the data into
...transition = function (gd, data, layout, traces, frameOpts, transitionOpts) {
var i, traceIdx;
var dataLength = Array.isArray(data) ? data.length : 0;
var traceIndices = traces.slice(0, dataLength);
var transitionedTraces = [];
function prepareTransitions() {
var i;
for(i = 0; i < traceIndices.length; i++) {
var traceIdx = traceIndices[i];
var trace = gd._fullData[traceIdx];
var module = trace._module;
// There's nothing to do if this module is not defined:
if(!module) continue;
// Don't register the trace as transitioned if it doens't know what to do.
// If it *is* registered, it will receive a callback that it's responsible
// for calling in order to register the transition as having completed.
if(module.animatable) {
transitionedTraces.push(traceIdx);
}
gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]);
}
// Follow the same procedure. Clone it so we don't mangle the input, then
// expand any object paths so we can merge deep into gd.layout:
var layoutUpdate = Lib.expandObjectPaths(Lib.extendDeepNoArrays({}, layout));
// Before merging though, we need to modify the incoming layout. We only
// know how to *transition* layout ranges, so it's imperative that a new
// range not be sent to the layout before the transition has started. So
// we must remove the things we can transition:
var axisAttrRe = /^[xy]axis[0-9]*$/;
for(var attr in layoutUpdate) {
if(!axisAttrRe.test(attr)) continue;
delete layoutUpdate[attr].range;
}
plots.extendLayout(gd.layout, layoutUpdate);
// Supply defaults after applying the incoming properties. Note that any attempt
// to simplify this step and reduce the amount of work resulted in the reconstruction
// of essentially the whole supplyDefaults step, so that it seems sensible to just use
// supplyDefaults even though it's heavier than would otherwise be desired for
// transitions:
plots.supplyDefaults(gd);
plots.doCalcdata(gd);
ErrorBars.calc(gd);
return Promise.resolve();
}
function executeCallbacks(list) {
var p = Promise.resolve();
if(!list) return p;
while(list.length) {
p = p.then((list.shift()));
}
return p;
}
function flushCallbacks(list) {
if(!list) return;
while(list.length) {
list.shift();
}
}
var aborted = false;
function executeTransitions() {
gd.emit('plotly_transitioning', []);
return new Promise(function(resolve) {
// This flag is used to disabled things like autorange:
gd._transitioning = true;
// When instantaneous updates are coming through quickly, it's too much to simply disable
// all interaction, so store this flag so we can disambiguate whether mouse interactions
// should be fully disabled or not:
if(transitionOpts.duration > 0) {
gd._transitioningWithDuration = true;
}
// If another transition is triggered, this callback will be executed simply because it's
// in the interruptCallbacks queue. If this transition completes, it will instead flush
// that queue and forget about this callback.
gd._transitionData._interruptCallbacks.push(function() {
aborted = true;
});
if(frameOpts.redraw) {
gd._transitionData._interruptCallbacks.push(function() {
return Plotly.redraw(gd);
});
}
// Emit this and make sure it happens last:
gd._transitionData._interruptCallbacks.push(function() {
gd.emit('plotly_transitioninterrupted', []);
}); ......
.each(function(thisText) {
var note = d3.select(this);
note.append('button')
.classed('notifier-close', true)
.html('×')
.on('click', function() {
note.transition().call(killNote);
});
var p = note.append('p');
var lines = thisText.split(/<br\s*\/?>/g);
for(var i = 0; i < lines.length; i++) {
if(i) p.append('br');
p.append('span').text(lines[i]);
...function filter(pts, tolerance) {
var ptsFiltered = [pts[0]],
doneRawIndex = 0,
doneFilteredIndex = 0;
function addPt(pt) {
pts.push(pt);
var prevFilterLen = ptsFiltered.length,
iLast = doneRawIndex;
ptsFiltered.splice(doneFilteredIndex + 1);
for(var i = iLast + 1; i < pts.length; i++) {
if(i === pts.length - 1 || isBent(pts, iLast, i + 1, tolerance)) {
ptsFiltered.push(pts[i]);
if(ptsFiltered.length < prevFilterLen - 2) {
doneRawIndex = i;
doneFilteredIndex = ptsFiltered.length - 1;
}
iLast = i;
}
}
}
if(pts.length > 1) {
var lastPt = pts.pop();
addPt(lastPt);
}
return {
addPt: addPt,
raw: pts,
filtered: ptsFiltered
};
}...
// Make a few changes to the data right away
// before it gets used for anything
exports.cleanData = function(data, existingData) {
// Enforce unique IDs
var suids = [], // seen uids --- so we can weed out incoming repeats
uids = data.concat(Array.isArray(existingData) ? existingData : [])
.filter(function(trace) { return 'uid' in trace; })
.map(function(trace) { return trace.uid; });
for(var tracei = 0; tracei < data.length; tracei++) {
var trace = data[tracei];
var i;
// assign uids to each trace and detect collisions.
...function isBent(pts, start, end, tolerance) {
var startPt = pts[start],
segment = [pts[end][0] - startPt[0], pts[end][1] - startPt[1]],
segmentSquared = dot(segment, segment),
segmentLen = Math.sqrt(segmentSquared),
unitPerp = [-segment[1] / segmentLen, segment[0] / segmentLen],
i,
part,
partParallel;
for(i = start + 1; i < end; i++) {
part = [pts[i][0] - startPt[0], pts[i][1] - startPt[1]];
partParallel = dot(part, segment);
if(partParallel < 0 || partParallel > segmentSquared ||
Math.abs(dot(part, unitPerp)) > tolerance) return true;
}
return false;
}n/a
function tester(ptsIn) {
var pts = ptsIn.slice(),
xmin = pts[0][0],
xmax = xmin,
ymin = pts[0][1],
ymax = ymin;
pts.push(pts[0]);
for(var i = 1; i < pts.length; i++) {
xmin = Math.min(xmin, pts[i][0]);
xmax = Math.max(xmax, pts[i][0]);
ymin = Math.min(ymin, pts[i][1]);
ymax = Math.max(ymax, pts[i][1]);
}
// do we have a rectangle? Handle this here, so we can use the same
// tester for the rectangular case without sacrificing speed
var isRect = false,
rectFirstEdgeTest;
if(pts.length === 5) {
if(pts[0][0] === pts[1][0]) { // vert, horz, vert, horz
if(pts[2][0] === pts[3][0] &&
pts[0][1] === pts[3][1] &&
pts[1][1] === pts[2][1]) {
isRect = true;
rectFirstEdgeTest = function(pt) { return pt[0] === pts[0][0]; };
}
}
else if(pts[0][1] === pts[1][1]) { // horz, vert, horz, vert
if(pts[2][1] === pts[3][1] &&
pts[0][0] === pts[3][0] &&
pts[1][0] === pts[2][0]) {
isRect = true;
rectFirstEdgeTest = function(pt) { return pt[1] === pts[0][1]; };
}
}
}
function rectContains(pt, omitFirstEdge) {
var x = pt[0],
y = pt[1];
if(x < xmin || x > xmax || y < ymin || y > ymax) {
// pt is outside the bounding box of polygon
return false;
}
if(omitFirstEdge && rectFirstEdgeTest(pt)) return false;
return true;
}
function contains(pt, omitFirstEdge) {
var x = pt[0],
y = pt[1];
if(x < xmin || x > xmax || y < ymin || y > ymax) {
// pt is outside the bounding box of polygon
return false;
}
var imax = pts.length,
x1 = pts[0][0],
y1 = pts[0][1],
crossings = 0,
i,
x0,
y0,
xmini,
ycross;
for(i = 1; i < imax; i++) {
// find all crossings of a vertical line upward from pt with
// polygon segments
// crossings exactly at xmax don't count, unless the point is
// exactly on the segment, then it counts as inside.
x0 = x1;
y0 = y1;
x1 = pts[i][0];
y1 = pts[i][1];
xmini = Math.min(x0, x1);
// outside the bounding box of this segment, it's only a crossing
// if it's below the box.
if(x < xmini || x > Math.max(x0, x1) || y > Math.max(y0, y1)) {
continue;
}
else if(y < Math.min(y0, y1)) {
// don't count the left-most point of the segment as a crossing
// because we don't want to double-count adjacent crossings
// UNLESS the polygon turns past vertical at exactly this x
// Note that this is repeated below, but we can't factor it out
// because
if(x !== xmini) crossings++;
}
// inside the bounding box, check the actual line intercept
else {
// vertical segment - we know already that the point is exactly
// on the segment, so mark the crossing as exactly at the point.
if(x1 === x0) ycross = y;
// any other angle
else ycross = y0 + (x - x0) * (y1 - y0) / (x1 - x0);
// exactly on the edge: counts as inside the polygon, unless it's the
// first edge and we're omitting it.
if(y === ycross) {
if(i === 1 && omitFirstEdge) return false;
return true;
}
if(y <= ycross && x !== xmini) crossings++;
}
}
// if we've gotten this far, odd crossings means inside, even is outside
return crossings % 2 === 1;
}
return {
xmin: xmin, ...n/a
add = function (gd, undoFunc, undoArgs, redoFunc, redoArgs) {
var queueObj,
queueIndex;
// make sure we have the queue and our position in it
gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
queueIndex = gd.undoQueue.index;
// if we're already playing an undo or redo, or if this is an auto operation
// (like pane resize... any others?) then we don't save this to the undo queue
if(gd.autoplay) {
if(!gd.undoQueue.inSequence) gd.autoplay = false;
return;
}
// if we're not in a sequence or are just starting, we need a new queue item
if(!gd.undoQueue.sequence || gd.undoQueue.beginSequence) {
queueObj = {undo: {calls: [], args: []}, redo: {calls: [], args: []}};
gd.undoQueue.queue.splice(queueIndex, gd.undoQueue.queue.length - queueIndex, queueObj);
gd.undoQueue.index += 1;
} else {
queueObj = gd.undoQueue.queue[queueIndex - 1];
}
gd.undoQueue.beginSequence = false;
// we unshift to handle calls for undo in a forward for loop later
if(queueObj) {
queueObj.undo.calls.unshift(undoFunc);
queueObj.undo.args.unshift(undoArgs);
queueObj.redo.calls.push(redoFunc);
queueObj.redo.args.push(redoArgs);
}
if(gd.undoQueue.queue.length > config.queueLength) {
gd.undoQueue.queue.shift();
gd.undoQueue.index--;
}
}...
if(calendar) {
try {
var dateJD = Math.round(ms / ONEDAY) + EPOCHJD,
calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar),
cDate = calInstance.fromJD(dateJD);
if(dMonth % 12) calInstance.add(cDate, dMonth, 'm');
else calInstance.add(cDate, dMonth / 12, 'y');
return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs;
}
catch(e) {
logError('invalid ms ' + ms + ' in calendar ' + calendar);
// then keep going in gregorian even though the result will be 'Invalid'
...plotDo = function (gd, func, args) {
gd.autoplay = true;
// this *won't* copy gd and it preserves `undefined` properties!
args = copyArgArray(gd, args);
// call the supplied function
func.apply(null, args);
}...
// get the queueObj for instructions on how to undo
queueObj = gd.undoQueue.queue[gd.undoQueue.index];
// this sequence keeps things from adding to the queue during undo/redo
gd.undoQueue.inSequence = true;
for(i = 0; i < queueObj.undo.calls.length; i++) {
queue.plotDo(gd, queueObj.undo.calls[i], queueObj.undo.args[i]);
}
gd.undoQueue.inSequence = false;
gd.autoplay = false;
};
/**
* Redo the current object in the undo, then move forward in the queue.
...function redo(gd) {
var queueObj, i;
if(gd.framework && gd.framework.isPolar) {
gd.framework.redo();
return;
}
if(gd.undoQueue === undefined ||
isNaN(gd.undoQueue.index) ||
gd.undoQueue.index >= gd.undoQueue.queue.length) {
return;
}
// get the queueObj for instructions on how to undo
queueObj = gd.undoQueue.queue[gd.undoQueue.index];
// this sequence keeps things from adding to the queue during undo/redo
gd.undoQueue.inSequence = true;
for(i = 0; i < queueObj.redo.calls.length; i++) {
queue.plotDo(gd, queueObj.redo.calls[i], queueObj.redo.args[i]);
}
gd.undoQueue.inSequence = false;
gd.autoplay = false;
// index is pointing to the thing we just redid, move it
gd.undoQueue.index++;
}...
*
* @param gd
*/
queue.redo = function redo(gd) {
var queueObj, i;
if(gd.framework && gd.framework.isPolar) {
gd.framework.redo();
return;
}
if(gd.undoQueue === undefined ||
isNaN(gd.undoQueue.index) ||
gd.undoQueue.index >= gd.undoQueue.queue.length) {
return;
}
...startSequence = function (gd) {
gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
gd.undoQueue.sequence = true;
gd.undoQueue.beginSequence = true;
}...
// something went wrong, reset gd to be safe and rethrow error
gd.data.splice(gd.data.length - traces.length, traces.length);
throw error;
}
// if we're here, the user has defined specific places to place the new traces
// this requires some extra work that moveTraces will do
Queue.startSequence(gd);
Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
promise = Plotly.moveTraces(gd, currentIndices, newIndices);
Queue.stopSequence(gd);
return promise;
};
/**
...stopSequence = function (gd) {
gd.undoQueue = gd.undoQueue || {index: 0, queue: [], sequence: false};
gd.undoQueue.sequence = false;
gd.undoQueue.beginSequence = false;
}...
}
// if we're here, the user has defined specific places to place the new traces
// this requires some extra work that moveTraces will do
Queue.startSequence(gd);
Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs);
promise = Plotly.moveTraces(gd, currentIndices, newIndices);
Queue.stopSequence(gd);
return promise;
};
/**
* Delete traces at `indices` from gd.data array.
*
* @param {Object|HTMLDivElement} gd The graph div
...function undo(gd) {
var queueObj, i;
if(gd.framework && gd.framework.isPolar) {
gd.framework.undo();
return;
}
if(gd.undoQueue === undefined ||
isNaN(gd.undoQueue.index) ||
gd.undoQueue.index <= 0) {
return;
}
// index is pointing to next *forward* queueObj, point to the one we're undoing
gd.undoQueue.index--;
// get the queueObj for instructions on how to undo
queueObj = gd.undoQueue.queue[gd.undoQueue.index];
// this sequence keeps things from adding to the queue during undo/redo
gd.undoQueue.inSequence = true;
for(i = 0; i < queueObj.undo.calls.length; i++) {
queue.plotDo(gd, queueObj.undo.calls[i], queueObj.undo.args[i]);
}
gd.undoQueue.inSequence = false;
gd.autoplay = false;
}...
*
* @param gd
*/
queue.undo = function undo(gd) {
var queueObj, i;
if(gd.framework && gd.framework.isPolar) {
gd.framework.undo();
return;
}
if(gd.undoQueue === undefined ||
isNaN(gd.undoQueue.index) ||
gd.undoQueue.index <= 0) {
return;
}
...getComponentMethod = function (name, method) {
var _module = exports.componentsRegistry[name];
if(!_module) return noop;
return _module[method] || noop;
}...
* dateTick0: get the canonical tick for this calendar
*
* bool sunday is for week ticks, shift it to a Sunday.
*/
exports.dateTick0 = function(calendar, sunday) {
if(isWorldCalendar(calendar)) {
return sunday ?
Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY
')[calendar] :
Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar];
}
else {
return sunday ? '2000-01-02' : '2000-01-01';
}
};
...getModule = function (trace) {
if(trace.r !== undefined) {
Loggers.warn('Tried to put a polar trace ' +
'on an incompatible graph of cartesian ' +
'data. Ignoring this dataset.', trace
);
return false;
}
var _module = exports.modules[getTraceType(trace)];
if(!_module) return false;
return _module._module;
}...
var attr = subplotsRegistry[subplotType].attr;
if(attr) coerceSubplotAttr(subplotType, attr);
}
if(visible) {
var _module = plots.getModule(traceOut);
traceOut._module = _module;
// gets overwritten in pie, geo and ternary modules
coerce('hoverinfo', (layout._dataLength === 1) ? 'x+y+z+text' : undefined);
if(plots.traceIs(traceOut, 'showLegend')) {
coerce('showlegend');
...register = function (_module, thisType, categoriesIn, meta) {
if(exports.modules[thisType]) {
Loggers.log('Type ' + thisType + ' already registered');
return;
}
var categoryObj = {};
for(var i = 0; i < categoriesIn.length; i++) {
categoryObj[categoriesIn[i]] = true;
exports.allCategories[categoriesIn[i]] = true;
}
exports.modules[thisType] = {
_module: _module,
categories: categoryObj
};
if(meta && Object.keys(meta).length) {
exports.modules[thisType].meta = meta;
}
exports.allTypes.push(thisType);
}...
If you would like to manually pick which plotly.js modules to include, you can create a *custom* bundle by using `plotly.js/lib/
core`, and loading only the trace types that you need (e.g. `pie` or `choropleth`). The recommended way to do this is by creating
a *bundling file*:
```javascript
// in custom-plotly.js
var Plotly = require('plotly.js/lib/core');
// Load in the trace types for pie, and choropleth
Plotly.register([
require('plotly.js/lib/pie'),
require('plotly.js/lib/choropleth')
]);
module.exports = Plotly;
```
...registerComponent = function (_module) {
var name = _module.name;
exports.componentsRegistry[name] = _module;
if(_module.layoutAttributes) {
if(_module.layoutAttributes._isLinkedToArray) {
pushUnique(exports.layoutArrayContainers, name);
}
findArrayRegexps(_module);
}
}...
}
function registerComponentModule(newModule) {
if(typeof newModule.name !== 'string') {
throw new Error('Component module *name* must be a string.');
}
Registry.registerComponent(newModule);
}
...registerSubplot = function (_module) {
var plotType = _module.name;
if(exports.subplotsRegistry[plotType]) {
Loggers.log('Plot type ' + plotType + ' already registered.');
return;
}
// relayout array handling will look for component module methods with this
// name and won't find them because this is a subplot module... but that
// should be fine, it will just fall back on redrawing the plot.
findArrayRegexps(_module);
// not sure what's best for the 'cartesian' type at this point
exports.subplotsRegistry[plotType] = _module;
}...
}
};
function registerTraceModule(newModule) {
Registry.register(newModule, newModule.name, newModule.categories, newModule.meta);
if(!Registry.subplotsRegistry[newModule.basePlotModule.name]) {
Registry.registerSubplot(newModule.basePlotModule);
}
}
function registerTransformModule(newModule) {
if(typeof newModule.name !== 'string') {
throw new Error('Transform module *name* must be a string.');
}
...traceIs = function (traceType, category) {
traceType = getTraceType(traceType);
// old plot.ly workspace hack, nothing to see here
if(traceType === 'various') return false;
var _module = exports.modules[traceType];
if(!_module) {
if(traceType && traceType !== 'area') {
Loggers.log('Unrecognized trace type ' + traceType + '.');
}
_module = exports.modules[basePlotAttributes.type.dflt];
}
return !!_module.categories[category];
}...
delete trace.xbins;
}
// error_y.opacity is obsolete - merge into color
if(trace.error_y && 'opacity' in trace.error_y) {
var dc = Color.defaults,
yeColor = trace.error_y.color ||
(Registry.traceIs(trace, 'bar') ? Color.defaultLine : dc[tracei % dc
.length]);
trace.error_y.color = Color.addOpacity(
Color.rgb(yeColor),
Color.opacity(yeColor) * trace.error_y.opacity);
delete trace.error_y.opacity;
}
// convert bardir to orientation, and put the data into
...function ScrollBox(gd, container, id) {
this.gd = gd;
this.container = container;
this.id = id;
// See ScrollBox.prototype.enable for further definition
this.position = null; // scrollbox position
this.translateX = null; // scrollbox horizontal translation
this.translateY = null; // scrollbox vertical translation
this.hbar = null; // horizontal scrollbar D3 selection
this.vbar = null; // vertical scrollbar D3 selection
// <rect> element to capture pointer events
this.bg = this.container.selectAll('rect.scrollbox-bg').data([0]);
this.bg.exit()
.on('.drag', null)
.on('wheel', null)
.remove();
this.bg.enter().append('rect')
.classed('scrollbox-bg', true)
.style('pointer-events', 'all')
.attr({
opacity: 0,
x: 0,
y: 0,
width: 0,
height: 0
});
}n/a
function onBarDrag() {
var translateX = this.translateX,
translateY = this.translateY;
if(this.hbar) {
var xMin = translateX + this._hbarXMin,
xMax = xMin + this._hbarTranslateMax,
x = Lib.constrain(d3.event.x, xMin, xMax),
xf = (x - xMin) / (xMax - xMin);
var translateXMax = this.position.w - this._box.w;
translateX = xf * translateXMax;
}
if(this.vbar) {
var yMin = translateY + this._vbarYMin,
yMax = yMin + this._vbarTranslateMax,
y = Lib.constrain(d3.event.y, yMin, yMax),
yf = (y - yMin) / (yMax - yMin);
var translateYMax = this.position.h - this._box.h;
translateY = yf * translateYMax;
}
this.setTranslate(translateX, translateY);
}n/a
function onBarDrag() {
var translateX = this.translateX,
translateY = this.translateY;
if(this.hbar) {
translateX -= d3.event.dx;
}
if(this.vbar) {
translateY -= d3.event.dy;
}
this.setTranslate(translateX, translateY);
}n/a
function onBarWheel() {
var translateX = this.translateX,
translateY = this.translateY;
if(this.hbar) {
translateX += d3.event.deltaY;
}
if(this.vbar) {
translateY += d3.event.deltaY;
}
this.setTranslate(translateX, translateY);
}n/a
function disable() {
if(this.hbar || this.vbar) {
this.bg.attr({
width: 0,
height: 0
});
this.container
.on('wheel', null)
.on('.drag', null)
.call(Drawing.setClipUrl, null);
delete this._clipRect;
}
if(this.hbar) {
this.hbar.on('.drag', null);
this.hbar.remove();
delete this.hbar;
delete this._hbarXMin;
delete this._hbarTranslateMax;
}
if(this.vbar) {
this.vbar.on('.drag', null);
this.vbar.remove();
delete this.vbar;
delete this._vbarYMin;
delete this._vbarTranslateMax;
}
}...
var update = bindingValueHasChanged(gd, binding, ret.cache);
if(update.changed && onchange) {
// Disable checks for the duration of this command in order to avoid
// infinite loops:
if(ret.lookupTable[update.value] !== undefined) {
ret.disable();
Promise.resolve(onchange({
value: update.value,
type: binding.type,
prop: binding.prop,
traces: binding.traces,
index: ret.lookupTable[update.value]
})).then(ret.enable, ret.enable);
...function enable(position, translateX, translateY) {
var fullLayout = this.gd._fullLayout,
fullWidth = fullLayout.width,
fullHeight = fullLayout.height;
// compute position of scrollbox
this.position = position;
var l = this.position.l,
w = this.position.w,
t = this.position.t,
h = this.position.h,
direction = this.position.direction,
isDown = (direction === 'down'),
isLeft = (direction === 'left'),
isRight = (direction === 'right'),
isUp = (direction === 'up'),
boxW = w,
boxH = h,
boxL, boxR,
boxT, boxB;
if(!isDown && !isLeft && !isRight && !isUp) {
this.position.direction = 'down';
isDown = true;
}
var isVertical = isDown || isUp;
if(isVertical) {
boxL = l;
boxR = boxL + boxW;
if(isDown) {
// anchor to top side
boxT = t;
boxB = Math.min(boxT + boxH, fullHeight);
boxH = boxB - boxT;
}
else {
// anchor to bottom side
boxB = t + boxH;
boxT = Math.max(boxB - boxH, 0);
boxH = boxB - boxT;
}
}
else {
boxT = t;
boxB = boxT + boxH;
if(isLeft) {
// anchor to right side
boxR = l + boxW;
boxL = Math.max(boxR - boxW, 0);
boxW = boxR - boxL;
}
else {
// anchor to left side
boxL = l;
boxR = Math.min(boxL + boxW, fullWidth);
boxW = boxR - boxL;
}
}
this._box = {
l: boxL,
t: boxT,
w: boxW,
h: boxH
};
// compute position of horizontal scroll bar
var needsHorizontalScrollBar = (w > boxW),
hbarW = ScrollBox.barLength + 2 * ScrollBox.barPad,
hbarH = ScrollBox.barWidth + 2 * ScrollBox.barPad,
// draw horizontal scrollbar on the bottom side
hbarL = l,
hbarT = t + h;
if(hbarT + hbarH > fullHeight) hbarT = fullHeight - hbarH;
var hbar = this.container.selectAll('rect.scrollbar-horizontal').data(
(needsHorizontalScrollBar) ? [0] : []);
hbar.exit()
.on('.drag', null)
.remove();
hbar.enter().append('rect')
.classed('scrollbar-horizontal', true)
.call(Color.fill, ScrollBox.barColor);
if(needsHorizontalScrollBar) {
this.hbar = hbar.attr({
'rx': ScrollBox.barRadius,
'ry': ScrollBox.barRadius,
'x': hbarL,
'y': hbarT,
'width': hbarW,
'height': hbarH
});
// hbar center moves between hbarXMin and hbarXMin + hbarTranslateMax
this._hbarXMin = hbarL + hbarW / 2;
this._hbarTranslateMax = boxW - hbarW;
}
else {
delete this.hbar;
delete this._hbarXMin;
delete this._hbarTranslateMax;
}
// compute position of vertical scroll bar
var needsVerticalScrollBar = (h > boxH),
vbarW = ScrollBox.barWidth + 2 * ScrollBox.barPad,
vbarH = ScrollBox.barLength + 2 * ScrollBox.barPad,
// draw vertical scrollbar on the right side
vbarL = l + w,
vbarT = t;
if(vbarL + vbarW > fullWidth) vbarL = fullWidth - vbarW;
var vbar = this.container.selectAll('rect.scrollbar-vertical').data(
(needsVerticalScrollBar) ? [0] : []);
vbar.exit()
.on('.drag', null)
.remove();
vbar.enter().append('rect')
.classed('scrollbar-vertical', true)
.call(Color.fill, ScrollBox.barColor);
if(needsVerticalScrollBar) {
this.vbar = vbar.attr({
'rx': ScrollBox.barRadius,
'ry': ScrollBox.barRadius,
'x': vbarL,
'y': vbarT,
'width': vbarW,
'height': vbarH
});
// vbar center moves between vbarYMin and vbarYMin + vbarTranslateMax
this._vbarYMin = vbarT + vbarH / 2;
this._vbarTranslateMax = boxH - vbarH;
}
else {
d ......
height: 1,
data: dummyPixel
});
}
function clear(regl, x, y, width, height) {
var gl = regl._gl;
gl.enable(gl.SCISSOR_TEST);
gl.scissor(x, y, width, height);
regl.clear({color: [0, 0, 0, 0], depth: 1}); // clearing is done in scissored panel only
}
function renderBlock(regl, glAes, renderState, blockLineCount, sampleCount, item) {
var rafKey = item.key;
...function setTranslate(translateX, translateY) {
// store translateX and translateY (needed by mouse event handlers)
var translateXMax = this.position.w - this._box.w,
translateYMax = this.position.h - this._box.h;
translateX = Lib.constrain(translateX || 0, 0, translateXMax);
translateY = Lib.constrain(translateY || 0, 0, translateYMax);
this.translateX = translateX;
this.translateY = translateY;
this.container.call(Drawing.setTranslate,
this._box.l - this.position.l - translateX,
this._box.t - this.position.t - translateY);
if(this._clipRect) {
this._clipRect.attr({
x: Math.floor(this.position.l + translateX - 0.5),
y: Math.floor(this.position.t + translateY - 0.5)
});
}
if(this.hbar) {
var xf = translateX / translateXMax;
this.hbar.call(Drawing.setTranslate,
translateX + xf * this._hbarTranslateMax,
translateY);
}
if(this.vbar) {
var yf = translateY / translateYMax;
this.vbar.call(Drawing.setTranslate,
translateX,
translateY + yf * this._vbarTranslateMax);
}
}...
this.vbar
.on('.drag', null)
.call(onBarDrag);
}
}
// set scrollbox translation
this.setTranslate(translateX, translateY);
};
/**
* If present, remove clip-path and scrollbars
*
* @method
*/
...distinctVals = function (valsIn) {
var vals = valsIn.slice(); // otherwise we sort the original array...
vals.sort(exports.sorterAsc);
var l = vals.length - 1,
minDiff = (vals[l] - vals[0]) || 1,
errDiff = minDiff / (l || 1) / 10000,
v2 = [vals[0]];
for(var i = 0; i < l; i++) {
// make sure values aren't just off by a rounding error
if(vals[i + 1] > vals[i] + errDiff) {
minDiff = Math.min(minDiff, vals[i + 1] - vals[i]);
v2.push(vals[i + 1]);
}
}
return {vals: v2, minDiff: minDiff};
}...
var size0;
if(nbins) size0 = ((dataMax - dataMin) / nbins);
else {
// totally auto: scale off std deviation so the highest bin is
// somewhat taller than the total number of bins, but don't let
// the size get smaller than the 'nice' rounded down minimum
// difference between values
var distinctData = Lib.distinctVals(data),
msexp = Math.pow(10, Math.floor(
Math.log(distinctData.minDiff) / Math.LN10)),
minSize = msexp * Lib.roundUp(
distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
size0 = Math.max(minSize, 2 * Lib.stdev(data) /
Math.pow(data.length, is2d ? 0.25 : 0.4));
...findBin = function (val, bins, linelow) {
if(isNumeric(bins.start)) {
return linelow ?
Math.ceil((val - bins.start) / bins.size) - 1 :
Math.floor((val - bins.start) / bins.size);
}
else {
var n1 = 0,
n2 = bins.length,
c = 0,
n,
test;
if(bins[bins.length - 1] >= bins[0]) {
test = linelow ? lessThan : lessOrEqual;
} else {
test = linelow ? greaterOrEqual : greaterThan;
}
// c is just to avoid infinite loops if there's an error
while(n1 < n2 && c++ < 100) {
n = Math.floor((n1 + n2) / 2);
if(test(bins[n], val)) n1 = n + 1;
else n2 = n;
}
if(c > 90) loggers.log('Long binary search...');
return n1 - 1;
}
}...
var i1, i2, text;
if(hasColumnText) text = Lib.init2dArray(col2vals.length, col1vals.length);
for(i = 0; i < colLen; i++) {
if(col1[i] !== BADNUM && col2[i] !== BADNUM) {
i1 = Lib.findBin(col1[i] + col1dv.minDiff / 2, col1vals);
i2 = Lib.findBin(col2[i] + col2dv.minDiff / 2, col2vals);
for(j = 0; j < arrayVarNames.length; j++) {
arrayVarName = arrayVarNames[j];
arrayVar = trace[arrayVarName];
newArray = newArrays[j];
newArray[i2][i1] = arrayVar[i];
...roundUp = function (val, arrayIn, reverse) {
var low = 0,
high = arrayIn.length - 1,
mid,
c = 0,
dlow = reverse ? 0 : 1,
dhigh = reverse ? 1 : 0,
rounded = reverse ? Math.ceil : Math.floor;
// c is just to avoid infinite loops if there's an error
while(low < high && c++ < 100) {
mid = rounded((low + high) / 2);
if(arrayIn[mid] <= val) low = mid + dlow;
else high = mid - dhigh;
}
return arrayIn[low];
}...
// totally auto: scale off std deviation so the highest bin is
// somewhat taller than the total number of bins, but don't let
// the size get smaller than the 'nice' rounded down minimum
// difference between values
var distinctData = Lib.distinctVals(data),
msexp = Math.pow(10, Math.floor(
Math.log(distinctData.minDiff) / Math.LN10)),
minSize = msexp * Lib.roundUp(
distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
size0 = Math.max(minSize, 2 * Lib.stdev(data) /
Math.pow(data.length, is2d ? 0.25 : 0.4));
// fallback if ax.d2c output BADNUMs
// e.g. when user try to plot categorical bins
// on a layout.xaxis.type: 'linear'
...sorterAsc = function (a, b) { return a - b; }n/a
sorterDes = function (a, b) { return b - a; }n/a
function Sieve(traces, separateNegativeValues, dontMergeOverlappingData) {
this.traces = traces;
this.separateNegativeValues = separateNegativeValues;
this.dontMergeOverlappingData = dontMergeOverlappingData;
var positions = [];
for(var i = 0; i < traces.length; i++) {
var trace = traces[i];
for(var j = 0; j < trace.length; j++) {
var bar = trace[j];
if(bar.p !== BADNUM) positions.push(bar.p);
}
}
this.positions = positions;
var dv = Lib.distinctVals(this.positions);
this.distinctPositions = dv.vals;
this.minDiff = dv.minDiff;
this.binWidth = this.minDiff;
this.bins = {};
}n/a
function put(position, value) {
var label = this.getLabel(position, value);
return this.bins[label] || 0;
}...
* - min, max: (number, integer only) inclusive bounds on allowed vals
* either or both may be omitted
* - dflt: if attribute is invalid or missing, use this default
* if dflt is provided as an argument to lib.coerce it takes precedence
* as a convenience, returns the value it finally set
*/
exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt) {
var opts = nestedProperty(attributes, attribute).get(),
propIn = nestedProperty(containerIn, attribute),
propOut = nestedProperty(containerOut, attribute),
v = propIn.get();
if(dflt === undefined) dflt = opts.dflt;
/**
...function getLabel(position, value) {
var prefix = (value < 0 && this.separateNegativeValues) ? 'v' : '^',
label = (this.dontMergeOverlappingData) ?
position :
Math.round(position / this.binWidth);
return prefix + label;
}...
*
* @method
* @param {number} position
* @param {number} value
* @returns {number} Previous bin value
*/
Sieve.prototype.put = function put(position, value) {
var label = this.getLabel(position, value),
oldValue = this.bins[label] || 0;
this.bins[label] = oldValue + value;
return oldValue;
};
...function put(position, value) {
var label = this.getLabel(position, value),
oldValue = this.bins[label] || 0;
this.bins[label] = oldValue + value;
return oldValue;
}...
for(j = 0; j < trace.length; j++) {
bar = trace[j];
if(bar.s === BADNUM) continue;
// stack current bar and get previous sum
var barBase = sieve.put(bar.p, bar.b + bar.s),
barTop = barBase + bar.b + bar.s;
// store the bar base and top in each calcdata item
bar.b = barBase;
bar[sLetter] = barTop;
if(!barnorm) {
...aggNums = function (f, v, a, len) {
var i,
b;
if(!len) len = a.length;
if(!isNumeric(v)) v = false;
if(Array.isArray(a[0])) {
b = new Array(len);
for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
a = b;
}
for(i = 0; i < len; i++) {
if(!isNumeric(v)) v = a[i];
else if(isNumeric(a[i])) v = f(+v, +a[i]);
}
return v;
}...
exports.aggNums = function(f, v, a, len) {
var i,
b;
if(!len) len = a.length;
if(!isNumeric(v)) v = false;
if(Array.isArray(a[0])) {
b = new Array(len);
for(i = 0; i < len; i++) b[i] = exports.aggNums(f, v, a[i]);
a = b;
}
for(i = 0; i < len; i++) {
if(!isNumeric(v)) v = a[i];
else if(isNumeric(a[i])) v = f(+v, +a[i]);
}
...interp = function (arr, n) {
if(!isNumeric(n)) throw 'n should be a finite number';
n = n * arr.length - 0.5;
if(n < 0) return arr[0];
if(n > arr.length - 1) return arr[arr.length - 1];
var frac = n % 1;
return frac * arr[Math.ceil(n)] + (1 - frac) * arr[Math.floor(n)];
}n/a
len = function (data) {
return exports.aggNums(function(a) { return a + 1; }, 0, data);
}...
* even need to use aggNums instead of .length, to toss out non-numerics
*/
exports.len = function(data) {
return exports.aggNums(function(a) { return a + 1; }, 0, data);
};
exports.mean = function(data, len) {
if(!len) len = exports.len(data);
return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
};
exports.variance = function(data, len, mean) {
if(!len) len = exports.len(data);
if(!isNumeric(mean)) mean = exports.mean(data, len);
...mean = function (data, len) {
if(!len) len = exports.len(data);
return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
}...
exports.mean = function(data, len) {
if(!len) len = exports.len(data);
return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len;
};
exports.variance = function(data, len, mean) {
if(!len) len = exports.len(data);
if(!isNumeric(mean)) mean = exports.mean(data, len);
return exports.aggNums(function(a, b) {
return a + Math.pow(b - mean, 2);
}, 0, data) / len;
};
exports.stdev = function(data, len, mean) {
...stdev = function (data, len, mean) {
return Math.sqrt(exports.variance(data, len, mean));
}...
// the size get smaller than the 'nice' rounded down minimum
// difference between values
var distinctData = Lib.distinctVals(data),
msexp = Math.pow(10, Math.floor(
Math.log(distinctData.minDiff) / Math.LN10)),
minSize = msexp * Lib.roundUp(
distinctData.minDiff / msexp, [0.9, 1.9, 4.9, 9.9], true);
size0 = Math.max(minSize, 2 * Lib.stdev(data) /
Math.pow(data.length, is2d ? 0.25 : 0.4));
// fallback if ax.d2c output BADNUMs
// e.g. when user try to plot categorical bins
// on a layout.xaxis.type: 'linear'
if(!isNumeric(size0)) size0 = 1;
}
...variance = function (data, len, mean) {
if(!len) len = exports.len(data);
if(!isNumeric(mean)) mean = exports.mean(data, len);
return exports.aggNums(function(a, b) {
return a + Math.pow(b - mean, 2);
}, 0, data) / len;
}...
return exports.aggNums(function(a, b) {
return a + Math.pow(b - mean, 2);
}, 0, data) / len;
};
exports.stdev = function(data, len, mean) {
return Math.sqrt(exports.variance(data, len, mean));
};
/**
* interp() computes a percentile (quantile) for a given distribution.
* We interpolate the distribution (to compute quantiles, we follow method #10 here:
* http://www.amstat.org/publications/jse/v14n3/langford.html).
* Typically the index or rank (n * arr.length) may be non-integer.
...doCamera = function (gd) {
var fullLayout = gd._fullLayout,
sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d');
for(var i = 0; i < sceneIds.length; i++) {
var sceneLayout = fullLayout[sceneIds[i]],
scene = sceneLayout._scene;
scene.setCamera(sceneLayout.camera);
}
}n/a
doColorBars = function (gd) {
for(var i = 0; i < gd.calcdata.length; i++) {
var cdi0 = gd.calcdata[i][0];
if((cdi0.t || {}).cb) {
var trace = cdi0.trace,
cb = cdi0.t.cb;
if(Registry.traceIs(trace, 'contour')) {
cb.line({
width: trace.contours.showlines !== false ?
trace.line.width : 0,
dash: trace.line.dash,
color: trace.contours.coloring === 'line' ?
cb._opts.line.color : trace.line.color
});
}
if(Registry.traceIs(trace, 'markerColorscale')) {
cb.options(trace.marker.colorbar)();
}
else cb.options(trace.colorbar)();
}
}
return Plots.previousPromises(gd);
}n/a
doLegend = function (gd) {
Registry.getComponentMethod('legend', 'draw')(gd);
return Plots.previousPromises(gd);
}n/a
doModeBar = function (gd) {
var fullLayout = gd._fullLayout;
var subplotIds, i;
ModeBar.manage(gd);
Plotly.Fx.init(gd);
subplotIds = Plots.getSubplotIds(fullLayout, 'gl3d');
for(i = 0; i < subplotIds.length; i++) {
var scene = fullLayout[subplotIds[i]]._scene;
scene.updateFx(fullLayout.dragmode, fullLayout.hovermode);
}
// no need to do this for gl2d subplots,
// Plots.linkSubplots takes care of it all.
return Plots.previousPromises(gd);
}n/a
doTicksRelayout = function (gd) {
Plotly.Axes.doTicks(gd, 'redraw');
exports.drawMainTitle(gd);
return Plots.previousPromises(gd);
}n/a
doTraceStyle = function (gd) {
for(var i = 0; i < gd.calcdata.length; i++) {
var cdi = gd.calcdata[i],
_module = ((cdi[0] || {}).trace || {})._module || {},
arraysToCalcdata = _module.arraysToCalcdata;
if(arraysToCalcdata) arraysToCalcdata(cdi, cdi[0].trace);
}
Plots.style(gd);
Registry.getComponentMethod('legend', 'draw')(gd);
return Plots.previousPromises(gd);
}n/a
drawMainTitle = function (gd) {
var fullLayout = gd._fullLayout;
Titles.draw(gd, 'gtitle', {
propContainer: fullLayout,
propName: 'title',
dfltName: 'Plot',
attributes: {
x: fullLayout.width / 2,
y: fullLayout._size.t / 2,
'text-anchor': 'middle'
}
});
}...
// mark free axes as displayed, so we don't draw them again
if(showfreex) { freefinished.push(xa._id); }
if(showfreey) { freefinished.push(ya._id); }
});
Plotly.Axes.makeClipPaths(gd);
exports.drawMainTitle(gd);
ModeBar.manage(gd);
return gd._promises.length && Promise.all(gd._promises);
};
exports.drawMainTitle = function(gd) {
var fullLayout = gd._fullLayout;
...layoutReplot = function (gd) {
var layout = gd.layout;
gd.layout = undefined;
return Plotly.plot(gd, '', layout);
}n/a
layoutStyles = function (gd) {
return Lib.syncOrAsync([Plots.doAutoMargin, exports.lsInner], gd);
}n/a
lsInner = function (gd) {
var fullLayout = gd._fullLayout,
gs = fullLayout._size,
axList = Plotly.Axes.list(gd),
i;
// clear axis line positions, to be set in the subplot loop below
for(i = 0; i < axList.length; i++) axList[i]._linepositions = {};
fullLayout._paperdiv
.style({
width: fullLayout.width + 'px',
height: fullLayout.height + 'px'
})
.selectAll('.main-svg')
.call(Drawing.setSize, fullLayout.width, fullLayout.height);
gd._context.setBackground(gd, fullLayout.paper_bgcolor);
var subplotSelection = fullLayout._paper.selectAll('g.subplot');
// figure out which backgrounds we need to draw, and in which layers
// to put them
var lowerBackgroundIDs = [];
var lowerDomains = [];
subplotSelection.each(function(subplot) {
var plotinfo = fullLayout._plots[subplot];
if(plotinfo.mainplot) {
// mainplot is a reference to the main plot this one is overlaid on
// so if it exists, this is an overlaid plot and we don't need to
// give it its own background
if(plotinfo.bg) {
plotinfo.bg.remove();
}
plotinfo.bg = undefined;
return;
}
var xa = Plotly.Axes.getFromId(gd, subplot, 'x'),
ya = Plotly.Axes.getFromId(gd, subplot, 'y'),
xDomain = xa.domain,
yDomain = ya.domain,
plotgroupBgData = [];
if(overlappingDomain(xDomain, yDomain, lowerDomains)) {
plotgroupBgData = [0];
}
else {
lowerBackgroundIDs.push(subplot);
lowerDomains.push([xDomain, yDomain]);
}
// create the plot group backgrounds now, since
// they're all independent selections
var plotgroupBg = plotinfo.plotgroup.selectAll('.bg')
.data(plotgroupBgData);
plotgroupBg.enter().append('rect')
.classed('bg', true);
plotgroupBg.exit().remove();
plotgroupBg.each(function() {
plotinfo.bg = plotgroupBg;
var pgNode = plotinfo.plotgroup.node();
pgNode.insertBefore(this, pgNode.childNodes[0]);
});
});
// now create all the lower-layer backgrounds at once now that
// we have the list of subplots that need them
var lowerBackgrounds = fullLayout._bgLayer.selectAll('.bg')
.data(lowerBackgroundIDs);
lowerBackgrounds.enter().append('rect')
.classed('bg', true);
lowerBackgrounds.exit().remove();
lowerBackgrounds.each(function(subplot) {
fullLayout._plots[subplot].bg = d3.select(this);
});
var freefinished = [];
subplotSelection.each(function(subplot) {
var plotinfo = fullLayout._plots[subplot],
xa = Plotly.Axes.getFromId(gd, subplot, 'x'),
ya = Plotly.Axes.getFromId(gd, subplot, 'y');
// reset scale in case the margins have changed
xa.setScale();
ya.setScale();
if(plotinfo.bg) {
plotinfo.bg
.call(Drawing.setRect,
xa._offset - gs.p, ya._offset - gs.p,
xa._length + 2 * gs.p, ya._length + 2 * gs.p)
.call(Color.fill, fullLayout.plot_bgcolor)
.style('stroke-width', 0);
}
// Clip so that data only shows up on the plot area.
plotinfo.clipId = 'clip' + fullLayout._uid + subplot + 'plot';
var plotClip = fullLayout._defs.selectAll('g.clips')
.selectAll('#' + plotinfo.clipId)
.data([0]);
plotClip.enter().append('clipPath')
.attr({
'class': 'plotclip',
'id': plotinfo.clipId
})
.append('rect');
plotClip.selectAll('rect')
.attr({
'width': xa._length,
'height': ya._length
});
plotinfo.plot.call(Drawing.setTranslate, xa._offset, ya._offset);
plotinfo.plot.call(Drawing.setClipUrl, ...n/a
hasLines = function (trace) {
return trace.visible && trace.mode &&
trace.mode.indexOf('lines') !== -1;
}...
var hasColorscale = require('../../components/colorscale/has_colorscale');
var calcColorscale = require('../../components/colorscale/calc');
var subTypes = require('./subtypes');
module.exports = function calcMarkerColorscale(trace) {
if(subTypes.hasLines(trace) && hasColorscale(trace, 'line')) {
calcColorscale(trace, trace.line.color, 'line', 'c');
}
if(subTypes.hasMarkers(trace)) {
if(hasColorscale(trace, 'marker')) {
calcColorscale(trace, trace.marker.color, 'marker', 'c');
}
...hasMarkers = function (trace) {
return trace.visible && trace.mode &&
trace.mode.indexOf('markers') !== -1;
}...
var keyFunc;
if(trace.ids) {
keyFunc = function(d) {return d.id;};
}
var sparse = (
subTypes.hasMarkers(trace) &&
trace.marker.maxdisplayed > 0
);
if(!yObj.visible && !xObj.visible) return;
var errorbars = d3.select(this).selectAll('g.errorbar')
.data(d, keyFunc);
...hasText = function (trace) {
return trace.visible && trace.mode &&
trace.mode.indexOf('text') !== -1;
}...
if(selectable) break;
var trace = fullData[i];
if(!trace._module || !trace._module.selectPoints) continue;
if(trace.type === 'scatter' || trace.type === 'scatterternary') {
if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) {
selectable = true;
}
}
// assume that in general if the trace module has selectPoints,
// then it's selectable. Scatter is an exception to this because it must
// have markers or text, not just be a scatter type.
else selectable = true;
...isBubble = function (trace) {
return Lib.isPlainObject(trace.marker) &&
Array.isArray(trace.marker.size);
}...
var hasColorscale = require('../../components/colorscale/has_colorscale');
var colorscaleDefaults = require('../../components/colorscale/defaults');
var subTypes = require('./subtypes');
module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce, opts) {
var isBubble = subTypes.isBubble(traceIn),
lineColor = (traceIn.line || {}).color,
defaultMLC;
// marker.color inherit from line.color (even if line.color is an array)
if(lineColor) defaultColor = lineColor;
coerce('marker.symbol');
...convertToTspans = function (_context, _callback) {
var str = _context.text();
var converted = convertToSVG(str);
var that = _context;
// Until we get tex integrated more fully (so it can be used along with non-tex)
// allow some elements to prohibit it by attaching 'data-notex' to the original
var tex = (!that.attr('data-notex')) && converted.match(/([^$]*)([$]+[^$]*[$]+)([^$]*)/);
var result = str;
var parent = d3.select(that.node().parentNode);
if(parent.empty()) return;
var svgClass = (that.attr('class')) ? that.attr('class').split(' ')[0] : 'text';
svgClass += '-math';
parent.selectAll('svg.' + svgClass).remove();
parent.selectAll('g.' + svgClass + '-group').remove();
_context.style({visibility: null});
for(var up = _context.node(); up && up.removeAttribute; up = up.parentNode) {
up.removeAttribute('data-bb');
}
function showText() {
if(!parent.empty()) {
svgClass = that.attr('class') + '-math';
parent.select('svg.' + svgClass).remove();
}
_context.text('')
.style({
visibility: 'inherit',
'white-space': 'pre'
});
result = _context.appendSVG(converted);
if(!result) _context.text(str);
if(_context.select('a').size()) {
// at least in Chrome, pointer-events does not seem
// to be honored in children of <text> elements
// so if we have an anchor, we have to make the
// whole element respond
_context.style('pointer-events', 'all');
}
if(_callback) _callback.call(that);
}
if(tex) {
var gd = Lib.getPlotDiv(that.node());
((gd && gd._promises) || []).push(new Promise(function(resolve) {
that.style({visibility: 'hidden'});
var config = {fontSize: parseInt(that.style('font-size'), 10)};
texToSVG(tex[2], config, function(_svgEl, _glyphDefs, _svgBBox) {
parent.selectAll('svg.' + svgClass).remove();
parent.selectAll('g.' + svgClass + '-group').remove();
var newSvg = _svgEl && _svgEl.select('svg');
if(!newSvg || !newSvg.node()) {
showText();
resolve();
return;
}
var mathjaxGroup = parent.append('g')
.classed(svgClass + '-group', true)
.attr({'pointer-events': 'none'});
mathjaxGroup.node().appendChild(newSvg.node());
// stitch the glyph defs
if(_glyphDefs && _glyphDefs.node()) {
newSvg.node().insertBefore(_glyphDefs.node().cloneNode(true),
newSvg.node().firstChild);
}
newSvg.attr({
'class': svgClass,
height: _svgBBox.height,
preserveAspectRatio: 'xMinYMin meet'
})
.style({overflow: 'visible', 'pointer-events': 'none'});
var fill = that.style('fill') || 'black';
newSvg.select('g').attr({fill: fill, stroke: fill});
var newSvgW = getSize(newSvg, 'width'),
newSvgH = getSize(newSvg, 'height'),
newX = +that.attr('x') - newSvgW *
{start: 0, middle: 0.5, end: 1}[that.attr('text-anchor') || 'start'],
// font baseline is about 1/4 fontSize below centerline
textHeight = parseInt(that.style('font-size'), 10) ||
getSize(that, 'height'),
dy = -textHeight / 4;
if(svgClass[0] === 'y') {
mathjaxGroup.attr({
transform: 'rotate(' + [-90, +that.attr('x'), +that.attr('y')] +
') translate(' + [-newSvgW / 2, dy - newSvgH / 2] + ')'
});
newSvg.attr({x: +that.attr('x'), y: +that.attr(' ......
.attr({
'text-anchor': {
left: 'start',
right: 'end'
}[options.align] || 'middle'
});
svgTextUtils.convertToTspans(s, drawGraphicalElements);
return s;
}
function drawGraphicalElements() {
// make sure lines are aligned the way they will be
// at the end, even if their position changes
...html_entity_decode = function (s) {
var hiddenDiv = d3.select('body').append('div').style({display: 'none'}).html('');
var replaced = s.replace(/(&[^;]*;)/gi, function(d) {
if(d === '<') { return '<'; } // special handling for brackets
if(d === '&rt;') { return '>'; }
if(d.indexOf('<') !== -1 || d.indexOf('>') !== -1) { return ''; }
return hiddenDiv.html(d).text(); // everything else, let the browser decode it to unicode
});
hiddenDiv.remove();
return replaced;
}...
// fix for IE namespacing quirk?
// http://stackoverflow.com/questions/19610089/unwanted-namespaces-on-svg-markup-when-using-xmlserializer-in-javascript-with
-ie
svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns', xmlnsNamespaces.svg);
svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns:xlink', xmlnsNamespaces.xlink);
var s = new window.XMLSerializer().serializeToString(svg.node());
s = svgTextUtils.html_entity_decode(s);
s = svgTextUtils.xml_entity_encode(s);
// Fix quotations around font strings
s = s.replace(/("TOBESTRIPPED)|(TOBESTRIPPED")/g, '\'');
return s;
};
...makeEditable = function (context, _delegate, options) {
if(!options) options = {};
var that = this;
var dispatch = d3.dispatch('edit', 'input', 'cancel');
var textSelection = d3.select(this.node())
.style({'pointer-events': 'all'});
var handlerElement = _delegate || textSelection;
if(_delegate) textSelection.style({'pointer-events': 'none'});
function handleClick() {
appendEditable();
that.style({opacity: 0});
// also hide any mathjax svg
var svgClass = handlerElement.attr('class'),
mathjaxClass;
if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
else mathjaxClass = '[class*=-math-group]';
if(mathjaxClass) {
d3.select(that.node().parentNode).select(mathjaxClass).style({opacity: 0});
}
}
function selectElementContents(_el) {
var el = _el.node();
var range = document.createRange();
range.selectNodeContents(el);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
el.focus();
}
function appendEditable() {
var gd = Lib.getPlotDiv(that.node()),
plotDiv = d3.select(gd),
container = plotDiv.select('.svg-container'),
div = container.append('div');
div.classed('plugin-editable editable', true)
.style({
position: 'absolute',
'font-family': that.style('font-family') || 'Arial',
'font-size': that.style('font-size') || 12,
color: options.fill || that.style('fill') || 'black',
opacity: 1,
'background-color': options.background || 'transparent',
outline: '#ffffff33 1px solid',
margin: [-parseFloat(that.style('font-size')) / 8 + 1, 0, 0, -1].join('px ') + 'px',
padding: '0',
'box-sizing': 'border-box'
})
.attr({contenteditable: true})
.text(options.text || that.attr('data-unformatted'))
.call(alignHTMLWith(that, container, options))
.on('blur', function() {
gd._editing = false;
that.text(this.textContent)
.style({opacity: 1});
var svgClass = d3.select(this).attr('class'),
mathjaxClass;
if(svgClass) mathjaxClass = '.' + svgClass.split(' ')[0] + '-math-group';
else mathjaxClass = '[class*=-math-group]';
if(mathjaxClass) {
d3.select(that.node().parentNode).select(mathjaxClass).style({opacity: 0});
}
var text = this.textContent;
d3.select(this).transition().duration(0).remove();
d3.select(document).on('mouseup', null);
dispatch.edit.call(that, text);
})
.on('focus', function() {
var context = this;
gd._editing = true;
d3.select(document).on('mouseup', function() {
if(d3.event.target === context) return false;
if(document.activeElement === div.node()) div.node().blur();
});
})
.on('keyup', function() {
if(d3.event.which === 27) {
gd._editing = false;
that.style({opacity: 1});
d3.select(this)
.style({opacity: 0})
.on('blur', function() { return false; })
.transition().remove();
dispatch.cancel.call(that, this.textContent);
}
else {
dispatch.input.call(that, this.textContent);
d3.select(this).call(alignHTMLWith(that, container, options));
}
})
.on('keydown', function() {
if(d3.event.which === 13) this.blur();
})
.call(selectElementContents); ...n/a
plainText = function (_str) {
// strip out our pseudo-html so we have a readable
// version to put into text fields
return (_str || '').replace(STRIP_TAGS, ' ');
}...
contrastColor = d.borderColor || Color.contrast(traceColor);
// to get custom 'name' labels pass cleanPoint
if(d.nameOverride !== undefined) d.name = d.nameOverride;
if(d.name && d.zLabelVal === undefined) {
// strip out our pseudo-html elements from d.name (if it exists at all)
name = svgTextUtils.plainText(d.name || '');
if(name.length > 15) name = name.substr(0, 12) + '...';
}
// used by other modules (initially just ternary) that
// manage their own hoverinfo independent of cleanPoint
// the rest of this will still apply, so such modules
...xml_entity_encode = function (str) {
return str.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g, '&');
}...
style = encodeForHTML(extraStyle) + (style ? ';' + style : '');
}
return tspanStart + (style ? ' style="' + style + '"' : '') + '>
;';
}
}
else {
return exports.xml_entity_encode(d).replace(/</g, '<'
;);
}
});
var indices = [];
for(var index = result.indexOf('<br>'); index > 0; index = result.indexOf('<br>', index
+ 1)) {
indices.push(index);
}
...function Ternary(options, fullLayout) {
this.id = options.id;
this.graphDiv = options.graphDiv;
this.init(fullLayout);
this.makeFramework();
}n/a
adjustLayout = function (ternaryLayout, graphSize) {
var _this = this,
domain = ternaryLayout.domain,
xDomainCenter = (domain.x[0] + domain.x[1]) / 2,
yDomainCenter = (domain.y[0] + domain.y[1]) / 2,
xDomain = domain.x[1] - domain.x[0],
yDomain = domain.y[1] - domain.y[0],
wmax = xDomain * graphSize.w,
hmax = yDomain * graphSize.h,
sum = ternaryLayout.sum,
amin = ternaryLayout.aaxis.min,
bmin = ternaryLayout.baxis.min,
cmin = ternaryLayout.caxis.min;
var x0, y0, w, h, xDomainFinal, yDomainFinal;
if(wmax > w_over_h * hmax) {
h = hmax;
w = h * w_over_h;
}
else {
w = wmax;
h = w / w_over_h;
}
xDomainFinal = xDomain * w / wmax;
yDomainFinal = yDomain * h / hmax;
x0 = graphSize.l + graphSize.w * xDomainCenter - w / 2;
y0 = graphSize.t + graphSize.h * (1 - yDomainCenter) - h / 2;
_this.x0 = x0;
_this.y0 = y0;
_this.w = w;
_this.h = h;
_this.sum = sum;
// set up the x and y axis objects we'll use to lay out the points
_this.xaxis = {
type: 'linear',
range: [amin + 2 * cmin - sum, sum - amin - 2 * bmin],
domain: [
xDomainCenter - xDomainFinal / 2,
xDomainCenter + xDomainFinal / 2
],
_id: 'x'
};
setConvert(_this.xaxis, _this.graphDiv._fullLayout);
_this.xaxis.setScale();
_this.yaxis = {
type: 'linear',
range: [amin, sum - bmin - cmin],
domain: [
yDomainCenter - yDomainFinal / 2,
yDomainCenter + yDomainFinal / 2
],
_id: 'y'
};
setConvert(_this.yaxis, _this.graphDiv._fullLayout);
_this.yaxis.setScale();
// set up the modified axes for tick drawing
var yDomain0 = _this.yaxis.domain[0];
// aaxis goes up the left side. Set it up as a y axis, but with
// fictitious angles and domain, but then rotate and translate
// it into place at the end
var aaxis = _this.aaxis = extendFlat({}, ternaryLayout.aaxis, {
visible: true,
range: [amin, sum - bmin - cmin],
side: 'left',
_counterangle: 30,
// tickangle = 'auto' means 0 anyway for a y axis, need to coerce to 0 here
// so we can shift by 30.
tickangle: (+ternaryLayout.aaxis.tickangle || 0) - 30,
domain: [yDomain0, yDomain0 + yDomainFinal * w_over_h],
_axislayer: _this.layers.aaxis,
_gridlayer: _this.layers.agrid,
_pos: 0, // _this.xaxis.domain[0] * graphSize.w,
_id: 'y',
_length: w,
_gridpath: 'M0,0l' + h + ',-' + (w / 2)
});
setConvert(aaxis, _this.graphDiv._fullLayout);
aaxis.setScale();
// baxis goes across the bottom (backward). We can set it up as an x axis
// without any enclosing transformation.
var baxis = _this.baxis = extendFlat({}, ternaryLayout.baxis, {
visible: true,
range: [sum - amin - cmin, bmin],
side: 'bottom',
_counterangle: 30,
domain: _this.xaxis.domain,
_axislayer: _this.layers.baxis,
_gridlayer: _this.layers.bgrid,
_counteraxis: _this.aaxis,
_pos: 0, // (1 - yDomain0) * graphSize.h,
_id: 'x',
_length: w,
_gridpath: 'M0,0l-' + (w / 2) + ',-' + h
});
setConvert(baxis, _this.graphDiv._fullLayout);
baxis.setScale();
aaxis._counteraxis = baxis;
// caxis goes down the right side. Set it up as a y axis, with
// post-transformation similar to aaxis
var caxis = _this.caxis = extendFlat({}, ternaryLayout.caxis, {
visible: true,
range: [sum - amin - bmin, cmin],
side: 'right',
_counterangle: 30,
tickangle: (+ternaryLayout.caxis.tickangle || 0) + 30,
domain: [yDomain0, yDomain0 + yDomainFinal * w_over_h],
_axislayer: _this.layers.caxis,
_gridlayer: _this.layers.cgrid,
_counteraxis: _this.baxis,
_pos: 0, // _this.xaxis.domain[1] * graphSize.w,
_id: 'y',
_length: w,
_gri ......
// TODO don't reset projection on all graph edits
_this.projection = null;
_this.setScale = createGeoScale(geoLayout, graphSize);
_this.makeProjection(geoLayout);
_this.makePath();
_this.adjustLayout(geoLayout, graphSize);
_this.zoom = createGeoZoom(_this, geoLayout);
_this.zoomReset = createGeoZoomReset(_this, geoLayout);
_this.mockAxis = createMockAxis(fullLayout);
_this.framework
.call(_this.zoom)
...drawAxes = function (doTitles) {
var _this = this,
gd = _this.graphDiv,
titlesuffix = _this.id.substr(7) + 'title',
aaxis = _this.aaxis,
baxis = _this.baxis,
caxis = _this.caxis;
// 3rd arg true below skips titles, so we can configure them
// correctly later on.
Axes.doTicks(gd, aaxis, true);
Axes.doTicks(gd, baxis, true);
Axes.doTicks(gd, caxis, true);
if(doTitles) {
var apad = Math.max(aaxis.showticklabels ? aaxis.tickfont.size / 2 : 0,
(caxis.showticklabels ? caxis.tickfont.size * 0.75 : 0) +
(caxis.ticks === 'outside' ? caxis.ticklen * 0.87 : 0));
Titles.draw(gd, 'a' + titlesuffix, {
propContainer: aaxis,
propName: _this.id + '.aaxis.title',
dfltName: 'Component A',
attributes: {
x: _this.x0 + _this.w / 2,
y: _this.y0 - aaxis.titlefont.size / 3 - apad,
'text-anchor': 'middle'
}
});
var bpad = (baxis.showticklabels ? baxis.tickfont.size : 0) +
(baxis.ticks === 'outside' ? baxis.ticklen : 0) + 3;
Titles.draw(gd, 'b' + titlesuffix, {
propContainer: baxis,
propName: _this.id + '.baxis.title',
dfltName: 'Component B',
attributes: {
x: _this.x0 - bpad,
y: _this.y0 + _this.h + baxis.titlefont.size * 0.83 + bpad,
'text-anchor': 'middle'
}
});
Titles.draw(gd, 'c' + titlesuffix, {
propContainer: caxis,
propName: _this.id + '.caxis.title',
dfltName: 'Component C',
attributes: {
x: _this.x0 + _this.w + bpad,
y: _this.y0 + _this.h + caxis.titlefont.size * 0.83 + bpad,
'text-anchor': 'middle'
}
});
}
}...
_this.layers.aaxis.attr('transform', aTransform);
_this.layers.agrid.attr('transform', aTransform);
var cTransform = 'translate(' + (x0 + w / 2) + ',' + y0 + ')rotate(-30)';
_this.layers.caxis.attr('transform', cTransform);
_this.layers.cgrid.attr('transform', cTransform);
_this.drawAxes(true);
// remove crispEdges - all the off-square angles in ternary plots
// make these counterproductive.
_this.plotContainer.selectAll('.crisp').classed('crisp', false);
var axlines = _this.layers.axlines;
axlines.select('.aline')
...init = function (fullLayout) {
this.container = fullLayout._ternarylayer;
this.defs = fullLayout._defs;
this.layoutId = fullLayout._uid;
this.traceHash = {};
}...
*/
Plotly.plot = function(gd, data, layout, config) {
var frames;
gd = helpers.getGraphDiv(gd);
// Events.init is idempotent and bails early if gd has already been init'd
Events.init(gd);
if(Lib.isPlainObject(data)) {
var obj = data;
data = obj.data;
layout = obj.layout;
config = obj.config;
frames = obj.frames;
...initInteractions = function () {
var _this = this,
dragger = _this.layers.plotbg.select('path').node(),
gd = _this.graphDiv,
zoomContainer = _this.layers.zoom;
// use plotbg for the main interactions
var dragOptions = {
element: dragger,
gd: gd,
plotinfo: {plot: zoomContainer},
doubleclick: doubleClick,
subplot: _this.id,
prepFn: function(e, startX, startY) {
// these aren't available yet when initInteractions
// is called
dragOptions.xaxes = [_this.xaxis];
dragOptions.yaxes = [_this.yaxis];
var dragModeNow = gd._fullLayout.dragmode;
if(e.shiftKey) {
if(dragModeNow === 'pan') dragModeNow = 'zoom';
else dragModeNow = 'pan';
}
if(dragModeNow === 'lasso') dragOptions.minDrag = 1;
else dragOptions.minDrag = undefined;
if(dragModeNow === 'zoom') {
dragOptions.moveFn = zoomMove;
dragOptions.doneFn = zoomDone;
zoomPrep(e, startX, startY);
}
else if(dragModeNow === 'pan') {
dragOptions.moveFn = plotDrag;
dragOptions.doneFn = dragDone;
panPrep();
clearSelect();
}
else if(dragModeNow === 'select' || dragModeNow === 'lasso') {
prepSelect(e, startX, startY, dragOptions, dragModeNow);
}
}
};
var x0, y0, mins0, span0, mins, lum, path0, dimmed, zb, corners;
function zoomPrep(e, startX, startY) {
var dragBBox = dragger.getBoundingClientRect();
x0 = startX - dragBBox.left;
y0 = startY - dragBBox.top;
mins0 = {
a: _this.aaxis.range[0],
b: _this.baxis.range[1],
c: _this.caxis.range[1]
};
mins = mins0;
span0 = _this.aaxis.range[1] - mins0.a;
lum = tinycolor(_this.graphDiv._fullLayout[_this.id].bgcolor).getLuminance();
path0 = 'M0,' + _this.h + 'L' + (_this.w / 2) + ', 0L' + _this.w + ',' + _this.h + 'Z';
dimmed = false;
zb = zoomContainer.append('path')
.attr('class', 'zoombox')
.style({
'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
'stroke-width': 0
})
.attr('d', path0);
corners = zoomContainer.append('path')
.attr('class', 'zoombox-corners')
.style({
fill: Color.background,
stroke: Color.defaultLine,
'stroke-width': 1,
opacity: 0
})
.attr('d', 'M0,0Z');
clearSelect();
}
function getAFrac(x, y) { return 1 - (y / _this.h); }
function getBFrac(x, y) { return 1 - ((x + (_this.h - y) / Math.sqrt(3)) / _this.w); }
function getCFrac(x, y) { return ((x - (_this.h - y) / Math.sqrt(3)) / _this.w); }
function zoomMove(dx0, dy0) {
var x1 = x0 + dx0,
y1 = y0 + dy0,
afrac = Math.max(0, Math.min(1, getAFrac(x0, y0), getAFrac(x1, y1))),
bfrac = Math.max(0, Math.min(1, getBFrac(x0, y0), getBFrac(x1, y1))),
cfrac = Math.max(0, Math.min(1, getCFrac(x0, y0), getCFrac(x1, y1))),
xLeft = ((afrac / 2) + cfrac) * _this.w,
xRight = (1 - (afrac / 2) - bfrac) * _this.w,
xCenter = (xLeft + xRight) / 2,
xSpan = xRight - xLeft,
yBottom = (1 - afrac) * _this.h,
yTop = yBottom - xSpan / w_over_h;
if(xSpan < constants.MINZOOM) {
mins = mins0;
zb.attr('d', path0);
corners.attr('d', 'M0,0Z');
}
else {
mins = {
a: mins0.a + afrac * span0,
b: mins0.b + bfrac * span0,
c: mins0.c + cfrac * span0
};
zb.attr('d', path0 + 'M' + xLeft + ',' + yBottom +
'H' + xRight + 'L' + xCenter + ',' + yTop + ......
.attr('class', function(d) { return 'grid ' + d; })
.each(function(d) { _this.layers[d] = d3.select(this); });
_this.plotContainer.selectAll('.backplot,.frontplot,.grids')
.call(Drawing.setClipUrl, clipId);
if(!_this.graphDiv._context.staticPlot) {
_this.initInteractions();
}
};
var w_over_h = Math.sqrt(4 / 3);
proto.adjustLayout = function(ternaryLayout, graphSize) {
var _this = this,
...makeFramework = function () {
var _this = this;
var defGroup = _this.defs.selectAll('g.clips')
.data([0]);
defGroup.enter().append('g')
.classed('clips', true);
// clippath for this ternary subplot
var clipId = 'clip' + _this.layoutId + _this.id;
_this.clipDef = defGroup.selectAll('#' + clipId)
.data([0]);
_this.clipDef.enter().append('clipPath').attr('id', clipId)
.append('path').attr('d', 'M0,0Z');
// container for everything in this ternary subplot
_this.plotContainer = _this.container.selectAll('g.' + _this.id)
.data([0]);
_this.plotContainer.enter().append('g')
.classed(_this.id, true);
_this.layers = {};
// inside that container, we have one container for the data, and
// one each for the three axes around it.
var plotLayers = [
'draglayer',
'plotbg',
'backplot',
'grids',
'frontplot',
'zoom',
'aaxis', 'baxis', 'caxis', 'axlines'
];
var toplevel = _this.plotContainer.selectAll('g.toplevel')
.data(plotLayers);
toplevel.enter().append('g')
.attr('class', function(d) { return 'toplevel ' + d; })
.each(function(d) {
var s = d3.select(this);
_this.layers[d] = s;
// containers for different trace types.
// NOTE - this is different from cartesian, where all traces
// are in front of grids. Here I'm putting maps behind the grids
// so the grids will always be visible if they're requested.
// Perhaps we want that for cartesian too?
if(d === 'frontplot') s.append('g').classed('scatterlayer', true);
else if(d === 'backplot') s.append('g').classed('maplayer', true);
else if(d === 'plotbg') s.append('path').attr('d', 'M0,0Z');
else if(d === 'axlines') {
s.selectAll('path').data(['aline', 'bline', 'cline'])
.enter().append('path').each(function(d) {
d3.select(this).classed(d, true);
});
}
});
var grids = _this.plotContainer.select('.grids').selectAll('g.grid')
.data(['agrid', 'bgrid', 'cgrid']);
grids.enter().append('g')
.attr('class', function(d) { return 'grid ' + d; })
.each(function(d) { _this.layers[d] = d3.select(this); });
_this.plotContainer.selectAll('.backplot,.frontplot,.grids')
.call(Drawing.setClipUrl, clipId);
if(!_this.graphDiv._context.staticPlot) {
_this.initInteractions();
}
}...
this.clipAngle = null;
this.setScale = null;
this.path = null;
this.zoom = null;
this.zoomReset = null;
this.makeFramework();
this.traceHash = {};
}
module.exports = Geo;
var proto = Geo.prototype;
...plot = function (ternaryCalcData, fullLayout) {
var _this = this,
ternaryLayout = fullLayout[_this.id],
graphSize = fullLayout._size;
_this.adjustLayout(ternaryLayout, graphSize);
Plots.generalUpdatePerTraceModule(_this, ternaryCalcData, ternaryLayout);
_this.layers.plotbg.select('path').call(Color.fill, ternaryLayout.bgcolor);
}...
fullLayout._infolayer.selectAll('.cb' + uid).remove();
}
}
// loop over the base plot modules present on graph
var basePlotModules = fullLayout._basePlotModules;
for(i = 0; i < basePlotModules.length; i++) {
basePlotModules[i].plot(gd);
}
// keep reference to shape layers in subplots
var layerSubplot = fullLayout._paper.selectAll('.layer-subplot');
fullLayout._shapeSubplotLayers = layerSubplot.selectAll('.shapelayer');
// styling separate from drawing
...getTopojsonFeatures = function (trace, topojson) {
var layer = locationmodeToLayer[trace.locationmode],
obj = topojson.objects[layer];
return topojsonFeature(topojson, obj).features;
}n/a
getTopojsonName = function (geoLayout) {
return [
geoLayout.scope.replace(/ /g, '-'), '_',
geoLayout.resolution.toString(), 'm'
].join('');
}...
Fx.loneUnhover(fullLayout._toppaper);
});
_this.framework.on('click', function() {
Fx.click(_this.graphDiv, d3.event);
});
topojsonNameNew = topojsonUtils.getTopojsonName(geoLayout);
if(_this.topojson === null || topojsonNameNew !== _this.topojsonName) {
_this.topojsonName = topojsonNameNew;
if(PlotlyGeoAssets.topojson[_this.topojsonName] !== undefined) {
_this.topojson = PlotlyGeoAssets.topojson[_this.topojsonName];
_this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
...getTopojsonPath = function (topojsonURL, topojsonName) {
return topojsonURL + topojsonName + '.json';
}...
_this.topojsonName = topojsonNameNew;
if(PlotlyGeoAssets.topojson[_this.topojsonName] !== undefined) {
_this.topojson = PlotlyGeoAssets.topojson[_this.topojsonName];
_this.onceTopojsonIsLoaded(geoCalcData, geoLayout);
}
else {
topojsonPath = topojsonUtils.getTopojsonPath(
_this.topojsonURL,
_this.topojsonName
);
promises.push(new Promise(function(resolve, reject) {
d3.json(topojsonPath, function(error, topojson) {
if(error) {
...function transform(dataIn, state) {
var dataOut = [];
for(var i = 0; i < dataIn.length; i++) {
var traceIn = dataIn[i];
if(traceIn.type !== 'candlestick') {
dataOut.push(traceIn);
continue;
}
dataOut.push(
makeTrace(traceIn, state, 'increasing'),
makeTrace(traceIn, state, 'decreasing')
);
}
helpers.addRangeSlider(dataOut, state.layout);
return dataOut;
}...
dataOut = [fullTrace];
for(var i = 0; i < container.length; i++) {
var transform = container[i],
_module = transformsRegistry[transform.type];
if(_module && _module.transform) {
dataOut = _module.transform(dataOut, {
transform: transform,
fullTrace: fullTrace,
fullData: fullData,
layout: layout,
fullLayout: fullLayout,
transformIndex: i
});
...function calcTransform(gd, trace, opts) {
var direction = opts.direction,
filterFn = helpers.getFilterFn(direction);
var open = trace.open,
high = trace.high,
low = trace.low,
close = trace.close;
var len = open.length,
x = [],
y = [];
var appendX = trace._fullInput.x ?
function(i) {
var v = trace.x[i];
x.push(v, v, v, v, v, v);
} :
function(i) {
x.push(i, i, i, i, i, i);
};
var appendY = function(o, h, l, c) {
y.push(l, o, c, c, c, h);
};
for(var i = 0; i < len; i++) {
if(filterFn(open[i], close[i])) {
appendX(i);
appendY(open[i], high[i], low[i], close[i]);
}
}
trace.x = x;
trace.y = y;
}...
for(j = 0; j < trace.transforms.length; j++) {
var transform = trace.transforms[j];
_module = transformsRegistry[transform.type];
if(_module && _module.calcTransform) {
hasCalcTransform = true;
_module.calcTransform(gd, trace, transform);
}
}
}
}
// clear stuff that should recomputed in 'regular' loop
if(hasCalcTransform) {
...supplyDefaults = function (transformIn, traceOut, layout, traceIn) {
helpers.clearEphemeralTransformOpts(traceIn);
helpers.copyOHLC(transformIn, traceOut);
return transformIn;
}...
gd._replotPending = true;
return Promise.reject();
} else {
// we're going ahead with a replot now
gd._replotPending = false;
}
Plots.supplyDefaults(gd);
var fullLayout = gd._fullLayout;
// Polar plots
if(data && data[0] && data[0].r) return plotPolar(gd, data, layout);
// so we don't try to re-call Plotly.plot from inside
...function unhoverRaw(gd, evt) {
var fullLayout = gd._fullLayout;
var oldhoverdata = gd._hoverdata;
if(!evt) evt = {};
if(evt.target &&
Events.triggerHandler(gd, 'plotly_beforehover', evt) === false) {
return;
}
fullLayout._hoverlayer.selectAll('g').remove();
fullLayout._hoverlayer.selectAll('line').remove();
fullLayout._hoverlayer.selectAll('circle').remove();
gd._hoverdata = undefined;
if(evt.target && oldhoverdata) {
gd.emit('plotly_unhover', {
event: evt,
points: oldhoverdata
});
}
}...
// Important, clear any queued hovers
if(gd._hoverTimer) {
clearTimeout(gd._hoverTimer);
gd._hoverTimer = undefined;
}
unhover.raw(gd, evt, subplot);
};
// remove hover effects on mouse out, and emit unhover event
unhover.raw = function unhoverRaw(gd, evt) {
var fullLayout = gd._fullLayout;
var oldhoverdata = gd._hoverdata;
...wrapped = function (gd, evt, subplot) {
if(typeof gd === 'string') gd = document.getElementById(gd);
// Important, clear any queued hovers
if(gd._hoverTimer) {
clearTimeout(gd._hoverTimer);
gd._hoverTimer = undefined;
}
unhover.raw(gd, evt, subplot);
}n/a