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