|
|
@ -1,10 +1,10 @@ |
|
|
|
/* FileSaver.js |
|
|
|
/* FileSaver.js |
|
|
|
* A saveAs() FileSaver implementation. |
|
|
|
* A saveAs() FileSaver implementation. |
|
|
|
* 2014-05-27 |
|
|
|
* 2015-03-04 |
|
|
|
* |
|
|
|
* |
|
|
|
* By Eli Grey, http://eligrey.com
|
|
|
|
* By Eli Grey, http://eligrey.com
|
|
|
|
* License: X11/MIT |
|
|
|
* License: X11/MIT |
|
|
|
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
|
|
|
|
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
/*global self */ |
|
|
|
/*global self */ |
|
|
@ -31,7 +31,7 @@ var saveAs = saveAs |
|
|
|
return view.URL || view.webkitURL || view; |
|
|
|
return view.URL || view.webkitURL || view; |
|
|
|
} |
|
|
|
} |
|
|
|
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") |
|
|
|
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") |
|
|
|
, can_use_save_link = !view.externalHost && "download" in save_link |
|
|
|
, can_use_save_link = "download" in save_link |
|
|
|
, click = function(node) { |
|
|
|
, click = function(node) { |
|
|
|
var event = doc.createEvent("MouseEvents"); |
|
|
|
var event = doc.createEvent("MouseEvents"); |
|
|
|
event.initMouseEvent( |
|
|
|
event.initMouseEvent( |
|
|
@ -49,18 +49,23 @@ var saveAs = saveAs |
|
|
|
} |
|
|
|
} |
|
|
|
, force_saveable_type = "application/octet-stream" |
|
|
|
, force_saveable_type = "application/octet-stream" |
|
|
|
, fs_min_size = 0 |
|
|
|
, fs_min_size = 0 |
|
|
|
, deletion_queue = [] |
|
|
|
// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
|
|
|
|
, process_deletion_queue = function() { |
|
|
|
// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
|
|
|
|
var i = deletion_queue.length; |
|
|
|
// for the reasoning behind the timeout and revocation flow
|
|
|
|
while (i--) { |
|
|
|
, arbitrary_revoke_timeout = 500 // in ms
|
|
|
|
var file = deletion_queue[i]; |
|
|
|
, revoke = function(file) { |
|
|
|
|
|
|
|
var revoker = function() { |
|
|
|
if (typeof file === "string") { // file is an object URL
|
|
|
|
if (typeof file === "string") { // file is an object URL
|
|
|
|
get_URL().revokeObjectURL(file); |
|
|
|
get_URL().revokeObjectURL(file); |
|
|
|
} else { // file is a File
|
|
|
|
} else { // file is a File
|
|
|
|
file.remove(); |
|
|
|
file.remove(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
if (view.chrome) { |
|
|
|
|
|
|
|
revoker(); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
setTimeout(revoker, arbitrary_revoke_timeout); |
|
|
|
} |
|
|
|
} |
|
|
|
deletion_queue.length = 0; // clear queue
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
, dispatch = function(filesaver, event_types, event) { |
|
|
|
, dispatch = function(filesaver, event_types, event) { |
|
|
|
event_types = [].concat(event_types); |
|
|
|
event_types = [].concat(event_types); |
|
|
@ -84,11 +89,6 @@ var saveAs = saveAs |
|
|
|
, blob_changed = false |
|
|
|
, blob_changed = false |
|
|
|
, object_url |
|
|
|
, object_url |
|
|
|
, target_view |
|
|
|
, target_view |
|
|
|
, get_object_url = function() { |
|
|
|
|
|
|
|
var object_url = get_URL().createObjectURL(blob); |
|
|
|
|
|
|
|
deletion_queue.push(object_url); |
|
|
|
|
|
|
|
return object_url; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
, dispatch_all = function() { |
|
|
|
, dispatch_all = function() { |
|
|
|
dispatch(filesaver, "writestart progress write writeend".split(" ")); |
|
|
|
dispatch(filesaver, "writestart progress write writeend".split(" ")); |
|
|
|
} |
|
|
|
} |
|
|
@ -96,15 +96,20 @@ var saveAs = saveAs |
|
|
|
, fs_error = function() { |
|
|
|
, fs_error = function() { |
|
|
|
// don't create more object URLs than needed
|
|
|
|
// don't create more object URLs than needed
|
|
|
|
if (blob_changed || !object_url) { |
|
|
|
if (blob_changed || !object_url) { |
|
|
|
object_url = get_object_url(blob); |
|
|
|
object_url = get_URL().createObjectURL(blob); |
|
|
|
} |
|
|
|
} |
|
|
|
if (target_view) { |
|
|
|
if (target_view) { |
|
|
|
target_view.location.href = object_url; |
|
|
|
target_view.location.href = object_url; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
window.open(object_url, "_blank"); |
|
|
|
var new_tab = view.open(object_url, "_blank"); |
|
|
|
|
|
|
|
if (new_tab == undefined && typeof safari !== "undefined") { |
|
|
|
|
|
|
|
//Apple do not allow window.open, see http://bit.ly/1kZffRI
|
|
|
|
|
|
|
|
view.location.href = object_url |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
filesaver.readyState = filesaver.DONE; |
|
|
|
filesaver.readyState = filesaver.DONE; |
|
|
|
dispatch_all(); |
|
|
|
dispatch_all(); |
|
|
|
|
|
|
|
revoke(object_url); |
|
|
|
} |
|
|
|
} |
|
|
|
, abortable = function(func) { |
|
|
|
, abortable = function(func) { |
|
|
|
return function() { |
|
|
|
return function() { |
|
|
@ -121,17 +126,24 @@ var saveAs = saveAs |
|
|
|
name = "download"; |
|
|
|
name = "download"; |
|
|
|
} |
|
|
|
} |
|
|
|
if (can_use_save_link) { |
|
|
|
if (can_use_save_link) { |
|
|
|
object_url = get_object_url(blob); |
|
|
|
object_url = get_URL().createObjectURL(blob); |
|
|
|
save_link.href = object_url; |
|
|
|
save_link.href = object_url; |
|
|
|
save_link.download = name; |
|
|
|
save_link.download = name; |
|
|
|
click(save_link); |
|
|
|
click(save_link); |
|
|
|
filesaver.readyState = filesaver.DONE; |
|
|
|
filesaver.readyState = filesaver.DONE; |
|
|
|
dispatch_all(); |
|
|
|
dispatch_all(); |
|
|
|
|
|
|
|
revoke(object_url); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// prepend BOM for UTF-8 XML and text/plain types
|
|
|
|
|
|
|
|
if (/^\s*(?:text\/(?:plain|xml)|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { |
|
|
|
|
|
|
|
blob = new Blob(["\ufeff", blob], {type: blob.type}); |
|
|
|
|
|
|
|
} |
|
|
|
// Object and web filesystem URLs have a problem saving in Google Chrome when
|
|
|
|
// Object and web filesystem URLs have a problem saving in Google Chrome when
|
|
|
|
// viewed in a tab, so I force save with application/octet-stream
|
|
|
|
// viewed in a tab, so I force save with application/octet-stream
|
|
|
|
// http://code.google.com/p/chromium/issues/detail?id=91158
|
|
|
|
// http://code.google.com/p/chromium/issues/detail?id=91158
|
|
|
|
|
|
|
|
// Update: Google errantly closed 91158, I submitted it again:
|
|
|
|
|
|
|
|
// https://code.google.com/p/chromium/issues/detail?id=389642
|
|
|
|
if (view.chrome && type && type !== force_saveable_type) { |
|
|
|
if (view.chrome && type && type !== force_saveable_type) { |
|
|
|
slice = blob.slice || blob.webkitSlice; |
|
|
|
slice = blob.slice || blob.webkitSlice; |
|
|
|
blob = slice.call(blob, 0, blob.size, force_saveable_type); |
|
|
|
blob = slice.call(blob, 0, blob.size, force_saveable_type); |
|
|
@ -158,9 +170,9 @@ var saveAs = saveAs |
|
|
|
file.createWriter(abortable(function(writer) { |
|
|
|
file.createWriter(abortable(function(writer) { |
|
|
|
writer.onwriteend = function(event) { |
|
|
|
writer.onwriteend = function(event) { |
|
|
|
target_view.location.href = file.toURL(); |
|
|
|
target_view.location.href = file.toURL(); |
|
|
|
deletion_queue.push(file); |
|
|
|
|
|
|
|
filesaver.readyState = filesaver.DONE; |
|
|
|
filesaver.readyState = filesaver.DONE; |
|
|
|
dispatch(filesaver, "writeend", event); |
|
|
|
dispatch(filesaver, "writeend", event); |
|
|
|
|
|
|
|
revoke(file); |
|
|
|
}; |
|
|
|
}; |
|
|
|
writer.onerror = function() { |
|
|
|
writer.onerror = function() { |
|
|
|
var error = writer.error; |
|
|
|
var error = writer.error; |
|
|
@ -217,11 +229,6 @@ var saveAs = saveAs |
|
|
|
FS_proto.onwriteend = |
|
|
|
FS_proto.onwriteend = |
|
|
|
null; |
|
|
|
null; |
|
|
|
|
|
|
|
|
|
|
|
view.addEventListener("unload", process_deletion_queue, false); |
|
|
|
|
|
|
|
saveAs.unload = function() { |
|
|
|
|
|
|
|
process_deletion_queue(); |
|
|
|
|
|
|
|
view.removeEventListener("unload", process_deletion_queue, false); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
return saveAs; |
|
|
|
return saveAs; |
|
|
|
}( |
|
|
|
}( |
|
|
|
typeof self !== "undefined" && self |
|
|
|
typeof self !== "undefined" && self |
|
|
@ -232,8 +239,8 @@ var saveAs = saveAs |
|
|
|
// while `this` is nsIContentFrameMessageManager
|
|
|
|
// while `this` is nsIContentFrameMessageManager
|
|
|
|
// with an attribute `content` that corresponds to the window
|
|
|
|
// with an attribute `content` that corresponds to the window
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof module !== "undefined" && module !== null) { |
|
|
|
if (typeof module !== "undefined" && module.exports) { |
|
|
|
module.exports = saveAs; |
|
|
|
module.exports.saveAs = saveAs; |
|
|
|
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) { |
|
|
|
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) { |
|
|
|
define([], function() { |
|
|
|
define([], function() { |
|
|
|
return saveAs; |
|
|
|
return saveAs; |
|
|
|