1
+ /*
2
+ * FileSaver.js
3
+ * A saveAs() FileSaver implementation.
4
+ *
5
+ * By Eli Grey, http://eligrey.com
6
+ *
7
+ * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
8
+ * source : http://purl.eligrey.com/github/FileSaver.js
9
+ */
10
+
11
+ // The one and only way of getting global scope in all environments
12
+ // https://stackoverflow.com/q/3277182/1008999
13
+ var _global = typeof window === 'object' && window . window === window
14
+ ? window : typeof self === 'object' && self . self === self
15
+ ? self : typeof global === 'object' && global . global === global
16
+ ? global
17
+ : this
18
+
19
+ function bom ( blob , opts ) {
20
+ if ( typeof opts === 'undefined' ) opts = { autoBom : false }
21
+ else if ( typeof opts !== 'object' ) {
22
+ console . warn ( 'Deprecated: Expected third argument to be a object' )
23
+ opts = { autoBom : ! opts }
24
+ }
25
+
26
+ // prepend BOM for UTF-8 XML and text/* types (including HTML)
27
+ // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
28
+ if ( opts . autoBom && / ^ \s * (?: t e x t \/ \S * | a p p l i c a t i o n \/ x m l | \S * \/ \S * \+ x m l ) \s * ; .* c h a r s e t \s * = \s * u t f - 8 / i. test ( blob . type ) ) {
29
+ return new Blob ( [ String . fromCharCode ( 0xFEFF ) , blob ] , { type : blob . type } )
30
+ }
31
+ return blob
32
+ }
33
+
34
+ function download ( url , name , opts ) {
35
+ var xhr = new XMLHttpRequest ( )
36
+ xhr . open ( 'GET' , url )
37
+ xhr . responseType = 'blob'
38
+ xhr . onload = function ( ) {
39
+ saveAs ( xhr . response , name , opts )
40
+ }
41
+ xhr . onerror = function ( ) {
42
+ console . error ( 'could not download file' )
43
+ }
44
+ xhr . send ( )
45
+ }
46
+
47
+ function corsEnabled ( url ) {
48
+ var xhr = new XMLHttpRequest ( )
49
+ // use sync to avoid popup blocker
50
+ xhr . open ( 'HEAD' , url , false )
51
+ try {
52
+ xhr . send ( )
53
+ } catch ( e ) { }
54
+ return xhr . status >= 200 && xhr . status <= 299
55
+ }
56
+
57
+ // `a.click()` doesn't work for all browsers (#465)
58
+ function click ( node ) {
59
+ try {
60
+ node . dispatchEvent ( new MouseEvent ( 'click' ) )
61
+ } catch ( e ) {
62
+ var evt = document . createEvent ( 'MouseEvents' )
63
+ evt . initMouseEvent ( 'click' , true , true , window , 0 , 0 , 0 , 80 ,
64
+ 20 , false , false , false , false , 0 , null )
65
+ node . dispatchEvent ( evt )
66
+ }
67
+ }
68
+
69
+ // Detect WebView inside a native macOS app by ruling out all browsers
70
+ // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
71
+ // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
72
+ var isMacOSWebView = _global . navigator && / M a c i n t o s h / . test ( navigator . userAgent ) && / A p p l e W e b K i t / . test ( navigator . userAgent ) && ! / S a f a r i / . test ( navigator . userAgent )
73
+
74
+ var saveAs = _global . saveAs || (
75
+ // probably in some web worker
76
+ ( typeof window !== 'object' || window !== _global )
77
+ ? function saveAs ( ) { /* noop */ }
78
+
79
+ // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
80
+ : ( 'download' in HTMLAnchorElement . prototype && ! isMacOSWebView )
81
+ ? function saveAs ( blob , name , opts ) {
82
+ var URL = _global . URL || _global . webkitURL
83
+ var a = document . createElement ( 'a' )
84
+ name = name || blob . name || 'download'
85
+
86
+ a . download = name
87
+ a . rel = 'noopener' // tabnabbing
88
+
89
+ // TODO: detect chrome extensions & packaged apps
90
+ // a.target = '_blank'
91
+
92
+ if ( typeof blob === 'string' ) {
93
+ // Support regular links
94
+ a . href = blob
95
+ if ( a . origin !== location . origin ) {
96
+ corsEnabled ( a . href )
97
+ ? download ( blob , name , opts )
98
+ : click ( a , a . target = '_blank' )
99
+ } else {
100
+ click ( a )
101
+ }
102
+ } else {
103
+ // Support blobs
104
+ a . href = URL . createObjectURL ( blob )
105
+ setTimeout ( function ( ) { URL . revokeObjectURL ( a . href ) } , 4E4 ) // 40s
106
+ setTimeout ( function ( ) { click ( a ) } , 0 )
107
+ }
108
+ }
109
+
110
+ // Use msSaveOrOpenBlob as a second approach
111
+ : 'msSaveOrOpenBlob' in navigator
112
+ ? function saveAs ( blob , name , opts ) {
113
+ name = name || blob . name || 'download'
114
+
115
+ if ( typeof blob === 'string' ) {
116
+ if ( corsEnabled ( blob ) ) {
117
+ download ( blob , name , opts )
118
+ } else {
119
+ var a = document . createElement ( 'a' )
120
+ a . href = blob
121
+ a . target = '_blank'
122
+ setTimeout ( function ( ) { click ( a ) } )
123
+ }
124
+ } else {
125
+ navigator . msSaveOrOpenBlob ( bom ( blob , opts ) , name )
126
+ }
127
+ }
128
+
129
+ // Fallback to using FileReader and a popup
130
+ : function saveAs ( blob , name , opts , popup ) {
131
+ // Open a popup immediately do go around popup blocker
132
+ // Mostly only available on user interaction and the fileReader is async so...
133
+ popup = popup || open ( '' , '_blank' )
134
+ if ( popup ) {
135
+ popup . document . title =
136
+ popup . document . body . innerText = 'downloading...'
137
+ }
138
+
139
+ if ( typeof blob === 'string' ) return download ( blob , name , opts )
140
+
141
+ var force = blob . type === 'application/octet-stream'
142
+ var isSafari = / c o n s t r u c t o r / i. test ( _global . HTMLElement ) || _global . safari
143
+ var isChromeIOS = / C r i O S \/ [ \d ] + / . test ( navigator . userAgent )
144
+
145
+ if ( ( isChromeIOS || ( force && isSafari ) || isMacOSWebView ) && typeof FileReader !== 'undefined' ) {
146
+ // Safari doesn't allow downloading of blob URLs
147
+ var reader = new FileReader ( )
148
+ reader . onloadend = function ( ) {
149
+ var url = reader . result
150
+ url = isChromeIOS ? url : url . replace ( / ^ d a t a : [ ^ ; ] * ; / , 'data:attachment/file;' )
151
+ if ( popup ) popup . location . href = url
152
+ else location = url
153
+ popup = null // reverse-tabnabbing #460
154
+ }
155
+ reader . readAsDataURL ( blob )
156
+ } else {
157
+ var URL = _global . URL || _global . webkitURL
158
+ var url = URL . createObjectURL ( blob )
159
+ if ( popup ) popup . location = url
160
+ else location . href = url
161
+ popup = null // reverse-tabnabbing #460
162
+ setTimeout ( function ( ) { URL . revokeObjectURL ( url ) } , 4E4 ) // 40s
163
+ }
164
+ }
165
+ )
166
+
167
+ _global . saveAs = saveAs . saveAs = saveAs
168
+
169
+ if ( typeof module !== 'undefined' ) {
170
+ module . exports = saveAs ;
171
+ }
0 commit comments