FileSaver.js (8039B)
1 /* FileSaver.js 2 * A saveAs() FileSaver implementation. 3 * 1.3.8 4 * 2018-03-22 14:03:47 5 * 6 * By Eli Grey, https://eligrey.com 7 * License: MIT 8 * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 9 */ 10 11 /*global self */ 12 /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 13 14 /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/src/FileSaver.js */ 15 16 export var saveAs = saveAs || (function(view) { 17 "use strict"; 18 // IE <10 is explicitly unsupported 19 if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 20 return; 21 } 22 var 23 doc = view.document 24 // only get URL when necessary in case Blob.js hasn't overridden it yet 25 , get_URL = function() { 26 return view.URL || view.webkitURL || view; 27 } 28 , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 29 , can_use_save_link = "download" in save_link 30 , click = function(node) { 31 var event = new MouseEvent("click"); 32 node.dispatchEvent(event); 33 } 34 , is_safari = /constructor/i.test(view.HTMLElement) || view.safari 35 , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) 36 , setImmediate = view.setImmediate || view.setTimeout 37 , throw_outside = function(ex) { 38 setImmediate(function() { 39 throw ex; 40 }, 0); 41 } 42 , force_saveable_type = "application/octet-stream" 43 // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to 44 , arbitrary_revoke_timeout = 1000 * 40 // in ms 45 , revoke = function(file) { 46 var revoker = function() { 47 if (typeof file === "string") { // file is an object URL 48 get_URL().revokeObjectURL(file); 49 } else { // file is a File 50 file.remove(); 51 } 52 }; 53 setTimeout(revoker, arbitrary_revoke_timeout); 54 } 55 , dispatch = function(filesaver, event_types, event) { 56 event_types = [].concat(event_types); 57 var i = event_types.length; 58 while (i--) { 59 var listener = filesaver["on" + event_types[i]]; 60 if (typeof listener === "function") { 61 try { 62 listener.call(filesaver, event || filesaver); 63 } catch (ex) { 64 throw_outside(ex); 65 } 66 } 67 } 68 } 69 , auto_bom = function(blob) { 70 // prepend BOM for UTF-8 XML and text/* types (including HTML) 71 // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 72 if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 73 return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); 74 } 75 return blob; 76 } 77 , FileSaver = function(blob, name, no_auto_bom) { 78 if (!no_auto_bom) { 79 blob = auto_bom(blob); 80 } 81 // First try a.download, then web filesystem, then object URLs 82 var 83 filesaver = this 84 , type = blob.type 85 , force = type === force_saveable_type 86 , object_url 87 , dispatch_all = function() { 88 dispatch(filesaver, "writestart progress write writeend".split(" ")); 89 } 90 // on any filesys errors revert to saving with object URLs 91 , fs_error = function() { 92 if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { 93 // Safari doesn't allow downloading of blob urls 94 var reader = new FileReader(); 95 reader.onloadend = function() { 96 var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); 97 var popup = view.open(url, '_blank'); 98 if(!popup) view.location.href = url; 99 url=undefined; // release reference before dispatching 100 filesaver.readyState = filesaver.DONE; 101 dispatch_all(); 102 }; 103 reader.readAsDataURL(blob); 104 filesaver.readyState = filesaver.INIT; 105 return; 106 } 107 // don't create more object URLs than needed 108 if (!object_url) { 109 object_url = get_URL().createObjectURL(blob); 110 } 111 if (force) { 112 view.location.href = object_url; 113 } else { 114 var opened = view.open(object_url, "_blank"); 115 if (!opened) { 116 // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html 117 view.location.href = object_url; 118 } 119 } 120 filesaver.readyState = filesaver.DONE; 121 dispatch_all(); 122 revoke(object_url); 123 } 124 ; 125 filesaver.readyState = filesaver.INIT; 126 127 if (can_use_save_link) { 128 object_url = get_URL().createObjectURL(blob); 129 setImmediate(function() { 130 save_link.href = object_url; 131 save_link.download = name; 132 save_link.target = '_self'; 133 click(save_link); 134 dispatch_all(); 135 revoke(object_url); 136 filesaver.readyState = filesaver.DONE; 137 }, 0); 138 return; 139 } 140 141 fs_error(); 142 } 143 , FS_proto = FileSaver.prototype 144 , saveAs = function(blob, name, no_auto_bom) { 145 return new FileSaver(blob, name || blob.name || "download", no_auto_bom); 146 } 147 ; 148 149 // IE 10+ (native saveAs) 150 if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 151 return function(blob, name, no_auto_bom) { 152 name = name || blob.name || "download"; 153 154 if (!no_auto_bom) { 155 blob = auto_bom(blob); 156 } 157 return navigator.msSaveOrOpenBlob(blob, name); 158 }; 159 } 160 161 // todo: detect chrome extensions & packaged apps 162 //save_link.target = "_blank"; 163 164 FS_proto.abort = function(){}; 165 FS_proto.readyState = FS_proto.INIT = 0; 166 FS_proto.WRITING = 1; 167 FS_proto.DONE = 2; 168 169 FS_proto.error = 170 FS_proto.onwritestart = 171 FS_proto.onprogress = 172 FS_proto.onwrite = 173 FS_proto.onabort = 174 FS_proto.onerror = 175 FS_proto.onwriteend = 176 null; 177 178 return saveAs; 179 }( 180 typeof self !== "undefined" && self 181 || typeof window !== "undefined" && window 182 || this 183 ));