diff --git a/FileSaver.js b/FileSaver.js index d54e85c..fcf8994 100644 --- a/FileSaver.js +++ b/FileSaver.js @@ -30,16 +30,13 @@ var saveAs = saveAs || (function(view) { var event = new MouseEvent("click"); node.dispatchEvent(event); } - , is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent) - , webkit_req_fs = view.webkitRequestFileSystem - , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem + , is_safari = /constructor/i.test(view.HTMLElement) , throw_outside = function(ex) { (view.setImmediate || view.setTimeout)(function() { throw ex; }, 0); } , force_saveable_type = "application/octet-stream" - , fs_min_size = 0 // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to , arbitrary_revoke_timeout = 1000 * 40 // in ms , revoke = function(file) { @@ -50,22 +47,6 @@ var saveAs = saveAs || (function(view) { file.remove(); } }; - /* // Take note W3C: - var - uri = typeof file === "string" ? file : file.toURL() - , revoker = function(evt) { - // idealy DownloadFinishedEvent.data would be the URL requested - if (evt.data === uri) { - if (typeof file === "string") { // file is an object URL - get_URL().revokeObjectURL(file); - } else { // file is a File - file.remove(); - } - } - } - ; - view.addEventListener("downloadfinished", revoker); - */ setTimeout(revoker, arbitrary_revoke_timeout); } , dispatch = function(filesaver, event_types, event) { @@ -97,20 +78,19 @@ var saveAs = saveAs || (function(view) { var filesaver = this , type = blob.type - , blob_changed = false + , force = type === force_saveable_type , object_url - , target_view , dispatch_all = function() { dispatch(filesaver, "writestart progress write writeend".split(" ")); } // on any filesys errors revert to saving with object URLs , fs_error = function() { - if (target_view && is_safari && typeof FileReader !== "undefined") { + if (force && is_safari && view.FileReader) { // Safari doesn't allow downloading of blob urls var reader = new FileReader(); reader.onloadend = function() { var base64Data = reader.result; - target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/)); + view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/)); filesaver.readyState = filesaver.DONE; dispatch_all(); }; @@ -119,14 +99,14 @@ var saveAs = saveAs || (function(view) { return; } // don't create more object URLs than needed - if (blob_changed || !object_url) { + if (!object_url) { object_url = get_URL().createObjectURL(blob); } - if (target_view) { - target_view.location.href = object_url; + if (force) { + view.location.href = object_url; } else { - var new_tab = view.open(object_url, "_blank"); - if (new_tab === undefined && is_safari) { + var opened = view.open(object_url, "_blank"); + if (!opened) { // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html view.location.href = object_url; } @@ -135,20 +115,9 @@ var saveAs = saveAs || (function(view) { dispatch_all(); revoke(object_url); } - , abortable = function(func) { - return function() { - if (filesaver.readyState !== filesaver.DONE) { - return func.apply(this, arguments); - } - }; - } - , create_if_not_found = {create: true, exclusive: false} - , slice ; filesaver.readyState = filesaver.INIT; - if (!name) { - name = "download"; - } + if (can_use_save_link) { object_url = get_URL().createObjectURL(blob); setTimeout(function() { @@ -161,93 +130,27 @@ var saveAs = saveAs || (function(view) { }); return; } - // 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 - // 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) { - slice = blob.slice || blob.webkitSlice; - blob = slice.call(blob, 0, blob.size, force_saveable_type); - blob_changed = true; - } - // Since I can't be sure that the guessed media type will trigger a download - // in WebKit, I append .download to the filename. - // https://bugs.webkit.org/show_bug.cgi?id=65440 - if (webkit_req_fs && name !== "download") { - name += ".download"; - } - if (type === force_saveable_type || webkit_req_fs) { - target_view = view; - } - if (!req_fs) { - fs_error(); - return; - } - fs_min_size += blob.size; - req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { - fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { - var save = function() { - dir.getFile(name, create_if_not_found, abortable(function(file) { - file.createWriter(abortable(function(writer) { - writer.onwriteend = function(event) { - target_view.location.href = file.toURL(); - filesaver.readyState = filesaver.DONE; - dispatch(filesaver, "writeend", event); - revoke(file); - }; - writer.onerror = function() { - var error = writer.error; - if (error.code !== error.ABORT_ERR) { - fs_error(); - } - }; - "writestart progress write abort".split(" ").forEach(function(event) { - writer["on" + event] = filesaver["on" + event]; - }); - writer.write(blob); - filesaver.abort = function() { - writer.abort(); - filesaver.readyState = filesaver.DONE; - }; - filesaver.readyState = filesaver.WRITING; - }), fs_error); - }), fs_error); - }; - dir.getFile(name, {create: false}, abortable(function(file) { - // delete file if it already exists - file.remove(); - save(); - }), abortable(function(ex) { - if (ex.code === ex.NOT_FOUND_ERR) { - save(); - } else { - fs_error(); - } - })); - }), fs_error); - }), fs_error); + + fs_error(); } , FS_proto = FileSaver.prototype , saveAs = function(blob, name, no_auto_bom) { - return new FileSaver(blob, name, no_auto_bom); + return new FileSaver(blob, name || blob.name || "download", no_auto_bom); } ; // IE 10+ (native saveAs) if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { return function(blob, name, no_auto_bom) { + name = name || blob.name || "download"; + if (!no_auto_bom) { blob = auto_bom(blob); } - return navigator.msSaveOrOpenBlob(blob, name || "download"); + return navigator.msSaveOrOpenBlob(blob, name); }; } - FS_proto.abort = function() { - var filesaver = this; - filesaver.readyState = filesaver.DONE; - dispatch(filesaver, "abort"); - }; + FS_proto.abort = function(){}; FS_proto.readyState = FS_proto.INIT = 0; FS_proto.WRITING = 1; FS_proto.DONE = 2;