Skip to content

Commit 705e102

Browse files
committed
Merge pull request #4459 from plotly/plotly-config-showSources
Improve Plotly.plot config setup
2 parents 2ef5cc9 + 89282d6 commit 705e102

File tree

6 files changed

+316
-274
lines changed

6 files changed

+316
-274
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
'use strict';
2+
3+
/* global $:false */
4+
/* global pullf: false */
5+
6+
// TODO remove jQuery dependency
7+
8+
var Plotly = require('../plotly');
9+
var d3 = require('d3');
10+
var isNumeric = require('../isnumeric');
11+
12+
function showSources(td) {
13+
if(td._context && td._context.staticPlot) return;
14+
// show the sources of data in the active tab
15+
var allsources = td.sourcelist;
16+
if(!allsources) {
17+
getSources(td);
18+
return;
19+
}
20+
var container = d3.select(td).select('.js-sourcelinks'),
21+
extsources = allsources.filter(function(v){
22+
return isNumeric(v.ref_fid);
23+
}),
24+
firstsource = extsources[0] || allsources[0];
25+
container.text('');
26+
td.shouldshowsources = false;
27+
// no sources at all? quit
28+
if(!firstsource) return;
29+
30+
// find number of unique internal and external sources
31+
var extobj = {}, plotlyobj = {};
32+
extsources.forEach(function(v){ extobj[v.url] = 1; });
33+
allsources.forEach(function(v){
34+
if(!isNumeric(v.ref_fid)) plotlyobj[v.ref_fid] = 1;
35+
});
36+
37+
var fidparts = String(firstsource.ref_fid).split(':'),
38+
isplot = Plotly.Lib.isPlotDiv(td),
39+
workspace = !isplot || td._context.workspace,
40+
mainlink,
41+
extraslink;
42+
43+
if(isplot) { // svg version for plots
44+
// only sources from the same user? also quit, if we're on a plot
45+
var thisuser = firstsource.fid.split(':')[0];
46+
if(allsources.every(function(v){
47+
return String(v.ref_fid).split(':')[0]===thisuser;
48+
})) {
49+
return;
50+
}
51+
td.shouldshowsources = true;
52+
53+
/**
54+
* in case someone REALLY doesn't want to show sources
55+
* they can hide them...
56+
* but you can always see them by going to the grid
57+
*/
58+
if(td.layout.hidesources) return;
59+
container.append('tspan').text('Source: ');
60+
mainlink = container.append('a').attr({'xlink:xlink:href':'#'});
61+
if(isNumeric(firstsource.ref_fid)) {
62+
mainlink.attr({
63+
'xlink:xlink:show':'new',
64+
'xlink:xlink:href':firstsource.ref_url
65+
});
66+
}
67+
else if(!workspace){
68+
mainlink.attr({
69+
'xlink:xlink:show':'new',
70+
'xlink:xlink:href':'/'+fidparts[1]+'/~'+fidparts[0]
71+
});
72+
}
73+
74+
if(allsources.length>1) {
75+
container.append('tspan').text(' - ');
76+
extraslink = container.append('a')
77+
.attr({'xlink:xlink:href':'#'});
78+
}
79+
}
80+
else { // html version for grids (and scripts?)
81+
if(!container.node()) {
82+
container = d3.select(td).select('.grid-container')
83+
.append('div')
84+
.attr('class', 'grid-sourcelinks js-sourcelinks');
85+
}
86+
container.append('span').text('Source: ');
87+
mainlink = container.append('a').attr({
88+
'href':'#',
89+
'class': 'link--impt'
90+
});
91+
if(isNumeric(firstsource.ref_fid)) {
92+
mainlink.attr({
93+
'target':'_blank',
94+
'href':firstsource.ref_url
95+
});
96+
}
97+
98+
if(allsources.length>1) {
99+
container.append('span').text(' - ');
100+
extraslink = container.append('a')
101+
.attr({href: '#'})
102+
.classed('link--impt', true);
103+
}
104+
}
105+
106+
mainlink.text(firstsource.ref_filename);
107+
108+
function pullSource(){
109+
pullf({fid: firstsource.ref_fid});
110+
return false;
111+
}
112+
113+
function fullSourcing(){
114+
var sourceModal = $('#sourceModal'),
115+
sourceViewer = sourceModal.find('#source-viewer').empty();
116+
117+
sourceViewer.data('jsontree', '')
118+
.jsontree(JSON.stringify(sourceObj),
119+
{terminators: false, collapsibleOuter: false})
120+
.show();
121+
if(workspace) {
122+
sourceModal.find('[data-fid]').click(function(){
123+
sourceModal.modal('hide');
124+
pullf({fid:$(this).attr('data-fid')});
125+
return false;
126+
});
127+
}
128+
else {
129+
sourceModal.find('[data-fid]').each(function(){
130+
fidparts = $(this).attr('data-fid').split(':');
131+
$(this).attr({href:'/~'+fidparts[0]+'/'+fidparts[1]});
132+
});
133+
if(window.self !== window.top) {
134+
// in an iframe: basically fill the frame
135+
sourceModal.css({
136+
left: '10px',
137+
right: '10px',
138+
bottom: '10px',
139+
width: 'auto',
140+
height: 'auto',
141+
margin: 0
142+
});
143+
}
144+
}
145+
sourceModal.modal('show');
146+
147+
sourceModal.find('.close')
148+
.off('click')
149+
.on('click', function(){
150+
sourceModal.modal('hide');
151+
return false;
152+
});
153+
return false;
154+
}
155+
156+
if(!isplot || workspace) mainlink.on('click', pullSource);
157+
158+
if(extraslink) extraslink.text('Full list').on('click', fullSourcing);
159+
160+
function makeSourceObj(container, refByUid) {
161+
if(cnt < 0) {
162+
console.log('infinite loop?');
163+
return container;
164+
}
165+
cnt--;
166+
167+
allsources.forEach(function(src){
168+
if(src.ref_by_uid === refByUid) {
169+
var linkval;
170+
if(isNumeric(src.ref_fid)) {
171+
linkval = '<a href="' + src.ref_url + '" target="_blank">' +
172+
src.ref_filename + '</a>';
173+
}
174+
else {
175+
var refUser = src.ref_fid.split(':')[0],
176+
fn = (refUser !== window.user ? refUser + ': ' : '') +
177+
src.ref_filename;
178+
linkval = '<a href="#" data-fid="' + src.ref_fid + '">'+
179+
fn + '</a>';
180+
}
181+
container[linkval] = makeSourceObj({}, src.uid);
182+
}
183+
});
184+
return container;
185+
}
186+
187+
var cnt = allsources.length,
188+
sourceObj = makeSourceObj({}, null);
189+
}
190+
191+
function getSources(td) {
192+
var extrarefs = (td.ref_fids || []).join(',');
193+
if(!td.fid && !extrarefs) return;
194+
if(!window.PLOTLYENV || !window.PLOTLYENV.DOMAIN_WEBAPP) return;
195+
196+
$.get('/getsources', {fid: td.fid, extrarefs:extrarefs}, function(res) {
197+
td.sourcelist = JSON.parse(res);
198+
if(!Array.isArray(td.sourcelist)) {
199+
console.log('sourcelist error',td.sourcelist);
200+
td.sourcelist = [];
201+
}
202+
showSources(td);
203+
});
204+
}
205+
206+
module.exports = showSources;

