-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add new promise-based toImage and downloadImage to Plotly #446
Changes from 1 commit
642ddba
41c3126
3fca377
649e067
0f5022e
5f8877f
05a2448
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ | |
var Plotly = require('../../plotly'); | ||
var Lib = require('../../lib'); | ||
var setCursor = require('../../lib/setcursor'); | ||
var Snapshot = require('../../snapshot'); | ||
var downloadImage = require('../../snapshot/download'); | ||
var Icons = require('../../../build/ploticon'); | ||
|
||
|
||
|
@@ -49,49 +49,15 @@ modeBarButtons.toImage = { | |
title: 'Download plot as a png', | ||
icon: Icons.camera, | ||
click: function(gd) { | ||
var format = 'png'; | ||
|
||
if(Lib.isIE()) { | ||
Lib.notifier('Snapshotting is unavailable in Internet Explorer. ' + | ||
'Consider exporting your images using the Plotly Cloud', 'long'); | ||
return; | ||
} | ||
|
||
if(gd._snapshotInProgress) { | ||
Lib.notifier('Snapshotting is still in progress - please hold', 'long'); | ||
return; | ||
} | ||
|
||
gd._snapshotInProgress = true; | ||
Lib.notifier('Taking snapshot - this may take a few seconds', 'long'); | ||
|
||
var ev = Snapshot.toImage(gd, {format: format}); | ||
|
||
var filename = gd.fn || 'newplot'; | ||
filename += '.' + format; | ||
|
||
ev.once('success', function(result) { | ||
gd._snapshotInProgress = false; | ||
|
||
var downloadLink = document.createElement('a'); | ||
downloadLink.href = result; | ||
downloadLink.download = filename; // only supported by FF and Chrome | ||
|
||
document.body.appendChild(downloadLink); | ||
downloadLink.click(); | ||
document.body.removeChild(downloadLink); | ||
|
||
ev.clean(); | ||
}); | ||
|
||
ev.once('error', function(err) { | ||
gd._snapshotInProgress = false; | ||
|
||
Lib.notifier('Sorry there was a problem downloading your ' + format, 'long'); | ||
console.error(err); | ||
|
||
ev.clean(); | ||
}); | ||
downloadImage(gd) | ||
.then(function(filename) { | ||
Lib.notifier('Snapshot succeeded - ' + filename, 'long'); | ||
}) | ||
.catch(function() { | ||
Lib.notifier('Sorry there was a problem downloading your snapshot', 'long'); | ||
}); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @timelyportfolio why did you review this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind. I see. It's here now: https://github.com/plotly/plotly.js/pull/446/files#diff-40d98cbce238527d9bffcf2ecd4efa9bR33 |
||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/** | ||
* Copyright 2012-2016, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var Plotly = require('../plotly'); | ||
|
||
var isNumeric = require('fast-isnumeric'); | ||
|
||
/** | ||
* @param {object} gd figure Object | ||
* @param {object} opts option object | ||
* @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' | ||
* @param opts.width width of snapshot in px | ||
* @param opts.height height of snapshot in px | ||
*/ | ||
function toImage(gd, opts) { | ||
var Snapshot = require('../snapshot'); | ||
|
||
var promise = new Promise(function(resolve, reject) { | ||
// check for undefined opts | ||
opts = opts || {}; | ||
// default to png | ||
opts.format = opts.format || 'png'; | ||
|
||
var isSizeGood = function(size) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be good to explicitly return false on this one when size not good (just to be safe!) Edit: Nvm I see it's only used right below. Up to you! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added to be safe. |
||
// undefined and null are valid options | ||
if(size === undefined || size === null) { | ||
return true; | ||
} | ||
|
||
if(isNumeric(size) && size > 1) { | ||
return true; | ||
} | ||
}; | ||
|
||
if(!isSizeGood(opts.width) || !isSizeGood(opts.height)) { | ||
reject(new Error('Height and width should be pixel values.')); | ||
} | ||
|
||
// first clone the GD so we can operate in a clean environment | ||
var clone = Snapshot.clone(gd, {format: 'png', height: opts.height, width: opts.width}); | ||
var clonedGd = clone.td; | ||
|
||
// put the cloned div somewhere off screen before attaching to DOM | ||
clonedGd.style.position = 'absolute'; | ||
clonedGd.style.left = '-5000px'; | ||
document.body.appendChild(clonedGd); | ||
|
||
function wait() { | ||
var delay = Snapshot.getDelay(clonedGd._fullLayout); | ||
|
||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
var svg = Snapshot.toSVG(clonedGd); | ||
|
||
var canvasContainer = window.document.createElement('div'); | ||
var canvas = window.document.createElement('canvas'); | ||
|
||
// window.document.body.appendChild(canvasContainer); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚡ the commented out code please. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
canvasContainer.appendChild(canvas); | ||
|
||
canvasContainer.id = Plotly.Lib.randstr(); | ||
canvas.id = Plotly.Lib.randstr(); | ||
|
||
Snapshot.svgToImg({ | ||
format: opts.format, | ||
width: clonedGd._fullLayout.width, | ||
height: clonedGd._fullLayout.height, | ||
canvas: canvas, | ||
svg: svg, | ||
// ask svgToImg to return a Promise | ||
// rather than EventEmitter | ||
// leave EventEmitter for backward | ||
// compatibility | ||
promise: true | ||
}).then(function(url) { | ||
if(clonedGd) clonedGd.remove(); | ||
resolve(url); | ||
}).catch(function(err) { | ||
reject(err); | ||
}); | ||
}, delay); | ||
}); | ||
} | ||
|
||
var redrawFunc = Snapshot.getRedrawFunc(clonedGd); | ||
|
||
Plotly.plot(clonedGd, clone.data, clone.layout, clone.config) | ||
// TODO: the following is Plotly.Plots.redrawText but without the waiting. | ||
// we shouldn't need to do this, but in *occasional* cases we do. Figure | ||
// out why and take it out. | ||
|
||
// not sure the above TODO makes sense anymore since | ||
// we have converted to promises | ||
.then(redrawFunc) | ||
.then(wait) | ||
.then(function(url) { resolve(url); }) | ||
.catch(function(err) { | ||
reject(err); | ||
}); | ||
}); | ||
|
||
return promise; | ||
} | ||
|
||
module.exports = toImage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
/** | ||
* Copyright 2012-2016, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
|
||
'use strict'; | ||
|
||
var toImage = require('../plot_api/to_image'); | ||
var Lib = require('../lib'); // for isIE | ||
var fileSaver = require('./filesaver'); | ||
|
||
/** | ||
* @param {object} gd figure Object | ||
* @param {object} opts option object | ||
* @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' | ||
* @param opts.width width of snapshot in px | ||
* @param opts.height height of snapshot in px | ||
* @param opts.filename name of file excluding extension | ||
*/ | ||
function downloadImage(gd, opts) { | ||
|
||
// check for undefined opts | ||
opts = opts || {}; | ||
|
||
// default to png | ||
opts.format = opts.format || 'png'; | ||
|
||
return new Promise(function(resolve,reject) { | ||
if(gd._snapshotInProgress) { | ||
reject(new Error('Snapshotting already in progress.')); | ||
} | ||
|
||
// 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.')); | ||
} | ||
|
||
gd._snapshotInProgress = true; | ||
var promise = toImage(gd, opts); | ||
|
||
var filename = opts.filename || gd.fn || 'newplot'; | ||
filename += '.' + opts.format; | ||
|
||
promise.then(function(result) { | ||
gd._snapshotInProgress = false; | ||
return fileSaver(result,filename); | ||
}).then(function(name) { | ||
resolve(name); | ||
}).catch(function(err) { | ||
gd._snapshotInProgress = false; | ||
reject(err); | ||
}); | ||
}); | ||
} | ||
|
||
module.exports = downloadImage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* Copyright 2012-2016, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
/* | ||
* substantial portions of this code from FileSaver.js | ||
* https://github.com/eligrey/FileSaver.js | ||
* License: https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md | ||
* FileSaver.js | ||
* A saveAs() FileSaver implementation. | ||
* 1.1.20160328 | ||
* | ||
* By Eli Grey, http://eligrey.com | ||
* License: MIT | ||
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var fileSaver = function(url, name) { | ||
var saveLink = document.createElement('a'); | ||
var canUseSaveLink = 'download' in saveLink; | ||
var isSafari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); | ||
var promise = new Promise(function(resolve, reject) { | ||
// IE <10 is explicitly unsupported | ||
if(typeof navigator !== 'undefined' && /MSIE [1-9]\./.test(navigator.userAgent)) { | ||
reject(new Error('IE < 10 unsupported')); | ||
} | ||
|
||
// First try a.download, then web filesystem, then object URLs | ||
if(isSafari) { | ||
// Safari doesn't allow downloading of blob urls | ||
document.location.href = 'data:attachment/file' + url.slice(url.search(/[,;]/)); | ||
resolve(name); | ||
} | ||
|
||
if(!name) { | ||
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.msSaveOrOpenBlob) { | ||
navigator.msSaveOrOpenBlob(url, name); | ||
resolve(name); | ||
} | ||
|
||
reject(new Error('download error')); | ||
}); | ||
|
||
return promise; | ||
}; | ||
|
||
module.exports = fileSaver; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we're in here, a little grammar fix:
Sorry, there was a problem downloading your snapshot!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wish they were all this easy