Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit defab4c

Browse filesBrowse files
authored
offline.plot/iplot improvements (plotly#1234)
* Support auto-resize in offline.plot with output_type='div' and include_plotlyjs=False * Support auto-resize for classic notebook iplot * Add 'cdn' and 'directory' include_plotlyjs options in offline.plot. When 'cdn', the resulting html file/div includes a script tag reference to the plotlyjs cdn. When 'directory', the resulting html file/div includes a script tag reference to a plotly.min.js bundle in the same directory as the html file. If output_type is 'file' then this plotly.min.js bundle is created in the output directory if it doesn't already exist. * Add option to specify include_plotlyjs as a path/url to a *.js file. This makes it possible to specify an alternative CDN or offline location for plotly.js
1 parent c62b66f commit defab4c
Copy full SHA for defab4c

File tree

Expand file treeCollapse file tree

2 files changed

+259
-36
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+259
-36
lines changed

‎plotly/offline/offline.py

Copy file name to clipboardExpand all lines: plotly/offline/offline.py
+85-28Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import time
1313
import webbrowser
1414

15+
import six
1516
from requests.compat import json as _json
1617

1718
import plotly
@@ -358,8 +359,12 @@ def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly',
358359
plot_html, plotdivid, width, height = _plot_html(
359360
figure_or_data, config, validate, '100%', 525, True
360361
)
361-
display_bundle['text/html'] = plot_html
362-
display_bundle['text/vnd.plotly.v1+html'] = plot_html
362+
resize_script = ''
363+
if width == '100%' or height == '100%':
364+
resize_script = _build_resize_script(plotdivid)
365+
366+
display_bundle['text/html'] = plot_html + resize_script
367+
display_bundle['text/vnd.plotly.v1+html'] = plot_html + resize_script
363368

364369
ipython_display.display(display_bundle, raw=True)
365370

@@ -389,6 +394,16 @@ def iplot(figure_or_data, show_link=True, link_text='Export to plot.ly',
389394
ipython_display.display(ipython_display.HTML(script))
390395

391396

397+
def _build_resize_script(plotdivid):
398+
resize_script = (
399+
'<script type="text/javascript">'
400+
'window.addEventListener("resize", function(){{'
401+
'Plotly.Plots.resize(document.getElementById("{id}"));}});'
402+
'</script>'
403+
).format(id=plotdivid)
404+
return resize_script
405+
406+
392407
def plot(figure_or_data, show_link=True, link_text='Export to plot.ly',
393408
validate=True, output_type='file', include_plotlyjs=True,
394409
filename='temp-plot.html', auto_open=True, image=None,
@@ -433,10 +448,41 @@ def plot(figure_or_data, show_link=True, link_text='Export to plot.ly',
433448
in a standalone HTML file.
434449
Use 'div' if you are embedding these graphs in an HTML file with
435450
other graphs or HTML markup, like a HTML report or an website.
436-
include_plotlyjs (default=True) -- If True, include the plotly.js
437-
source code in the output file or string.
438-
Set as False if your HTML file already contains a copy of the plotly.js
451+
include_plotlyjs (True | False | 'cdn' | 'directory' | path - default=True)
452+
Specifies how the plotly.js library is included in the output html
453+
file or div string.
454+
455+
If True, a script tag containing the plotly.js source code (~3MB)
456+
is included in the output. HTML files generated with this option are
457+
fully self-contained and can be used offline.
458+
459+
If 'cdn', a script tag that references the plotly.js CDN is included
460+
in the output. HTML files generated with this option are about 3MB
461+
smaller than those generated with include_plotlyjs=True, but they
462+
require an active internet connection in order to load the plotly.js
439463
library.
464+
465+
If 'directory', a script tag is included that references an external
466+
plotly.min.js bundle that is assumed to reside in the same
467+
directory as the HTML file. If output_type='file' then the
468+
plotly.min.js bundle is copied into the directory of the resulting
469+
HTML file. If a file named plotly.min.js already exists in the output
470+
directory then this file is left unmodified and no copy is performed.
471+
HTML files generated with this option can be used offline, but they
472+
require a copy of the plotly.min.js bundle in the same directory.
473+
This option is useful when many figures will be saved as HTML files in
474+
the same directory because the plotly.js source code will be included
475+
only once per output directory, rather than once per output file.
476+
477+
If a string that ends in '.js', a script tag is included that
478+
references the specified path. This approach can be used to point
479+
the resulting HTML file to an alternative CDN.
480+
481+
If False, no script tag referencing plotly.js is included. This is
482+
useful when output_type='div' and the resulting div string will be
483+
placed inside an HTML document that already loads plotly.js. This
484+
option is not advised when output_type='file' as it will result in
485+
a non-functional html file.
440486
filename (default='temp-plot.html') -- The local filename to save the
441487
outputted chart to. If the filename already exists, it will be
442488
overwritten. This argument only applies if `output_type` is 'file'.
@@ -477,25 +523,31 @@ def plot(figure_or_data, show_link=True, link_text='Export to plot.ly',
477523

478524
resize_script = ''
479525
if width == '100%' or height == '100%':
480-
resize_script = (
481-
''
482-
'<script type="text/javascript">'
483-
'window.addEventListener("resize", function(){{'
484-
'Plotly.Plots.resize(document.getElementById("{id}"));}});'
485-
'</script>'
486-
).format(id=plotdivid)
526+
resize_script = _build_resize_script(plotdivid)
527+
528+
if isinstance(include_plotlyjs, six.string_types):
529+
include_plotlyjs = include_plotlyjs.lower()
530+
531+
if include_plotlyjs == 'cdn':
532+
plotly_js_script = """\
533+
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>"""
534+
elif include_plotlyjs == 'directory':
535+
plotly_js_script = '<script src="plotly.min.js"></script>'
536+
elif (isinstance(include_plotlyjs, six.string_types) and
537+
include_plotlyjs.endswith('.js')):
538+
plotly_js_script = '<script src="{url}"></script>'.format(
539+
url=include_plotlyjs)
540+
elif include_plotlyjs:
541+
plotly_js_script = ''.join([
542+
'<script type="text/javascript">',
543+
get_plotlyjs(),
544+
'</script>',
545+
])
546+
else:
547+
plotly_js_script = ''
487548

488549
if output_type == 'file':
489550
with open(filename, 'w') as f:
490-
if include_plotlyjs:
491-
plotly_js_script = ''.join([
492-
'<script type="text/javascript">',
493-
get_plotlyjs(),
494-
'</script>',
495-
])
496-
else:
497-
plotly_js_script = ''
498-
499551
if image:
500552
if image not in __IMAGE_FORMATS:
501553
raise ValueError('The image parameter must be one of the '
@@ -523,25 +575,30 @@ def plot(figure_or_data, show_link=True, link_text='Export to plot.ly',
523575
'</body>',
524576
'</html>']))
525577

578+
# Check if we should copy plotly.min.js to output directory
579+
if include_plotlyjs == 'directory':
580+
bundle_path = os.path.join(
581+
os.path.dirname(filename), 'plotly.min.js')
582+
583+
if not os.path.exists(bundle_path):
584+
with open(bundle_path, 'w') as f:
585+
f.write(get_plotlyjs())
586+
526587
url = 'file://' + os.path.abspath(filename)
527588
if auto_open:
528589
webbrowser.open(url)
529590

530591
return url
531592

532593
elif output_type == 'div':
533-
if include_plotlyjs:
534-
return ''.join([
594+
595+
return ''.join([
535596
'<div>',
536-
'<script type="text/javascript">',
537-
get_plotlyjs(),
538-
'</script>',
597+
plotly_js_script,
539598
plot_html,
540599
resize_script,
541600
'</div>',
542601
])
543-
else:
544-
return plot_html
545602

546603

547604
def plot_mpl(mpl_fig, resize=False, strip_style=False,

‎plotly/tests/test_core/test_offline/test_offline.py

Copy file name to clipboardExpand all lines: plotly/tests/test_core/test_offline/test_offline.py
+174-8Lines changed: 174 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,29 @@
2121
)
2222
}
2323

24+
25+
resize_code_strings = [
26+
'window.addEventListener("resize", ',
27+
'Plotly.Plots.resize('
28+
]
29+
30+
2431
PLOTLYJS = plotly.offline.offline.get_plotlyjs()
2532

33+
cdn_script = ('<script src="https://cdn.plot.ly/plotly-latest.min.js">'
34+
'</script>')
35+
36+
directory_script = '<script src="plotly.min.js"></script>'
37+
2638

2739
class PlotlyOfflineBaseTestCase(TestCase):
2840
def tearDown(self):
2941
# Some offline tests produce an html file. Make sure we clean up :)
3042
try:
3143
os.remove('temp-plot.html')
44+
# Some tests that produce temp-plot.html
45+
# also produce plotly.min.js
46+
os.remove('plotly.min.js')
3247
except OSError:
3348
pass
3449

@@ -64,10 +79,142 @@ def test_default_plot_generates_expected_html(self):
6479
# and it's an <html> doc
6580
self.assertTrue(html.startswith('<html>') and html.endswith('</html>'))
6681

67-
def test_including_plotlyjs(self):
68-
html = self._read_html(plotly.offline.plot(fig, include_plotlyjs=False,
69-
auto_open=False))
70-
self.assertNotIn(PLOTLYJS, html)
82+
def test_including_plotlyjs_truthy_html(self):
83+
# For backwards compatibility all truthy values that aren't otherwise
84+
# recognized are considered true
85+
for include_plotlyjs in [True, 34, 'non-empty-str']:
86+
html = self._read_html(plotly.offline.plot(
87+
fig,
88+
include_plotlyjs=include_plotlyjs,
89+
output_type='file',
90+
auto_open=False))
91+
self.assertIn(PLOTLYJS, html)
92+
self.assertNotIn(cdn_script, html)
93+
self.assertNotIn(directory_script, html)
94+
95+
def test_including_plotlyjs_truthy_div(self):
96+
# For backwards compatibility all truthy values that aren't otherwise
97+
# recognized are considered true
98+
for include_plotlyjs in [True, 34, 'non-empty-str']:
99+
html = plotly.offline.plot(
100+
fig,
101+
include_plotlyjs=include_plotlyjs,
102+
output_type='div')
103+
self.assertIn(PLOTLYJS, html)
104+
self.assertNotIn(cdn_script, html)
105+
self.assertNotIn(directory_script, html)
106+
107+
def test_including_plotlyjs_false_html(self):
108+
# For backwards compatibility all truthy values that aren't otherwise
109+
# recognized are considered true
110+
for include_plotlyjs in [False, 0, '']:
111+
html = self._read_html(plotly.offline.plot(
112+
fig,
113+
include_plotlyjs=include_plotlyjs,
114+
output_type='file',
115+
auto_open=False))
116+
self.assertNotIn(PLOTLYJS, html)
117+
self.assertNotIn(cdn_script, html)
118+
self.assertNotIn(directory_script, html)
119+
120+
def test_including_plotlyjs_false_div(self):
121+
for include_plotlyjs in [False, 0, '']:
122+
html = plotly.offline.plot(
123+
fig,
124+
include_plotlyjs=include_plotlyjs,
125+
output_type='div')
126+
self.assertNotIn(PLOTLYJS, html)
127+
self.assertNotIn(cdn_script, html)
128+
self.assertNotIn(directory_script, html)
129+
130+
def test_including_plotlyjs_cdn_html(self):
131+
for include_plotlyjs in ['cdn', 'CDN', 'Cdn']:
132+
html = self._read_html(plotly.offline.plot(
133+
fig,
134+
include_plotlyjs=include_plotlyjs,
135+
output_type='file',
136+
auto_open=False))
137+
self.assertNotIn(PLOTLYJS, html)
138+
self.assertIn(cdn_script, html)
139+
self.assertNotIn(directory_script, html)
140+
141+
def test_including_plotlyjs_cdn_div(self):
142+
for include_plotlyjs in ['cdn', 'CDN', 'Cdn']:
143+
html = plotly.offline.plot(
144+
fig,
145+
include_plotlyjs=include_plotlyjs,
146+
output_type='div')
147+
self.assertNotIn(PLOTLYJS, html)
148+
self.assertIn(cdn_script, html)
149+
self.assertNotIn(directory_script, html)
150+
151+
def test_including_plotlyjs_directory_html(self):
152+
self.assertFalse(os.path.exists('plotly.min.js'))
153+
154+
for include_plotlyjs in ['directory', 'Directory', 'DIRECTORY']:
155+
html = self._read_html(plotly.offline.plot(
156+
fig,
157+
include_plotlyjs=include_plotlyjs,
158+
auto_open=False))
159+
self.assertNotIn(PLOTLYJS, html)
160+
self.assertNotIn(cdn_script, html)
161+
self.assertIn(directory_script, html)
162+
163+
# plot creates plotly.min.js in the output directory
164+
self.assertTrue(os.path.exists('plotly.min.js'))
165+
with open('plotly.min.js', 'r') as f:
166+
self.assertEqual(f.read(), PLOTLYJS)
167+
168+
def test_including_plotlyjs_directory_div(self):
169+
self.assertFalse(os.path.exists('plotly.min.js'))
170+
171+
for include_plotlyjs in ['directory', 'Directory', 'DIRECTORY']:
172+
html = plotly.offline.plot(
173+
fig,
174+
include_plotlyjs=include_plotlyjs,
175+
output_type='div',
176+
auto_open=False)
177+
178+
self.assertNotIn(PLOTLYJS, html)
179+
self.assertNotIn(cdn_script, html)
180+
self.assertIn(directory_script, html)
181+
182+
# plot does NOT create a plotly.min.js file in the output directory
183+
# when output_type is div
184+
self.assertFalse(os.path.exists('plotly.min.js'))
185+
186+
def test_including_plotlyjs_path_html(self):
187+
for include_plotlyjs in [
188+
('https://cdnjs.cloudflare.com/ajax/libs/plotly.js/1.40.1/'
189+
'plotly.min.js'),
190+
'subpath/to/plotly.min.js',
191+
'something.js']:
192+
193+
html = self._read_html(plotly.offline.plot(
194+
fig,
195+
include_plotlyjs=include_plotlyjs,
196+
output_type='file',
197+
auto_open=False))
198+
self.assertNotIn(PLOTLYJS, html)
199+
self.assertNotIn(cdn_script, html)
200+
self.assertNotIn(directory_script, html)
201+
self.assertIn(include_plotlyjs, html)
202+
203+
def test_including_plotlyjs_path_div(self):
204+
for include_plotlyjs in [
205+
('https://cdnjs.cloudflare.com/ajax/libs/plotly.js/1.40.1/'
206+
'plotly.min.js'),
207+
'subpath/to/plotly.min.js',
208+
'something.js']:
209+
210+
html = plotly.offline.plot(
211+
fig,
212+
include_plotlyjs=include_plotlyjs,
213+
output_type='div')
214+
self.assertNotIn(PLOTLYJS, html)
215+
self.assertNotIn(cdn_script, html)
216+
self.assertNotIn(directory_script, html)
217+
self.assertIn(include_plotlyjs, html)
71218

72219
def test_div_output(self):
73220
html = plotly.offline.plot(fig, output_type='div', auto_open=False)
@@ -77,10 +224,7 @@ def test_div_output(self):
77224
self.assertTrue(html.startswith('<div>') and html.endswith('</div>'))
78225

79226
def test_autoresizing(self):
80-
resize_code_strings = [
81-
'window.addEventListener("resize", ',
82-
'Plotly.Plots.resize('
83-
]
227+
84228
# If width or height wasn't specified, then we add a window resizer
85229
html = self._read_html(plotly.offline.plot(fig, auto_open=False))
86230
for resize_code_string in resize_code_strings:
@@ -96,6 +240,28 @@ def test_autoresizing(self):
96240
for resize_code_string in resize_code_strings:
97241
self.assertNotIn(resize_code_string, html)
98242

243+
def test_autoresizing_div(self):
244+
245+
# If width or height wasn't specified, then we add a window resizer
246+
for include_plotlyjs in [True, False, 'cdn', 'directory']:
247+
html = plotly.offline.plot(fig,
248+
output_type='div',
249+
include_plotlyjs=include_plotlyjs)
250+
251+
for resize_code_string in resize_code_strings:
252+
self.assertIn(resize_code_string, html)
253+
254+
# If width or height was specified, then we don't resize
255+
html = plotly.offline.plot({
256+
'data': fig['data'],
257+
'layout': {
258+
'width': 500, 'height': 500
259+
}
260+
}, output_type='div')
261+
262+
for resize_code_string in resize_code_strings:
263+
self.assertNotIn(resize_code_string, html)
264+
99265
def test_config(self):
100266
config = dict(linkText='Plotly rocks!',
101267
editable=True)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.