shelly/plotlyjs/static/plotlyjs/src/graph_obj.js

Lines changed: 19 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
'use strict';
2-
/* jshint camelcase: false */
32

43
// ---external global dependencies
54
/* global Promise:false */
65

7-
86
var Plotly = require('./plotly'),
97
d3 = require('d3'),
108
m4FromQuat = require('gl-mat4/fromQuat'),
@@ -241,63 +239,13 @@ plots.redrawText = function(gd) {
241239
});
242240
};
243241

244-
// where and how the background gets set can be overridden by context
245-
// so we define the default (plotlyjs) behavior here
246-
function defaultSetBackground(gd, bgColor) {
247-
try {
248-
gd._fullLayout._paper.style('background', bgColor);
249-
}
250-
catch(e) { console.log(e); }
251-
}
252-
253242
function opaqueSetBackground(gd, bgColor) {
254243
gd._fullLayout._paperdiv.style('background', 'white');
255-
defaultSetBackground(gd, bgColor);
244+
Plotly.defaultConfig.setBackground(gd, bgColor);
256245
}
257246

258-
// this will be transfered over to gd and overridden by
259-
// config args to Plotly.plot
260-
// the defaults are the appropriate settings for plotly.js,
261-
// so we get the right experience without any config argument
262-
plots.defaultConfig = {
263-
// no interactivity, for export or image generation
264-
staticPlot: false,
265-
// we're in the workspace, so need toolbar etc
266-
// TODO describe functionality instead?
267-
workspace: false,
268-
// we can edit titles, move annotations, etc
269-
editable: false,
270-
// plot will respect layout.autosize=true and infer its container size
271-
autosizable: false,
272-
// if we DO autosize, do we fill the container or the screen?
273-
fillFrame: false,
274-
// if we DO autosize, set the frame margins in percents of plot size
275-
frameMargins: 0,
276-
// mousewheel or two-finger scroll zooms the plot
277-
scrollZoom: false,
278-
// double click interaction (false, 'reset', 'autosize' or 'reset+autosize')
279-
doubleClick: 'reset+autosize',
280-
// new users see some hints about interactivity
281-
showTips: true,
282-
// link to open this plot in plotly
283-
showLink: true,
284-
// if we show a link, does it contain data or just link to a plotly file?
285-
sendData: true,
286-
// text appearing in the sendData link
287-
linkText: 'Edit chart',
288-
// display the modebar (true, false, or 'hover')
289-
displayModeBar: 'hover',
290-
// add the plotly logo on the end of the modebar
291-
displaylogo: true,
292-
// increase the pixel ratio for Gl plot images
293-
plotGlPixelRatio: 2,
294-
// fn to add the background color to a different container
295-
// or 'opaque' to ensure there's white behind it
296-
setBackground: defaultSetBackground
297-
};
298-
299247
function setPlotContext(gd, config) {
300-
if(!gd._context) gd._context = Plotly.Lib.extendFlat({}, plots.defaultConfig);
248+
if(!gd._context) gd._context = Plotly.Lib.extendFlat({}, Plotly.defaultConfig);
301249
var context = gd._context;
302250

303251
if(config) {
@@ -323,7 +271,6 @@ function setPlotContext(gd, config) {
323271

324272
//staticPlot forces a bunch of others:
325273
if(context.staticPlot) {
326-
context.workspace = false;
327274
context.editable = false;
328275
context.autosizable = false;
329276
context.scrollZoom = false;
@@ -334,14 +281,20 @@ function setPlotContext(gd, config) {
334281
}
335282
}
336283

337-
// the 'view in plotly' and source links - note that now plot() calls this
338-
// so it can regenerate whenever it replots
284+
/**
285+
* Adds the 'Edit chart' link.
286+
* Note that now Plotly.plot() calls this so it can regenerate whenever it replots
287+
*
288+
* Add source links to your graph inside the 'showSources' config argument.
289+
*/
339290
plots.addLinks = function(gd) {
340291
var fullLayout = gd._fullLayout;
341-
var linkContainer = fullLayout._paper.selectAll('text.js-plot-link-container').data([0]);
292+
293+
var linkContainer = fullLayout._paper
294+
.selectAll('text.js-plot-link-container').data([0]);
342295

343296
linkContainer.enter().append('text')
344-
.classed('js-plot-link-container',true)
297+
.classed('js-plot-link-container', true)
345298
.style({
346299
'font-family':'"Open Sans",Arial,sans-serif',
347300
'font-size':'12px',
@@ -350,9 +303,9 @@ plots.addLinks = function(gd) {
350303
})
351304
.each(function(){
352305
var links = d3.select(this);
353-
links.append('tspan').classed('js-link-to-tool',true);
354-
links.append('tspan').classed('js-link-spacer',true);
355-
links.append('tspan').classed('js-sourcelinks',true);
306+
links.append('tspan').classed('js-link-to-tool', true);
307+
links.append('tspan').classed('js-link-spacer', true);
308+
links.append('tspan').classed('js-sourcelinks', true);
356309
});
357310

358311
// The text node inside svg
@@ -371,24 +324,23 @@ plots.addLinks = function(gd) {
371324
// Align the text at the left
372325
attrs['text-anchor'] = 'start';
373326
attrs.x = 5;
374-
} else {
327+
}
328+
else {
375329
// Align the text at the right
376330
attrs['text-anchor'] = 'end';
377331
attrs.x = fullLayout._paper.attr('width') - 7;
378332
}
379333

380334
linkContainer.attr(attrs);
381335

382-
383336
var toolspan = linkContainer.select('.js-link-to-tool'),
384337
spacespan = linkContainer.select('.js-link-spacer'),
385338
sourcespan = linkContainer.select('.js-sourcelinks');
386339

387-
// data source links
388-
Plotly.Lib.showSources(gd);
340+
if(gd._context.showSources) gd._context.showSources(gd);
389341

390342
// 'view in plotly' link for embedded plots
391-
if(gd._context.showLink) positionPlayWithData(gd,toolspan);
343+
if(gd._context.showLink) positionPlayWithData(gd, toolspan);
392344

393345
// separator if we have both sources and tool link
394346
spacespan.text((toolspan.text() && sourcespan.text()) ? ' - ' : '');

0 commit comments

Comments
 (0)