@ -1,6 +1,7 @@
/ * F i l e S a v e r . j s
* A saveAs ( ) FileSaver implementation .
* 1.1 . 20151003
* 1.3 . 2
* 2016 - 06 - 16 18 : 25 : 19
*
* By Eli Grey , http : //eligrey.com
* License : MIT
@ -15,7 +16,7 @@
var saveAs = saveAs || ( function ( view ) {
"use strict" ;
// IE <10 is explicitly unsupported
if ( typeof navigator !== "undefined" && /MSIE [1-9]\./ . test ( navigator . userAgent ) ) {
if ( typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./ . test ( navigator . userAgent ) ) {
return ;
}
var
@ -30,20 +31,16 @@ 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 )
, is _chrome _ios = /CriOS\/[\d]+/ . test ( navigator . userAgent )
, throw _outside = function ( ex ) {
( view . setImmediate || view . setTimeout ) ( function ( ) {
throw ex ;
} , 0 ) ;
}
, force _saveable _type = "application/octet-stream"
, fs _min _size = 0
// See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
// https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
// for the reasoning behind the timeout and revocation flow
, arbitrary _revoke _timeout = 500 // in ms
// 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 ) {
var revoker = function ( ) {
if ( typeof file === "string" ) { // file is an object URL
@ -52,11 +49,7 @@ var saveAs = saveAs || (function(view) {
file . remove ( ) ;
}
} ;
if ( view . chrome ) {
revoker ( ) ;
} else {
setTimeout ( revoker , arbitrary _revoke _timeout ) ;
}
setTimeout ( revoker , arbitrary _revoke _timeout ) ;
}
, dispatch = function ( filesaver , event _types , event ) {
event _types = [ ] . concat ( event _types ) ;
@ -74,8 +67,9 @@ var saveAs = saveAs || (function(view) {
}
, auto _bom = function ( blob ) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if ( /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i . test ( blob . type ) ) {
return new Blob ( [ "\ufeff" , blob ] , { type : blob . type } ) ;
return new Blob ( [ String . fromCharCode ( 0xFEFF ) , blob ] , { type : blob . type } ) ;
}
return blob ;
}
@ -87,20 +81,21 @@ var saveAs = saveAs || (function(view) {
var
filesaver = this
, type = blob . type
, blob _changed = fals e
, force = type === force _saveable _typ e
, 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 ( ( is _chrome _ios || ( 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 ( /[,;]/ ) ) ;
var url = is _chrome _ios ? reader . result : reader . result . replace ( /^data:[^;]*;/ , 'data:attachment/file;' ) ;
var popup = view . open ( url , '_blank' ) ;
if ( ! popup ) view . location . href = url ;
url = undefined ; // release reference before dispatching
filesaver . readyState = filesaver . DONE ;
dispatch _all ( ) ;
} ;
@ -109,41 +104,30 @@ 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 ) {
//Apple do not allow window.open, see http://bit.ly/1kZffRI
view . location . href = object _url
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 ;
}
}
filesaver . readyState = filesaver . DONE ;
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 ) ;
save _link . href = object _url ;
save _link . download = name ;
setTimeout ( function ( ) {
save _link . href = object _url ;
save _link . download = name ;
click ( save _link ) ;
dispatch _all ( ) ;
revoke ( object _url ) ;
@ -151,93 +135,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 ;
@ -263,8 +181,8 @@ var saveAs = saveAs || (function(view) {
if ( typeof module !== "undefined" && module . exports ) {
module . exports . saveAs = saveAs ;
} else if ( ( typeof define !== "undefined" && define !== null ) && ( define . amd != null ) ) {
define ( [ ] , function ( ) {
} else if ( ( typeof define !== "undefined" && define !== null ) && ( define . amd !== null ) ) {
define ( "FileSaver.js" , function ( ) {
return saveAs ;
} ) ;
}